d2ir: wip

This commit is contained in:
Anmol Sethi 2023-01-11 11:05:50 -08:00
parent 4b50748dd0
commit 83ef53dc40
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
8 changed files with 139 additions and 87 deletions

View file

@ -24,7 +24,10 @@ 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)
return c.err
if !c.err.Empty() {
return c.err
}
return nil
}
func (c *compiler) apply(dst *Map, ast *d2ast.Map) {

View file

@ -7,20 +7,17 @@ import (
"strings"
"testing"
"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2ir"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/internal/assert"
)
type testCase struct {
name string
text string
base *d2ir.Map
exp func(testing.TB, *d2ir.Map, error)
run func(testing.TB, *d2ir.Map)
}
func TestApply(t *testing.T) {
@ -30,12 +27,13 @@ func TestApply(t *testing.T) {
}
func testApplySimple(t *testing.T) {
tcs := []testCase{
t.Parallel()
tca := []testCase{
{
name: "one",
text: `x`,
exp: func(t testing.TB, m *d2ir.Map, err error) {
run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x`)
assert.Success(t, err)
assertField(t, m, 1, 0, nil)
@ -44,9 +42,8 @@ func testApplySimple(t *testing.T) {
},
{
name: "nested",
text: `x.y -> z.p`,
exp: func(t testing.TB, m *d2ir.Map, err error) {
run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x.y -> z.p`)
assert.Success(t, err)
assertField(t, m, 4, 1, nil)
@ -65,9 +62,8 @@ func testApplySimple(t *testing.T) {
},
{
name: "underscore_parent",
text: `x._ -> z`,
exp: func(t testing.TB, m *d2ir.Map, err error) {
run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x._ -> z`)
assert.Success(t, err)
assertField(t, m, 2, 1, nil)
@ -83,63 +79,76 @@ func testApplySimple(t *testing.T) {
},
}
runa(t, tcs)
runa(t, tca)
}
func runa(t *testing.T, tcs []testCase) {
for _, tc := range tcs {
func runa(t *testing.T, tca []testCase) {
for _, tc := range tca {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
run(t, tc)
m := &d2ir.Map{}
tc.run(t, m)
})
}
}
func run(t testing.TB, tc testCase) {
func parse(t testing.TB, dst *d2ir.Map, text string) error {
t.Helper()
d2Path := fmt.Sprintf("d2/testdata/d2ir/%v.d2", t.Name())
ast, err := d2parser.Parse(d2Path, strings.NewReader(tc.text), nil)
if err != nil {
tc.exp(t, nil, err)
t.FailNow()
return
}
ast, err := d2parser.Parse(d2Path, strings.NewReader(text), nil)
assert.Success(t, err)
dst := tc.base.Copy(nil).(*d2ir.Map)
err = d2ir.Apply(dst, ast)
tc.exp(t, dst, err)
err = diff.Testdata(filepath.Join("..", "testdata", "d2ir", t.Name()), dst)
if err != nil {
tc.exp(t, nil, err)
t.FailNow()
return
return err
}
err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2ir", t.Name()), dst)
return err
}
func assertField(t testing.TB, n d2ir.Node, nfields, nedges int, primary interface{}, ida ...string) *d2ir.Field {
t.Helper()
m := d2ir.NodeToMap(n)
p := d2ir.NodeToPrimary(n)
var m *d2ir.Map
p := &d2ir.Scalar{
Value: &d2ast.Null{},
}
switch n := n.(type) {
case *d2ir.Field:
mm, ok := n.Composite.(*d2ir.Map)
if ok {
m = mm
} else {
t.Fatalf("unexpected d2ir.Field.Composite %T", n.Composite)
}
p = n.Primary
case *d2ir.Map:
m = n
p.Value = &d2ast.Null{}
default:
t.Fatalf("unexpected d2ir.Node %T", n)
}
var f *d2ir.Field
var ok bool
if len(ida) > 0 {
f = m.Get(ida)
if f == nil {
t.Fatalf("expected field %#v in map %#v but not found", ida, m)
f, ok = m.Get(ida)
if !ok {
t.Fatalf("expected field %v in map %s", ida, m)
}
p = f.Primary
m = d2ir.NodeToMap(f)
if f_m, ok := f.Composite.(*d2ir.Map); ok {
m = f_m
} else {
m = &d2ir.Map{}
}
}
if m.FieldCount() != nfields {
t.Fatalf("expected %d fields but got %d", nfields, m.FieldCount())
}
if m.EdgeCount() != nedges {
t.Fatalf("expected %d edges but got %d", nedges, m.EdgeCount())
}
assert.Equal(t, nfields, m.FieldCount())
assert.Equal(t, nedges, m.EdgeCount())
if !p.Equal(makeScalar(primary)) {
t.Fatalf("expected primary %#v but %#v", primary, p)
}
@ -150,19 +159,27 @@ func assertField(t testing.TB, n d2ir.Node, nfields, nedges int, primary interfa
func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eid *d2ir.EdgeID) *d2ir.Edge {
t.Helper()
m := d2ir.NodeToMap(n)
e := m.GetEdge(eid)
if e == nil {
t.Fatalf("expected edge %#v in map %#v but not found", eid, m)
var m *d2ir.Map
switch n := n.(type) {
case *d2ir.Field:
mm, ok := n.Composite.(*d2ir.Map)
if ok {
m = mm
} else {
t.Fatalf("unexpected d2ir.Field.Composite %T", n.Composite)
}
case *d2ir.Map:
m = n
default:
t.Fatalf("unexpected d2ir.Node %T", n)
}
if e.Map.FieldCount() != nfields {
t.Fatalf("expected %d fields but got %d", nfields, e.Map.FieldCount())
}
if e.Map.EdgeCount() != 0 {
t.Fatalf("expected %d edges but got %d", 0, e.Map.EdgeCount())
e, ok := m.GetEdge(eid)
if !ok {
t.Fatalf("expected edge %v in map %s but not found", eid, m)
}
assert.Equal(t, nfields, e.Map.FieldCount())
if !e.Primary.Equal(makeScalar(primary)) {
t.Fatalf("expected primary %#v but %#v", primary, e.Primary)
}

View file

@ -1,6 +1,8 @@
package d2ir
import (
"encoding/json"
"fmt"
"strings"
"oss.terrastruct.com/d2/d2ast"
@ -51,6 +53,7 @@ func (n *Edge) node() {}
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 }
@ -77,6 +80,17 @@ func (s *Scalar) Copy(newp Parent) Node {
}
func (s *Scalar) Equal(s2 *Scalar) bool {
if s == nil {
if s2 == nil {
return true
}
_, ok := s2.Value.(*d2ast.Null)
return ok
}
if s2 == nil {
_, ok := s.Value.(*d2ast.Null)
return ok
}
return s.Value.ScalarString() == s2.Value.ScalarString() && s.Value.Type() == s2.Value.Type()
}
@ -113,10 +127,10 @@ type Field struct {
Name string `json:"name"`
Primary *Scalar `json:"primary"`
Composite Composite `json:"composite"`
Primary *Scalar `json:"primary,omitempty"`
Composite Composite `json:"composite,omitempty"`
Refs []KeyReference `json:"refs"`
Refs []KeyReference `json:"refs,omitempty"`
}
func (f *Field) Copy(newp Parent) Node {
@ -203,10 +217,10 @@ type Edge struct {
ID *EdgeID `json:"edge_id"`
Primary *Scalar `json:"primary"`
Map *Map `json:"map"`
Primary *Scalar `json:"primary,omitempty"`
Map *Map `json:"map,omitempty"`
Refs []EdgeReference `json:"refs"`
Refs []EdgeReference `json:"refs,omitempty"`
}
func (e *Edge) Copy(newp Parent) Node {
@ -384,3 +398,11 @@ func (m *Map) GetEdge(eid *EdgeID) (*Edge, bool) {
}
return nil, false
}
func (m *Map) String() string {
b, err := json.Marshal(m)
if err != nil {
panic(fmt.Sprintf("d2ir: failed to marshal d2ir.Map: %v", err))
}
return string(b)
}

View file

@ -3,9 +3,10 @@ package d2ir_test
import (
"testing"
"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2ir"
"oss.terrastruct.com/d2/internal/assert"
)
func TestCopy(t *testing.T) {
@ -13,14 +14,11 @@ func TestCopy(t *testing.T) {
const scalStr = `Those who claim the dead never return to life haven't ever been around.`
s := &d2ir.Scalar{
parent: nil,
Value: d2ast.FlatUnquotedString(scalStr),
Value: d2ast.FlatUnquotedString(scalStr),
}
a := &d2ir.Array{
Parent: nil,
Values: []d2ir.Value{
&d2ir.Scalar{
parent: nil,
Value: &d2ast.Boolean{
Value: true,
},
@ -28,7 +26,6 @@ func TestCopy(t *testing.T) {
},
}
m2 := &d2ir.Map{
Parent: nil,
Fields: []*d2ir.Field{
{Primary: s},
},
@ -36,20 +33,17 @@ func TestCopy(t *testing.T) {
const keyStr = `Absence makes the heart grow frantic.`
f := &d2ir.Field{
Parent: nil,
Name: keyStr,
Name: keyStr,
Primary: s,
Composite: a,
}
e := &d2ir.Edge{
Parent: nil,
Primary: s,
Map: m2,
}
m := &d2ir.Map{
Parent: nil,
Fields: []*d2ir.Field{f},
Edges: []*d2ir.Edge{e},
@ -58,20 +52,20 @@ func TestCopy(t *testing.T) {
m = m.Copy(nil).(*d2ir.Map)
f.Name = `Many a wife thinks her husband is the world's greatest lover.`
assert.Equal(t, m, m.Fields[0].Parent)
assert.Equal(t, m, m.Fields[0].Parent())
assert.Equal(t, keyStr, m.Fields[0].Name)
assert.Equal(t, m.Fields[0], m.Fields[0].Primary.parent)
assert.Equal(t, m.Fields[0], m.Fields[0].Composite.(*d2ir.Array).Parent)
assert.Equal(t, m.Fields[0], m.Fields[0].Primary.Parent())
assert.Equal(t, m.Fields[0], m.Fields[0].Composite.(*d2ir.Array).Parent())
assert.Equal(t,
m.Fields[0].Composite,
m.Fields[0].Composite.(*d2ir.Array).Values[0].(*d2ir.Scalar).parent,
m.Fields[0].Composite.(*d2ir.Array).Values[0].(*d2ir.Scalar).Parent(),
)
assert.Equal(t, m, m.Edges[0].Parent)
assert.Equal(t, m.Edges[0], m.Edges[0].Primary.parent)
assert.Equal(t, m.Edges[0], m.Edges[0].Map.Parent)
assert.Equal(t, m, m.Edges[0].Parent())
assert.Equal(t, m.Edges[0], m.Edges[0].Primary.Parent())
assert.Equal(t, m.Edges[0], m.Edges[0].Map.Parent())
assert.Equal(t, m.Edges[0].Map, m.Edges[0].Map.Fields[0].Parent)
assert.Equal(t, m.Edges[0].Map.Fields[0], m.Edges[0].Map.Fields[0].Primary.parent)
assert.Equal(t, m.Edges[0].Map, m.Edges[0].Map.Fields[0].Parent())
assert.Equal(t, m.Edges[0].Map.Fields[0], m.Edges[0].Map.Fields[0].Primary.Parent())
}

View file

@ -45,7 +45,7 @@ func Parse(path string, r io.RuneReader, opts *ParseOptions) (*d2ast.Map, error)
}
m := p.parseMap(true)
if !p.err.empty() {
if !p.err.Empty() {
return m, p.err
}
return m, nil
@ -57,7 +57,7 @@ func ParseKey(key string) (*d2ast.KeyPath, error) {
}
k := p.parseKey()
if !p.err.empty() {
if !p.err.Empty() {
return nil, fmt.Errorf("failed to parse key %q: %w", key, p.err)
}
if k == nil {
@ -72,7 +72,7 @@ func ParseMapKey(mapKey string) (*d2ast.Key, error) {
}
mk := p.parseMapKey()
if !p.err.empty() {
if !p.err.Empty() {
return nil, fmt.Errorf("failed to parse map key %q: %w", mapKey, p.err)
}
if mk == nil {
@ -87,7 +87,7 @@ func ParseValue(value string) (d2ast.Value, error) {
}
v := p.parseValue()
if !p.err.empty() {
if !p.err.Empty() {
return nil, fmt.Errorf("failed to parse value %q: %w", value, p.err)
}
if v.Unbox() == nil {
@ -130,7 +130,7 @@ type ParseError struct {
Errors []d2ast.Error `json:"errs"`
}
func (pe ParseError) empty() bool {
func (pe ParseError) Empty() bool {
return pe.IOError == nil && len(pe.Errors) == 0
}

4
testdata/d2ir/TestApply/simple/nested.exp.json generated vendored Normal file
View file

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

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

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

View file

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