From 2425838e044e549d8118b9a7f7badb12c9b20419 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 17 Apr 2024 11:24:50 -0700 Subject: [PATCH] fix nested spread substitutions compiler error --- ci/release/changelogs/next.md | 1 + d2ir/compile.go | 17 +- e2etests/testdata/txtar.txt | 18 ++ .../dagre/board.exp.json | 219 ++++++++++++++++++ .../dagre/sketch.exp.svg | 104 +++++++++ .../elk/board.exp.json | 210 +++++++++++++++++ .../elk/sketch.exp.svg | 104 +++++++++ 7 files changed, 669 insertions(+), 4 deletions(-) create mode 100644 e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/board.exp.json create mode 100644 e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/board.exp.json create mode 100644 e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/sketch.exp.svg diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 432f3a3c3..498d06ed6 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -5,3 +5,4 @@ #### Bugfixes ⛑️ - Fix executable plugins that implement standalone router [#1910](https://github.com/terrastruct/d2/pull/1910) +- Fix compiler error with multiple nested spread substitutions [#1913](https://github.com/terrastruct/d2/pull/1913) diff --git a/d2ir/compile.go b/d2ir/compile.go index bbbe60489..995f48432 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -127,14 +127,21 @@ func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) { varsStack = append([]*Map{f.Map()}, varsStack...) } } - for _, f := range m.Fields { + for i := 0; i < len(m.Fields); i++ { + f := m.Fields[i] if f.Primary() != nil { - c.resolveSubstitutions(varsStack, f) + removed := c.resolveSubstitutions(varsStack, f) + if removed { + i-- + } } if arr, ok := f.Composite.(*Array); ok { for _, val := range arr.Values { if scalar, ok := val.(*Scalar); ok { - c.resolveSubstitutions(varsStack, scalar) + removed := c.resolveSubstitutions(varsStack, scalar) + if removed { + i-- + } } } } else if f.Map() != nil { @@ -213,7 +220,7 @@ func (c *compiler) validateConfigs(configs *Field) { } } -func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) { +func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) (removedField bool) { var subbed bool var resolvedField *Field @@ -264,6 +271,7 @@ func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) { for i, f2 := range m.Fields { if n == f2 { m.Fields = append(m.Fields[:i], m.Fields[i+1:]...) + removedField = true break } } @@ -334,6 +342,7 @@ func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) { s.Coalesce() } } + return removedField } func (c *compiler) resolveSubstitution(vars *Map, substitution *d2ast.Substitution) *Field { diff --git a/e2etests/testdata/txtar.txt b/e2etests/testdata/txtar.txt index e6d17bf6d..e43669f2a 100644 --- a/e2etests/testdata/txtar.txt +++ b/e2etests/testdata/txtar.txt @@ -188,3 +188,21 @@ b: hello there cat { width: 64 height: 66 } + +-- nested-spread-substitutions-regression -- +vars: { + dog1: Frido { + shape: circle + } + my-house: { + label: "Home" + } +} + +ok: { + ...${my-house} + dog1: { + ...${dog1} + } + dog1 -> dog3 +} diff --git a/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/board.exp.json b/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/board.exp.json new file mode 100644 index 000000000..648e0b3f2 --- /dev/null +++ b/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/board.exp.json @@ -0,0 +1,219 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "ok", + "type": "rectangle", + "pos": { + "x": 10, + "y": 20 + }, + "width": 157, + "height": 323, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Home", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 68, + "labelHeight": 36, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "ok.dog1", + "type": "oval", + "pos": { + "x": 40, + "y": 50 + }, + "width": 97, + "height": 97, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "dog1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 35, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "ok.dog3", + "type": "rectangle", + "pos": { + "x": 49, + "y": 247 + }, + "width": 80, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "dog3", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 35, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "ok.(dog1 -> dog3)[0]", + "src": "ok.dog1", + "srcArrow": "none", + "dst": "ok.dog3", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 89, + "y": 147 + }, + { + "x": 88.5999984741211, + "y": 187 + }, + { + "x": 88.5, + "y": 207 + }, + { + "x": 88.5, + "y": 247 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/sketch.exp.svg b/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/sketch.exp.svg new file mode 100644 index 000000000..48d5fa4dd --- /dev/null +++ b/e2etests/testdata/txtar/nested-spread-substitutions-regression/dagre/sketch.exp.svg @@ -0,0 +1,104 @@ +Homedog1dog3 + + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/board.exp.json b/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/board.exp.json new file mode 100644 index 000000000..6505afcec --- /dev/null +++ b/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/board.exp.json @@ -0,0 +1,210 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "ok", + "type": "rectangle", + "pos": { + "x": 12, + "y": 12 + }, + "width": 197, + "height": 333, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Home", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 68, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "ok.dog1", + "type": "oval", + "pos": { + "x": 62, + "y": 62 + }, + "width": 97, + "height": 97, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "dog1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 35, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "ok.dog3", + "type": "rectangle", + "pos": { + "x": 70, + "y": 229 + }, + "width": 80, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "dog3", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 35, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "ok.(dog1 -> dog3)[0]", + "src": "ok.dog1", + "srcArrow": "none", + "dst": "ok.dog3", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 111, + "y": 159 + }, + { + "x": 110, + "y": 229 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/sketch.exp.svg b/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/sketch.exp.svg new file mode 100644 index 000000000..fc53a8475 --- /dev/null +++ b/e2etests/testdata/txtar/nested-spread-substitutions-regression/elk/sketch.exp.svg @@ -0,0 +1,104 @@ +Homedog1dog3 + + + + + \ No newline at end of file