d2ir: IR Root wip
This commit is contained in:
parent
180334a8e1
commit
748557d8f2
4 changed files with 102 additions and 71 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
76
d2ir/d2ir.go
76
d2ir/d2ir.go
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue