d2ir: IR Root wip

This commit is contained in:
Anmol Sethi 2023-01-18 04:32:12 -08:00
parent 180334a8e1
commit 748557d8f2
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
4 changed files with 102 additions and 71 deletions

View file

@ -242,8 +242,7 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height c
}
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/shape_unquoted_hex.d2:3:10: missing value after colon
`,
expErr: `d2/testdata/d2compiler/TestCompile/shape_unquoted_hex.d2:3:10: missing value after colon`,
},
{
name: "edge_unquoted_hex",
@ -254,8 +253,7 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height c
}
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_unquoted_hex.d2:3:10: missing value after colon
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_unquoted_hex.d2:3:10: missing value after colon`,
},
{
name: "blank_underscore",
@ -265,8 +263,7 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height c
_
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/blank_underscore.d2:3:3: invalid use of parent "_"
`,
expErr: `d2/testdata/d2compiler/TestCompile/blank_underscore.d2:3:3: invalid use of parent "_"`,
},
{
name: "image_non_style",
@ -277,8 +274,7 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height c
name: y
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/image_non_style.d2:4:3: image shapes cannot have children.
`,
expErr: `d2/testdata/d2compiler/TestCompile/image_non_style.d2:4:3: image shapes cannot have children.`,
},
{
name: "stroke-width",
@ -303,8 +299,7 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height c
style.stroke-width: -1
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/illegal-stroke-width.d2:2:23: expected "stroke-width" to be a number between 0 and 15
`,
expErr: `d2/testdata/d2compiler/TestCompile/illegal-stroke-width.d2:2:23: expected "stroke-width" to be a number between 0 and 15`,
},
{
name: "underscore_parent_create",
@ -457,8 +452,7 @@ x: {
text: `
_.x
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_root.d2:2:1: parent "_" cannot be used in the root scope
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_root.d2:2:1: parent "_" cannot be used in the root scope`,
},
{
name: "underscore_parent_middle_path",
@ -468,8 +462,7 @@ x: {
y._.z
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_middle_path.d2:3:3: parent "_" can only be used in the beginning of paths, e.g. "_.x"
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_middle_path.d2:3:3: parent "_" can only be used in the beginning of paths, e.g. "_.x"`,
},
{
name: "underscore_parent_sandwich_path",
@ -479,8 +472,7 @@ x: {
_.z._
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_sandwich_path.d2:3:3: parent "_" can only be used in the beginning of paths, e.g. "_.x"
`,
expErr: `d2/testdata/d2compiler/TestCompile/underscore_parent_sandwich_path.d2:3:3: parent "_" can only be used in the beginning of paths, e.g. "_.x"`,
},
{
name: "underscore_edge",
@ -997,8 +989,7 @@ x -> y: {
text: `x: {shape: triangle}
`,
expErr: `d2/testdata/d2compiler/TestCompile/object_arrowhead_shape.d2:1:5: invalid shape, can only set "triangle" for arrowheads
`,
expErr: `d2/testdata/d2compiler/TestCompile/object_arrowhead_shape.d2:1:5: invalid shape, can only set "triangle" for arrowheads`,
},
{
name: "edge_flat_label_arrowhead",
@ -1084,8 +1075,7 @@ x -> y: {
space -> stars
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/nested_edge.d2:1:1: edges cannot be nested within another edge
`,
expErr: `d2/testdata/d2compiler/TestCompile/nested_edge.d2:1:1: edges cannot be nested within another edge`,
},
{
name: "shape_edge_style",
@ -1095,8 +1085,7 @@ x: {
style.animated: true
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/shape_edge_style.d2:3:2: key "animated" can only be applied to edges
`,
expErr: `d2/testdata/d2compiler/TestCompile/shape_edge_style.d2:3:2: key "animated" can only be applied to edges`,
},
{
name: "edge_chain_map",
@ -1352,8 +1341,7 @@ x -> y: {
z
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_map_non_reserved.d2:2:1: edge map keys must be reserved keywords
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_map_non_reserved.d2:2:1: edge map keys must be reserved keywords`,
},
{
name: "url_link",
@ -1398,8 +1386,7 @@ x -> y: {
text: `x.near: txop-center
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_constant.d2:1:1: near key "txop-center" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_constant.d2:1:1: near key "txop-center" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right`,
},
{
name: "near_bad_container",
@ -1409,8 +1396,7 @@ x -> y: {
y
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_container.d2:1:1: constant near keys cannot be set on shapes with children
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_container.d2:1:1: constant near keys cannot be set on shapes with children`,
},
{
name: "near_bad_connected",
@ -1420,16 +1406,14 @@ x -> y: {
}
x -> y
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_connected.d2:1:1: constant near keys cannot be set on connected shapes
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_bad_connected.d2:1:1: constant near keys cannot be set on connected shapes`,
},
{
name: "nested_near_constant",
text: `x.y.near: top-center
`,
expErr: `d2/testdata/d2compiler/TestCompile/nested_near_constant.d2:1:1: constant near keys can only be set on root level shapes
`,
expErr: `d2/testdata/d2compiler/TestCompile/nested_near_constant.d2:1:1: constant near keys can only be set on root level shapes`,
},
{
name: "reserved_icon_near_style",
@ -1477,15 +1461,13 @@ y
expErr: `d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:3:9: bad icon url "::????:::%%orange": parse "::????:::%%orange": missing protocol scheme
d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:4:18: expected "opacity" to be a number between 0.0 and 1.0
d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:5:18: expected "opacity" to be a number between 0.0 and 1.0
d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key "y" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right
`,
d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key "y" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right`,
},
{
name: "errors/missing_shape_icon",
text: `x.shape: image`,
expErr: `d2/testdata/d2compiler/TestCompile/errors/missing_shape_icon.d2:1:1: image shape must include an "icon" field
`,
text: `x.shape: image`,
expErr: `d2/testdata/d2compiler/TestCompile/errors/missing_shape_icon.d2:1:1: image shape must include an "icon" field`,
},
{
name: "edge_in_column",
@ -1501,8 +1483,7 @@ d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key "
text: `x: {style.opacity: 0.4}
y -> x.style
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_to_style.d2:2:1: cannot connect to reserved keyword
`,
expErr: `d2/testdata/d2compiler/TestCompile/edge_to_style.d2:2:1: cannot connect to reserved keyword`,
},
{
name: "escaped_id",
@ -1683,8 +1664,7 @@ x.y -> a.b: {
text: `SVP1.style.shape: oval
SVP1.style.3d: true`,
expErr: `d2/testdata/d2compiler/TestCompile/3d_oval.d2:2:1: key "3d" can only be applied to squares and rectangles
`,
expErr: `d2/testdata/d2compiler/TestCompile/3d_oval.d2:2:1: key "3d" can only be applied to squares and rectangles`,
}, {
name: "edge_column_index",
text: `src: {
@ -1741,8 +1721,7 @@ dst.id <-> src.dst_id
}
b -> x.a
`,
expErr: `d2/testdata/d2compiler/TestCompile/leaky_sequence.d2:5:1: connections within sequence diagrams can connect only to other objects within the same sequence diagram
`,
expErr: `d2/testdata/d2compiler/TestCompile/leaky_sequence.d2:5:1: connections within sequence diagrams can connect only to other objects within the same sequence diagram`,
},
{
name: "sequence_scoping",
@ -1819,8 +1798,7 @@ choo: {
text: `x: {
direction: diagonal
}`,
expErr: `d2/testdata/d2compiler/TestCompile/invalid_direction.d2:2:14: direction must be one of up, down, right, left, got "diagonal"
`,
expErr: `d2/testdata/d2compiler/TestCompile/invalid_direction.d2:2:14: direction must be one of up, down, right, left, got "diagonal"`,
},
{
name: "self-referencing",
@ -1869,8 +1847,7 @@ choo: {
test_id: varchar(64) {constraint: [primary_key, foreign_key]}
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/sql-panic.d2:3:27: constraint value must be a string
`,
expErr: `d2/testdata/d2compiler/TestCompile/sql-panic.d2:3:27: constraint value must be a string`,
},
{
name: "wrong_column_index",

View file

@ -276,6 +276,25 @@ func testCompileEdges(t *testing.T) {
func testCompileLayers(t *testing.T) {
t.Parallel()
t.Run("errs", func(t *testing.T) {
tca := []testCase{
{
name: "bad_edge/1",
run: func(t testing.TB) {
_, err := compile(t, `layers.x -> layers.y`)
assert.ErrorString(t, err, `TestCompile/layer/errs/bad_edge/1.d2:1:1: cannot create edges between layers, scenarios or steps`)
},
},
{
name: "bad_edge/2",
run: func(t testing.TB) {
_, err := compile(t, `layers -> scenarios`)
assert.ErrorString(t, err, `TestCompile/layer/errs/bad_edge/2.d2:1:1: cannot create edges between layers, scenarios or steps`)
},
},
}
runa(t, tca)
})
tca := []testCase{
{
name: "root",

View file

@ -121,25 +121,28 @@ func (m *Map) Copy(newp Node) Node {
// Root reports whether the Map is the root of the D2 tree.
func (m *Map) Root() bool {
return ParentMap(m) == nil
return m.parent == nil
}
// Layer reports whether the Map represents the root of a layer.
func (m *Map) Layer() bool {
f := ParentField(m)
if f == nil {
return true
}
f = ParentField(f)
if f == nil {
return false
}
switch f.Name {
case "layers", "scenarios", "steps":
return true
default:
return false
// Layer reports whether n represents the root of a layer.
func IsLayer(n Node) bool {
switch n := n.(type) {
case *Field:
n = ParentField(n)
if n != nil {
switch n.Name {
case "layers", "scenarios", "steps":
return true
}
}
case *Map:
f := ParentField(n)
if f == nil {
return true
}
return IsLayer(f)
}
return false
}
type Field struct {
@ -318,8 +321,8 @@ func (a *Array) Copy(newp Node) Node {
}
type FieldReference struct {
String d2ast.String `json:"string"`
KeyPath *d2ast.KeyPath `json:"key_path"`
String d2ast.String `json:"string"`
KeyPath *d2ast.KeyPath `json:"key_path"`
Context *RefContext `json:"context"`
}
@ -484,7 +487,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field,
switch head {
case "layers", "scenarios", "steps":
if !m.Layer() {
if !IsLayer(m) {
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a layer root", head)
}
}
@ -614,6 +617,27 @@ func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) (*Edge, error) {
return f_m.CreateEdge(eid, refctx)
}
ij := hasLayerKeyword(eid.SrcPath)
if ij != -1 {
return nil, d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "cannot create edges between layers, scenarios or steps")
}
src := m.GetField(eid.SrcPath...)
if IsLayer(src) {
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot create edges between layers, scenarios or steps")
}
ij = hasLayerKeyword(eid.DstPath)
if ij != -1 {
return nil, d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "cannot create edges between layers, scenarios or steps")
}
dst := m.GetField(eid.DstPath...)
if IsLayer(dst) {
return nil, d2parser.Errorf(refctx.Edge.Dst, "cannot create edges between layers, scenarios or steps")
}
if ParentLayer(src) != ParentLayer(dst) {
return nil, d2parser.Errorf(refctx.Edge, "cannot create edges between layers, scenarios or steps")
}
eid.Index = nil
ea := m.GetEdges(eid)
index := len(ea)
@ -774,13 +798,13 @@ func ParentField(n Node) *Field {
return nil
}
func ParentLayer(n Node) *Map {
func ParentLayer(n Node) Node {
for {
m := ParentMap(n)
if m == nil {
return nil
}
if m.Layer() {
if IsLayer(m) {
return m
}
n = m
@ -797,3 +821,13 @@ func countUnderscores(p []string) int {
}
return count
}
func hasLayerKeyword(ida []string) int {
for i := range ida {
switch ida[i] {
case "layers", "scenarios", "steps":
return i
}
}
return -1
}

View file

@ -147,11 +147,12 @@ func (pe ParseError) Error() string {
var sb strings.Builder
if pe.IOError != nil {
sb.WriteString(pe.IOError.Error())
sb.WriteByte('\n')
}
for _, err := range pe.Errors {
for i, err := range pe.Errors {
if pe.IOError != nil || i > 0 {
sb.WriteByte('\n')
}
sb.WriteString(err.Error())
sb.WriteByte('\n')
}
return sb.String()
}