d2ir: wip
This commit is contained in:
parent
fd241e4425
commit
989fdb0fe5
13 changed files with 308 additions and 39 deletions
|
|
@ -397,3 +397,16 @@ func (p *printer) edgeIndex(ei *d2ast.EdgeIndex) {
|
|||
}
|
||||
p.sb.WriteByte(']')
|
||||
}
|
||||
|
||||
func KeyPath(kp *d2ast.KeyPath) []string {
|
||||
var ids []string
|
||||
for _, s := range kp.Path {
|
||||
// We format each string of the key to ensure the resulting strings can be parsed
|
||||
// correctly.
|
||||
n := &d2ast.KeyPath{
|
||||
Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(s.Unbox().ScalarString(), true)).StringBox()},
|
||||
}
|
||||
ids = append(ids, Format(n))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1238,16 +1238,7 @@ func (g *Graph) Texts() []*d2target.MText {
|
|||
}
|
||||
|
||||
func Key(k *d2ast.KeyPath) []string {
|
||||
var ids []string
|
||||
for _, s := range k.Path {
|
||||
// We format each string of the key to ensure the resulting strings can be parsed
|
||||
// correctly.
|
||||
n := &d2ast.KeyPath{
|
||||
Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(s.Unbox().ScalarString(), true)).StringBox()},
|
||||
}
|
||||
ids = append(ids, d2format.Format(n))
|
||||
}
|
||||
return ids
|
||||
return d2format.KeyPath(k)
|
||||
}
|
||||
|
||||
var ReservedKeywords = map[string]struct{}{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
)
|
||||
|
||||
|
|
@ -23,28 +23,29 @@ func (c *compiler) errorf(n d2ast.Node, f string, v ...interface{}) {
|
|||
|
||||
func Apply(dst *Map, ast *d2ast.Map) error {
|
||||
var c compiler
|
||||
c.apply(dst, ast)
|
||||
c.compileMap(dst, ast)
|
||||
if !c.err.Empty() {
|
||||
return c.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *compiler) apply(dst *Map, ast *d2ast.Map) {
|
||||
func (c *compiler) compileMap(dst *Map, ast *d2ast.Map) {
|
||||
for _, n := range ast.Nodes {
|
||||
if n.MapKey == nil {
|
||||
continue
|
||||
switch {
|
||||
case n.MapKey != nil:
|
||||
c.compileField(dst, n.MapKey)
|
||||
case n.Substitution != nil:
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
c.applyKey(dst, n.MapKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) applyKey(dst *Map, k *d2ast.Key) {
|
||||
func (c *compiler) compileField(dst *Map, k *d2ast.Key) {
|
||||
if k.Key != nil && len(k.Key.Path) > 0 {
|
||||
f, ok := dst.Ensure(d2graph.Key(k.Key))
|
||||
f, ok := dst.Ensure(d2format.KeyPath(k.Key))
|
||||
if !ok {
|
||||
c.errorf(k.Key, "cannot index into array")
|
||||
c.errorf(k, "cannot index into array")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +56,28 @@ func (c *compiler) applyKey(dst *Map, k *d2ast.Key) {
|
|||
Value: k.Primary.Unbox(),
|
||||
}
|
||||
}
|
||||
if k.Value.Array != nil {
|
||||
a := &Array{
|
||||
parent: f,
|
||||
}
|
||||
c.compileArray(a, k.Value.Array)
|
||||
f.Composite = a
|
||||
} else if k.Value.Map != nil {
|
||||
m := &Map{
|
||||
parent: f,
|
||||
}
|
||||
c.compileMap(m, k.Value.Map)
|
||||
f.Composite = m
|
||||
} else if k.Value.ScalarBox().Unbox() != nil {
|
||||
f.Primary = &Scalar{
|
||||
parent: f,
|
||||
Value: k.Value.ScalarBox().Unbox(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) compileArray(dst *Array, a *d2ast.Array) {
|
||||
panic(fmt.Sprintf("TODO"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/diff"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2ir"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
)
|
||||
|
|
@ -31,7 +32,7 @@ func testApplySimple(t *testing.T) {
|
|||
|
||||
tca := []testCase{
|
||||
{
|
||||
name: "one",
|
||||
name: "field",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x`)
|
||||
assert.Success(t, err)
|
||||
|
|
@ -40,6 +41,62 @@ func testApplySimple(t *testing.T) {
|
|||
assertField(t, m, 0, 0, nil, "x")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "field/label",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x: yes`)
|
||||
assert.Success(t, err)
|
||||
assertField(t, m, 1, 0, nil)
|
||||
|
||||
assertField(t, m, 0, 0, "yes", "x")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "field/label/nested",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x.y: yes`)
|
||||
assert.Success(t, err)
|
||||
assertField(t, m, 2, 0, nil)
|
||||
|
||||
assertField(t, m, 1, 0, nil, "x")
|
||||
assertField(t, m, 0, 0, "yes", "x", "y")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x: yes { pqrs }`)
|
||||
assert.Success(t, err)
|
||||
assertField(t, m, 2, 0, nil)
|
||||
|
||||
assertField(t, m, 1, 0, "yes", "x")
|
||||
assertField(t, m, 0, 0, nil, "x", "pqrs")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "primary/nested",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x.y: yes { pqrs }`)
|
||||
assert.Success(t, err)
|
||||
assertField(t, m, 3, 0, nil)
|
||||
|
||||
assertField(t, m, 2, 0, nil, "x")
|
||||
assertField(t, m, 1, 0, "yes", "x", "y")
|
||||
assertField(t, m, 0, 0, nil, "x", "y", "pqrs")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
err := parse(t, m, `x -> y`)
|
||||
assert.Success(t, err)
|
||||
assertField(t, m, 2, 1, nil)
|
||||
assertEdge(t, m, 0, nil, `(x -> y)[0]`)
|
||||
|
||||
assertField(t, m, 0, 0, nil, "x")
|
||||
assertField(t, m, 0, 0, nil, "y")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested",
|
||||
run: func(t testing.TB, m *d2ir.Map) {
|
||||
|
|
@ -53,11 +110,7 @@ func testApplySimple(t *testing.T) {
|
|||
assertField(t, m, 1, 0, nil, "z")
|
||||
assertField(t, m, 0, 0, nil, "z", "p")
|
||||
|
||||
assertEdge(t, m, 0, nil, &d2ir.EdgeID{
|
||||
[]string{"x", "y"}, false,
|
||||
[]string{"z", "p"}, true,
|
||||
-1,
|
||||
})
|
||||
assertEdge(t, m, 0, nil, "(x.y -> z.p)[0]")
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -70,11 +123,7 @@ func testApplySimple(t *testing.T) {
|
|||
assertField(t, m, 0, 0, nil, "x")
|
||||
assertField(t, m, 0, 0, nil, "z")
|
||||
|
||||
assertEdge(t, m, 0, nil, &d2ir.EdgeID{
|
||||
[]string{"x"}, false,
|
||||
[]string{"z"}, true,
|
||||
-1,
|
||||
})
|
||||
assertEdge(t, m, 0, nil, "(x -> z)[0]")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -150,14 +199,30 @@ func assertField(t testing.TB, n d2ir.Node, nfields, nedges int, primary interfa
|
|||
assert.Equal(t, nfields, m.FieldCount())
|
||||
assert.Equal(t, nedges, m.EdgeCount())
|
||||
if !makeScalar(p).Equal(makeScalar(primary)) {
|
||||
t.Fatalf("expected primary %#v but %#v", primary, p)
|
||||
t.Fatalf("expected primary %#v but got %s", primary, p)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eid *d2ir.EdgeID) *d2ir.Edge {
|
||||
func parseEdgeID(t testing.TB, eids string) *d2ir.EdgeID {
|
||||
t.Helper()
|
||||
k, err := d2parser.ParseMapKey(eids)
|
||||
assert.Success(t, err)
|
||||
|
||||
return &d2ir.EdgeID{
|
||||
SrcPath: d2format.KeyPath(k.Edges[0].Src),
|
||||
SrcArrow: k.Edges[0].SrcArrow == "<",
|
||||
DstPath: d2format.KeyPath(k.Edges[0].Dst),
|
||||
DstArrow: k.Edges[0].DstArrow == ">",
|
||||
Index: *k.EdgeIndex.Int,
|
||||
}
|
||||
}
|
||||
|
||||
func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eids string) *d2ir.Edge {
|
||||
t.Helper()
|
||||
|
||||
eid := parseEdgeID(t, eids)
|
||||
|
||||
var m *d2ir.Map
|
||||
switch n := n.(type) {
|
||||
|
|
@ -181,7 +246,7 @@ func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eid
|
|||
|
||||
assert.Equal(t, nfields, e.Map.FieldCount())
|
||||
if !makeScalar(e.Primary).Equal(makeScalar(primary)) {
|
||||
t.Fatalf("expected primary %#v but %#v", primary, e.Primary)
|
||||
t.Fatalf("expected primary %#v but %s", primary, e.Primary)
|
||||
}
|
||||
|
||||
return e
|
||||
|
|
|
|||
21
d2ir/d2ir.go
21
d2ir/d2ir.go
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
)
|
||||
|
||||
type Node interface {
|
||||
|
|
@ -54,10 +55,10 @@ func (n *Array) node() {}
|
|||
func (n *Map) node() {}
|
||||
|
||||
func (n *Scalar) Parent() Parent { return n.parent }
|
||||
func (n *Field) Parent() Parent { return n.parent }
|
||||
func (n *Edge) Parent() Parent { return n.parent }
|
||||
func (n *Array) Parent() Parent { return n.parent }
|
||||
func (n *Map) Parent() Parent { return n.parent }
|
||||
func (n *Field) Parent() Parent { return n.parent }
|
||||
func (n *Edge) Parent() Parent { return n.parent }
|
||||
func (n *Array) Parent() Parent { return n.parent }
|
||||
func (n *Map) Parent() Parent { return n.parent }
|
||||
|
||||
func (n *Scalar) value() {}
|
||||
func (n *Array) value() {}
|
||||
|
|
@ -80,7 +81,17 @@ func (s *Scalar) Copy(newp Parent) Node {
|
|||
}
|
||||
|
||||
func (s *Scalar) Equal(s2 *Scalar) bool {
|
||||
return s.Value.ScalarString() == s2.Value.ScalarString() && s.Value.Type() == s2.Value.Type()
|
||||
if _, ok := s.Value.(d2ast.String); ok {
|
||||
if _, ok = s2.Value.(d2ast.String); ok {
|
||||
return s.Value.ScalarString() == s2.Value.ScalarString()
|
||||
}
|
||||
}
|
||||
return s.Value.Type() == s2.Value.Type() && s.Value.ScalarString() == s2.Value.ScalarString()
|
||||
|
||||
}
|
||||
|
||||
func (s *Scalar) String() string {
|
||||
return d2format.Format(s.Value)
|
||||
}
|
||||
|
||||
type Map struct {
|
||||
|
|
|
|||
4
testdata/d2ir/TestApply/simple/edge.exp.json
generated
vendored
Normal file
4
testdata/d2ir/TestApply/simple/edge.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"fields": null,
|
||||
"edges": null
|
||||
}
|
||||
8
testdata/d2ir/TestApply/simple/field.exp.json
generated
vendored
Normal file
8
testdata/d2ir/TestApply/simple/field.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x"
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
19
testdata/d2ir/TestApply/simple/field/label.exp.json
generated
vendored
Normal file
19
testdata/d2ir/TestApply/simple/field/label.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/field/label.d2,0:3:3-0:6:6",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
27
testdata/d2ir/TestApply/simple/field/label/nested.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/field/label/nested.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"composite": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "y",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/field/label/nested.d2,0:5:5-0:8:8",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
19
testdata/d2ir/TestApply/simple/label.exp.json
generated
vendored
Normal file
19
testdata/d2ir/TestApply/simple/label.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/label.d2,0:3:3-0:6:6",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
27
testdata/d2ir/TestApply/simple/primary#01.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/primary#01.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/primary#01.d2,0:3:3-0:6:6",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"composite": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "pqrs"
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
27
testdata/d2ir/TestApply/simple/primary.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/primary.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/primary.d2,0:3:3-0:6:6",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"composite": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "pqrs"
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
35
testdata/d2ir/TestApply/simple/primary/nested.exp.json
generated
vendored
Normal file
35
testdata/d2ir/TestApply/simple/primary/nested.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"composite": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "y",
|
||||
"primary": {
|
||||
"value": {
|
||||
"range": "d2/testdata/d2ir/TestApply/simple/primary/nested.d2,0:5:5-0:8:8",
|
||||
"value": [
|
||||
{
|
||||
"string": "yes",
|
||||
"raw_string": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"composite": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "pqrs"
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": null
|
||||
}
|
||||
Loading…
Reference in a new issue