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(']') 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 { func Key(k *d2ast.KeyPath) []string {
var ids []string return d2format.KeyPath(k)
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
} }
var ReservedKeywords = map[string]struct{}{ var ReservedKeywords = map[string]struct{}{

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"oss.terrastruct.com/d2/d2ast" "oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser" "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 { func Apply(dst *Map, ast *d2ast.Map) error {
var c compiler var c compiler
c.apply(dst, ast) c.compileMap(dst, ast)
if !c.err.Empty() { if !c.err.Empty() {
return c.err return c.err
} }
return nil 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 { for _, n := range ast.Nodes {
if n.MapKey == nil { switch {
continue 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 { 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 { if !ok {
c.errorf(k.Key, "cannot index into array") c.errorf(k, "cannot index into array")
return return
} }
@ -55,6 +56,28 @@ func (c *compiler) applyKey(dst *Map, k *d2ast.Key) {
Value: k.Primary.Unbox(), 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/util-go/diff"
"oss.terrastruct.com/d2/d2ast" "oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2ir" "oss.terrastruct.com/d2/d2ir"
"oss.terrastruct.com/d2/d2parser" "oss.terrastruct.com/d2/d2parser"
) )
@ -31,7 +32,7 @@ func testApplySimple(t *testing.T) {
tca := []testCase{ tca := []testCase{
{ {
name: "one", name: "field",
run: func(t testing.TB, m *d2ir.Map) { run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x`) err := parse(t, m, `x`)
assert.Success(t, err) assert.Success(t, err)
@ -40,6 +41,62 @@ func testApplySimple(t *testing.T) {
assertField(t, m, 0, 0, nil, "x") 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", name: "nested",
run: func(t testing.TB, m *d2ir.Map) { 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, 1, 0, nil, "z")
assertField(t, m, 0, 0, nil, "z", "p") assertField(t, m, 0, 0, nil, "z", "p")
assertEdge(t, m, 0, nil, &d2ir.EdgeID{ assertEdge(t, m, 0, nil, "(x.y -> z.p)[0]")
[]string{"x", "y"}, false,
[]string{"z", "p"}, true,
-1,
})
}, },
}, },
{ {
@ -70,11 +123,7 @@ func testApplySimple(t *testing.T) {
assertField(t, m, 0, 0, nil, "x") assertField(t, m, 0, 0, nil, "x")
assertField(t, m, 0, 0, nil, "z") assertField(t, m, 0, 0, nil, "z")
assertEdge(t, m, 0, nil, &d2ir.EdgeID{ assertEdge(t, m, 0, nil, "(x -> z)[0]")
[]string{"x"}, false,
[]string{"z"}, true,
-1,
})
}, },
}, },
} }
@ -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, nfields, m.FieldCount())
assert.Equal(t, nedges, m.EdgeCount()) assert.Equal(t, nedges, m.EdgeCount())
if !makeScalar(p).Equal(makeScalar(primary)) { 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 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() 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 var m *d2ir.Map
switch n := n.(type) { 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()) assert.Equal(t, nfields, e.Map.FieldCount())
if !makeScalar(e.Primary).Equal(makeScalar(primary)) { 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 return e

View file

@ -6,6 +6,7 @@ import (
"strings" "strings"
"oss.terrastruct.com/d2/d2ast" "oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
) )
type Node interface { type Node interface {
@ -80,7 +81,17 @@ func (s *Scalar) Copy(newp Parent) Node {
} }
func (s *Scalar) Equal(s2 *Scalar) bool { 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 { 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
}