d2ir: References wip

This commit is contained in:
Anmol Sethi 2023-01-17 04:44:14 -08:00
parent 7721c8b2b4
commit f69f401d23
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
6 changed files with 190 additions and 74 deletions

View file

@ -651,7 +651,7 @@ type KeyPath struct {
}
func MakeKeyPath(a []string) *KeyPath {
var kp *KeyPath
kp := &KeyPath{}
for _, el := range a {
kp.Path = append(kp.Path, MakeValueBox(RawString(el, true)).StringBox())
}

View file

@ -50,7 +50,7 @@ func (c *compiler) compileKey(dst *Map, k *d2ast.Key) {
}
func (c *compiler) compileField(dst *Map, k *d2ast.Key) {
f, err := dst.Ensure(d2format.KeyPath(k.Key))
f, err := dst.EnsureField(d2format.KeyPath(k.Key))
if err != nil {
c.errorf(k, err.Error())
return
@ -88,7 +88,7 @@ func (c *compiler) compileField(dst *Map, k *d2ast.Key) {
func (c *compiler) compileEdges(dst *Map, k *d2ast.Key) {
if k.Key != nil && len(k.Key.Path) > 0 {
f, err := dst.Ensure(d2format.KeyPath(k.Key))
f, err := dst.EnsureField(d2format.KeyPath(k.Key))
if err != nil {
c.errorf(k, err.Error())
return
@ -115,7 +115,17 @@ func (c *compiler) compileEdges(dst *Map, k *d2ast.Key) {
}
e = ea[0]
} else {
var err error
_, err := dst.EnsureField(eid.SrcPath)
if err != nil {
c.errorf(k.Edges[i].Src, err.Error())
continue
}
_, err = dst.EnsureField(eid.DstPath)
if err != nil {
c.errorf(k.Edges[i].Dst, err.Error())
continue
}
e, err = dst.EnsureEdge(eid)
if err != nil {
c.errorf(k.Edges[i], err.Error())
@ -123,17 +133,6 @@ func (c *compiler) compileEdges(dst *Map, k *d2ast.Key) {
}
}
_, err := dst.Ensure(eid.SrcPath)
if err != nil {
c.errorf(k.Edges[i].Src, err.Error())
continue
}
_, err = dst.Ensure(eid.DstPath)
if err != nil {
c.errorf(k.Edges[i].Dst, err.Error())
continue
}
if k.EdgeKey != nil {
if e.Map == nil {
e.Map = &Map{

View file

@ -79,7 +79,7 @@ func assertField(t testing.TB, n d2ir.Node, nfields, nedges int, primary interfa
var f *d2ir.Field
if len(ida) > 0 {
f = m.Get(ida)
f = m.GetField(ida)
if f == nil {
t.Fatalf("expected field %v in map %s", ida, m)
}
@ -255,7 +255,7 @@ func testCompileEdge(t *testing.T) {
t.Parallel()
tca := []testCase{
{
name: "edge",
name: "root",
run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x -> y`)
assert.Success(t, err)
@ -285,14 +285,14 @@ func testCompileEdge(t *testing.T) {
{
name: "underscore",
run: func(t testing.TB, m *d2ir.Map) {
err := parse(t, m, `x._ -> z`)
err := parse(t, m, `p: { _.x -> z }`)
assert.Success(t, err)
assertField(t, m, 3, 1, nil)
assertField(t, m, 0, 0, nil, "x")
assertField(t, m, 0, 0, nil, "z")
assertField(t, m, 1, 0, nil, "p")
assertEdge(t, m, 0, nil, "(x -> z)[0]")
assertEdge(t, m, 0, nil, "(x -> p.z)[0]")
},
},
}

View file

@ -5,6 +5,8 @@ import (
"fmt"
"strings"
"oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
)
@ -12,7 +14,9 @@ import (
type Node interface {
node()
ast() d2ast.Node
Copy(newp Parent) Node
Parent() Node
Copy(newp Node) Node
fmt.Stringer
}
@ -22,16 +26,6 @@ var _ Node = &Edge{}
var _ Node = &Array{}
var _ Node = &Map{}
type Parent interface {
Node
Parent() Parent
}
var _ Parent = &Field{}
var _ Parent = &Edge{}
var _ Parent = &Array{}
var _ Parent = &Map{}
type Value interface {
Node
value()
@ -56,11 +50,11 @@ 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 }
func (n *Map) Parent() Parent { return n.parent }
func (n *Scalar) Parent() Node { return n.parent }
func (n *Field) Parent() Node { return n.parent }
func (n *Edge) Parent() Node { return n.parent }
func (n *Array) Parent() Node { return n.parent }
func (n *Map) Parent() Node { return n.parent }
func (n *Scalar) value() {}
func (n *Array) value() {}
@ -76,11 +70,11 @@ func (n *Array) String() string { return d2format.Format(n.ast()) }
func (n *Map) String() string { return d2format.Format(n.ast()) }
type Scalar struct {
parent Parent
parent Node
Value d2ast.Scalar `json:"value"`
}
func (s *Scalar) Copy(newp Parent) Node {
func (s *Scalar) Copy(newp Node) Node {
tmp := *s
s = &tmp
@ -99,12 +93,12 @@ func (s *Scalar) Equal(s2 *Scalar) bool {
}
type Map struct {
parent Parent
parent Node
Fields []*Field `json:"fields"`
Edges []*Edge `json:"edges"`
}
func (m *Map) Copy(newp Parent) Node {
func (m *Map) Copy(newp Node) Node {
tmp := *m
m = &tmp
@ -134,15 +128,15 @@ type Field struct {
Primary *Scalar `json:"primary,omitempty"`
Composite Composite `json:"composite,omitempty"`
References []KeyReference `json:"references,omitempty"`
References []FieldReference `json:"references,omitempty"`
}
func (f *Field) Copy(newp Parent) Node {
func (f *Field) Copy(newp Node) Node {
tmp := *f
f = &tmp
f.parent = newp.(*Map)
f.References = append([]KeyReference(nil), f.References...)
f.References = append([]FieldReference(nil), f.References...)
if f.Primary != nil {
f.Primary = f.Primary.Copy(f).(*Scalar)
}
@ -221,6 +215,30 @@ func (eid *EdgeID) Match(eid2 *EdgeID) bool {
return true
}
func (eid *EdgeID) resolveUnderscores(m *Map) (*EdgeID, *Map, error) {
eid = eid.Copy()
maxUnderscores := go2.Max(countUnderscores(eid.SrcPath), countUnderscores(eid.DstPath))
for i := 0; i < maxUnderscores; i++ {
if eid.SrcPath[0] == "_" {
eid.SrcPath = eid.SrcPath[1:]
} else {
mf := parentField(m)
eid.SrcPath = append([]string{mf.Name}, eid.SrcPath...)
}
if eid.DstPath[0] == "_" {
eid.DstPath = eid.DstPath[1:]
} else {
mf := parentField(m)
eid.DstPath = append([]string{mf.Name}, eid.DstPath...)
}
m = parentMap(m)
if m == nil {
return nil, nil, errors.New("invalid underscore")
}
}
return eid, m, nil
}
func (eid *EdgeID) trimCommon() (common []string, _ *EdgeID) {
eid = eid.Copy()
for len(eid.SrcPath) > 1 && len(eid.DstPath) > 1 {
@ -245,7 +263,7 @@ type Edge struct {
References []EdgeReference `json:"references,omitempty"`
}
func (e *Edge) Copy(newp Parent) Node {
func (e *Edge) Copy(newp Node) Node {
tmp := *e
e = &tmp
@ -261,11 +279,11 @@ func (e *Edge) Copy(newp Parent) Node {
}
type Array struct {
parent Parent
parent Node
Values []Value `json:"values"`
}
func (a *Array) Copy(newp Parent) Node {
func (a *Array) Copy(newp Node) Node {
tmp := *a
a = &tmp
@ -277,14 +295,14 @@ func (a *Array) Copy(newp Parent) Node {
return a
}
type KeyReference struct {
type FieldReference struct {
String *d2ast.StringBox `json:"string"`
KeyPath *d2ast.KeyPath `json:"key_path"`
Context *RefContext `json:"-"`
}
func (kr KeyReference) KeyPathIndex() int {
func (kr FieldReference) KeyPathIndex() int {
for i, sb := range kr.KeyPath.Path {
if sb == kr.String {
return i
@ -293,11 +311,11 @@ func (kr KeyReference) KeyPathIndex() int {
panic("d2ir.KeyReference.KeyPathIndex: String not in KeyPath?")
}
func (kr KeyReference) EdgeDest() bool {
func (kr FieldReference) EdgeDest() bool {
return kr.KeyPath == kr.Context.Edge.Dst
}
func (kr KeyReference) InEdge() bool {
func (kr FieldReference) InEdge() bool {
return kr.KeyPath != kr.Context.Key.Key
}
@ -311,7 +329,6 @@ type RefContext struct {
Scope *d2ast.Map
// UnresolvedScopeMap is prior to interpreting _
ScopeMap *Map
UnresolvedScopeMap *Map
}
@ -347,6 +364,11 @@ func (m *Map) EdgeCountRecursive() int {
return 0
}
acc := len(m.Edges)
for _, f := range m.Fields {
if f_m, ok := f.Composite.(*Map); ok {
acc += f_m.EdgeCountRecursive()
}
}
for _, e := range m.Edges {
if e.Map != nil {
acc += e.Map.EdgeCountRecursive()
@ -355,7 +377,17 @@ func (m *Map) EdgeCountRecursive() int {
return acc
}
func (m *Map) Get(ida []string) *Field {
func (m *Map) GetField(ida []string) *Field {
for len(ida) > 0 && ida[0] == "_" {
m = parentMap(m)
if m == nil {
return nil
}
}
return m.getField(ida)
}
func (m *Map) getField(ida []string) *Field {
if len(ida) == 0 {
return nil
}
@ -363,6 +395,10 @@ func (m *Map) Get(ida []string) *Field {
s := ida[0]
rest := ida[1:]
if s == "_" {
return nil
}
for _, f := range m.Fields {
if !strings.EqualFold(f.Name, s) {
continue
@ -371,20 +407,35 @@ func (m *Map) Get(ida []string) *Field {
return f
}
if f_m, ok := f.Composite.(*Map); ok {
return f_m.Get(rest)
return f_m.getField(rest)
}
}
return nil
}
func (m *Map) Ensure(ida []string) (*Field, error) {
func (m *Map) EnsureField(ida []string) (*Field, error) {
for len(ida) > 0 && ida[0] == "_" {
m = parentMap(m)
if m == nil {
return nil, errors.New("invalid underscore")
}
ida = ida[1:]
}
return m.ensureField(ida)
}
func (m *Map) ensureField(ida []string) (*Field, error) {
if len(ida) == 0 {
return nil, errors.New("empty ida")
return nil, errors.New("invalid underscore")
}
s := ida[0]
rest := ida[1:]
if s == "_" {
return nil, errors.New(`parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
}
for _, f := range m.Fields {
if !strings.EqualFold(f.Name, s) {
continue
@ -394,14 +445,14 @@ func (m *Map) Ensure(ida []string) (*Field, error) {
}
switch fc := f.Composite.(type) {
case *Map:
return fc.Ensure(rest)
return fc.ensureField(rest)
case *Array:
return nil, errors.New("cannot index into array")
}
f.Composite = &Map{
parent: f,
}
return f.Composite.(*Map).Ensure(rest)
return f.Composite.(*Map).ensureField(rest)
}
f := &Field{
@ -415,7 +466,7 @@ func (m *Map) Ensure(ida []string) (*Field, error) {
f.Composite = &Map{
parent: f,
}
return f.Composite.(*Map).Ensure(rest)
return f.Composite.(*Map).ensureField(rest)
}
func (m *Map) Delete(ida []string) bool {
@ -442,9 +493,13 @@ func (m *Map) Delete(ida []string) bool {
}
func (m *Map) GetEdges(eid *EdgeID) []*Edge {
eid, m, err := eid.resolveUnderscores(m)
if err != nil {
return nil
}
common, eid := eid.trimCommon()
if len(common) > 0 {
f := m.Get(common)
f := m.GetField(common)
if f == nil {
return nil
}
@ -464,9 +519,13 @@ func (m *Map) GetEdges(eid *EdgeID) []*Edge {
}
func (m *Map) EnsureEdge(eid *EdgeID) (*Edge, error) {
eid, m, err := eid.resolveUnderscores(m)
if err != nil {
return nil, err
}
common, eid := eid.trimCommon()
if len(common) > 0 {
f, err := m.Ensure(common)
f, err := m.EnsureField(common)
if err != nil {
return nil, err
}
@ -560,8 +619,10 @@ func (m *Map) ast() d2ast.Node {
return nil
}
astMap := &d2ast.Map{}
if m.parent != nil {
astMap.Range = d2ast.MakeRange(",1:0:0-1:0:0")
if m.parent == nil {
astMap.Range = d2ast.MakeRange(",0:0:0-1:0:0")
} else {
astMap.Range = d2ast.MakeRange(",1:0:0-2:0:0")
}
for _, f := range m.Fields {
astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.ast().(d2ast.MapNode)))
@ -572,15 +633,15 @@ func (m *Map) ast() d2ast.Node {
return astMap
}
func (m *Map) appendKeyReferences(i int, kp *d2ast.KeyPath, refctx *RefContext) {
func (m *Map) appendFieldReferences(i int, kp *d2ast.KeyPath, refctx *RefContext) {
sb := kp.Path[i]
f := m.Get([]string{sb.Unbox().ScalarString()})
f := m.GetField([]string{sb.Unbox().ScalarString()})
if f == nil {
return
}
f.References = append(f.References, KeyReference{
String: sb,
f.References = append(f.References, FieldReference{
String: sb,
KeyPath: kp,
Context: refctx,
})
@ -588,7 +649,7 @@ func (m *Map) appendKeyReferences(i int, kp *d2ast.KeyPath, refctx *RefContext)
return
}
if f_m, ok := f.Composite.(*Map); ok {
f_m.appendKeyReferences(i+1, kp, refctx)
f_m.appendFieldReferences(i+1, kp, refctx)
}
}
@ -596,6 +657,37 @@ func (m *Map) appendEdgeReferences(e *Edge, refctx *RefContext) {
e.References = append(e.References, EdgeReference{
Context: refctx,
})
m.appendKeyReferences(0, refctx.Edge.Src, refctx)
m.appendKeyReferences(0, refctx.Edge.Dst, refctx)
m.appendFieldReferences(0, refctx.Edge.Src, refctx)
m.appendFieldReferences(0, refctx.Edge.Dst, refctx)
}
func parentMap(n Node) *Map {
for n.Parent() != nil {
n = n.Parent()
if n_m, ok := n.(*Map); ok {
return n_m
}
}
return nil
}
func parentField(n Node) *Field {
for n.Parent() != nil {
n = n.Parent()
if n_f, ok := n.(*Field); ok {
return n_f
}
}
return nil
}
func countUnderscores(p []string) int {
var count int
for _, el := range p {
if el != "_" {
break
}
count++
}
return count
}

25
testdata/d2ir/TestCompile/edge/root.exp.json generated vendored Normal file
View file

@ -0,0 +1,25 @@
{
"fields": [
{
"name": "x"
},
{
"name": "y"
}
],
"edges": [
{
"edge_id": {
"src_path": [
"x"
],
"src_arrow": false,
"dst_path": [
"y"
],
"dst_arrow": true,
"index": 0
}
}
]
}

View file

@ -1,29 +1,29 @@
{
"fields": [
{
"name": "x",
"name": "p",
"composite": {
"fields": [
{
"name": "_"
"name": "z"
}
],
"edges": null
}
},
{
"name": "z"
"name": "x"
}
],
"edges": [
{
"edge_id": {
"src_path": [
"x",
"_"
"x"
],
"src_arrow": false,
"dst_path": [
"p",
"z"
],
"dst_arrow": true,