diff --git a/d2ir/compile.go b/d2ir/compile.go index 2d2c93721..592e6efbb 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -124,6 +124,15 @@ func (c *compiler) compileMap(dst *Map, ast *d2ast.Map) { continue } OverlayMap(dst, impn.Map()) + + if impnf, ok := impn.(*Field); ok { + if impnf.Primary_ != nil { + dstf := ParentField(dst) + if dstf != nil { + dstf.Primary_ = impnf.Primary_ + } + } + } case n.Substitution != nil: panic("TODO") } @@ -192,13 +201,47 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext) switch n := n.(type) { case *Field: if n.Primary_ != nil { - f.Primary_ = n.Primary_.Copy(f).(*Scalar) + refctx2 := refctx.Copy() + key2 := *refctx.Key + refctx.Key = &key2 + refctx2.Key.Value = d2ast.MakeValueBox(n.Primary_.Value) + // If the link is a board, we need to transform it into an absolute path. + if f.Name == "link" { + c.compileLink(refctx2) + } + f.Primary_ = &Scalar{ + parent: f, + Value: refctx2.Key.Value.ScalarBox().Unbox(), + } } if n.Composite != nil { f.Composite = n.Composite.Copy(f).(Composite) } case *Map: - f.Composite = n.Copy(f).(Composite) + f.Composite = &Map{ + parent: f, + } + switch NodeBoardKind(f) { + case BoardScenario: + c.overlay(ParentBoard(f).Map(), f) + case BoardStep: + stepsMap := ParentMap(f) + for i := range stepsMap.Fields { + if stepsMap.Fields[i] == f { + if i == 0 { + c.overlay(ParentBoard(f).Map(), f) + } else { + c.overlay(stepsMap.Fields[i-1].Map(), f) + } + break + } + } + } + OverlayMap(f.Map(), n) + switch NodeBoardKind(f) { + case BoardScenario, BoardStep: + c.compileClasses(f.Map()) + } } } else if refctx.Key.Value.ScalarBox().Unbox() != nil { // If the link is a board, we need to transform it into an absolute path. @@ -408,7 +451,7 @@ func (c *compiler) compileArray(dst *Array, a *d2ast.Array) { } case *Map: if v.Spread { - c.errorf(v, "cannot spread import map into array") + c.errorf(v, "can only spread import array into array") continue } irv = n diff --git a/d2ir/import_test.go b/d2ir/import_test.go index dceaa64a5..abfd04a69 100644 --- a/d2ir/import_test.go +++ b/d2ir/import_test.go @@ -96,6 +96,20 @@ label: meow`, assertQuery(t, m, 0, 0, nil, "jon") }, }, + { + name: "nested/spread_primary", + run: func(t testing.TB) { + m, err := compileFS(t, "index.d2", map[string]string{ + "index.d2": "q: { ...@x.y }", + "x.d2": "y: meow { jon; jan }", + }) + assert.Success(t, err) + assertQuery(t, m, 3, 0, nil, "") + assertQuery(t, m, 2, 0, "meow", "q") + assertQuery(t, m, 0, 0, nil, "q.jan") + assertQuery(t, m, 0, 0, nil, "q.jon") + }, + }, } runa(t, tca) @@ -156,6 +170,16 @@ x.d2:1:7: connection missing source`) assert.ErrorString(t, err, `q.d2:1:1: detected cyclic import chain: x -> y -> q -> x`) }, }, + { + name: "spread_non_map", + run: func(t testing.TB) { + _, err := compileFS(t, "index.d2", map[string]string{ + "index.d2": "...@x.y", + "x.d2": "y: meow", + }) + assert.ErrorString(t, err, `index.d2:1:1: cannot spread import non map into map`) + }, + }, } runa(t, tca) }) diff --git a/testdata/d2ir/TestCompile/imports/nested/spread_primary.exp.json b/testdata/d2ir/TestCompile/imports/nested/spread_primary.exp.json new file mode 100644 index 000000000..1dbc2241c --- /dev/null +++ b/testdata/d2ir/TestCompile/imports/nested/spread_primary.exp.json @@ -0,0 +1,226 @@ +{ + "fields": [ + { + "name": "q", + "primary": { + "value": { + "range": "x.d2,0:3:3-0:7:7", + "value": [ + { + "string": "meow", + "raw_string": "meow" + } + ] + } + }, + "composite": { + "fields": [ + { + "name": "jon", + "references": [ + { + "string": { + "range": "x.d2,0:10:10-0:13:13", + "value": [ + { + "string": "jon", + "raw_string": "jon" + } + ] + }, + "key_path": { + "range": "x.d2,0:10:10-0:13:13", + "path": [ + { + "unquoted_string": { + "range": "x.d2,0:10:10-0:13:13", + "value": [ + { + "string": "jon", + "raw_string": "jon" + } + ] + } + } + ] + }, + "context": { + "edge": null, + "key": { + "range": "x.d2,0:10:10-0:13:13", + "key": { + "range": "x.d2,0:10:10-0:13:13", + "path": [ + { + "unquoted_string": { + "range": "x.d2,0:10:10-0:13:13", + "value": [ + { + "string": "jon", + "raw_string": "jon" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + } + } + ] + }, + { + "name": "jan", + "references": [ + { + "string": { + "range": "x.d2,0:15:15-0:18:18", + "value": [ + { + "string": "jan", + "raw_string": "jan" + } + ] + }, + "key_path": { + "range": "x.d2,0:15:15-0:18:18", + "path": [ + { + "unquoted_string": { + "range": "x.d2,0:15:15-0:18:18", + "value": [ + { + "string": "jan", + "raw_string": "jan" + } + ] + } + } + ] + }, + "context": { + "edge": null, + "key": { + "range": "x.d2,0:15:15-0:19:19", + "key": { + "range": "x.d2,0:15:15-0:18:18", + "path": [ + { + "unquoted_string": { + "range": "x.d2,0:15:15-0:18:18", + "value": [ + { + "string": "jan", + "raw_string": "jan" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + } + } + ] + } + ], + "edges": null + }, + "references": [ + { + "string": { + "range": "index.d2,0:0:0-0:1:1", + "value": [ + { + "string": "q", + "raw_string": "q" + } + ] + }, + "key_path": { + "range": "index.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "index.d2,0:0:0-0:1:1", + "value": [ + { + "string": "q", + "raw_string": "q" + } + ] + } + } + ] + }, + "context": { + "edge": null, + "key": { + "range": "index.d2,0:0:0-0:14:14", + "key": { + "range": "index.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "index.d2,0:0:0-0:1:1", + "value": [ + { + "string": "q", + "raw_string": "q" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "index.d2,0:3:3-0:14:14", + "nodes": [ + { + "import": { + "range": "index.d2,0:5:5-0:13:13", + "spread": true, + "pre": "", + "path": [ + { + "unquoted_string": { + "range": "index.d2,0:9:9-0:10:10", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + }, + { + "unquoted_string": { + "range": "index.d2,0:11:11-0:12:12", + "value": [ + { + "string": "y", + "raw_string": "y" + } + ] + } + } + ] + } + } + ] + } + } + } + } + } + ] + } + ], + "edges": null +}