d2ir: wip

This commit is contained in:
Anmol Sethi 2023-01-11 15:50:49 -08:00
parent fd241e4425
commit 989fdb0fe5
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
13 changed files with 308 additions and 39 deletions

View file

@ -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
}

View file

@ -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{}{

View file

@ -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"))
}

View file

@ -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

View file

@ -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
View file

@ -0,0 +1,4 @@
{
"fields": null,
"edges": null
}

8
testdata/d2ir/TestApply/simple/field.exp.json generated vendored Normal file
View file

@ -0,0 +1,8 @@
{
"fields": [
{
"name": "x"
}
],
"edges": null
}

19
testdata/d2ir/TestApply/simple/field/label.exp.json generated vendored Normal file
View 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
}

View 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
View 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
View 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
View 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
View 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
}