d2compiler: Integrate d2ir (wip)

This commit is contained in:
Anmol Sethi 2023-01-22 00:21:21 -08:00
parent afa26752e6
commit 3e7bdc5468
No known key found for this signature in database
GPG key ID: 25BC68888A99A8BA
5 changed files with 170 additions and 51 deletions

View file

@ -18,10 +18,11 @@ import (
func GenDSL(maxi int) (_ string, err error) { func GenDSL(maxi int) (_ string, err error) {
gs := &dslGenState{ gs := &dslGenState{
rand: mathrand.New(mathrand.NewSource(time.Now().UnixNano())), rand: mathrand.New(mathrand.NewSource(time.Now().UnixNano())),
g: d2graph.NewGraph(&d2ast.Map{}), g: d2graph.NewGraph(),
nodeShapes: make(map[string]string), nodeShapes: make(map[string]string),
nodeContainer: make(map[string]string), nodeContainer: make(map[string]string),
} }
gs.g.AST = &d2ast.Map{}
err = gs.gen(maxi) err = gs.gen(maxi)
if err != nil { if err != nil {
return "", err return "", err

View file

@ -25,6 +25,7 @@ const INNER_LABEL_PADDING int = 5
const DEFAULT_SHAPE_PADDING = 100. const DEFAULT_SHAPE_PADDING = 100.
type Graph struct { type Graph struct {
Name string `json:"name"`
AST *d2ast.Map `json:"ast"` AST *d2ast.Map `json:"ast"`
Root *Object `json:"root"` Root *Object `json:"root"`
@ -36,10 +37,8 @@ type Graph struct {
Steps []*Graph `json:"steps,omitempty"` Steps []*Graph `json:"steps,omitempty"`
} }
func NewGraph(ast *d2ast.Map) *Graph { func NewGraph() *Graph {
d := &Graph{ d := &Graph{}
AST: ast,
}
d.Root = &Object{ d.Root = &Object{
Graph: d, Graph: d,
Parent: nil, Parent: nil,
@ -552,6 +551,7 @@ func (obj *Object) HasEdge(mk *d2ast.Key) (*Edge, bool) {
return nil, false return nil, false
} }
// TODO: remove once not used anywhere
func ResolveUnderscoreKey(ida []string, obj *Object) (resolvedObj *Object, resolvedIDA []string, _ error) { func ResolveUnderscoreKey(ida []string, obj *Object) (resolvedObj *Object, resolvedIDA []string, _ error) {
if len(ida) > 0 && !obj.IsSequenceDiagram() { if len(ida) > 0 && !obj.IsSequenceDiagram() {
objSD := obj.OuterSequenceDiagram() objSD := obj.OuterSequenceDiagram()
@ -635,6 +635,12 @@ func (obj *Object) FindEdges(mk *d2ast.Key) ([]*Edge, bool) {
// EnsureChild grabs the child by ids or creates it if it does not exist including all // EnsureChild grabs the child by ids or creates it if it does not exist including all
// intermediate nodes. // intermediate nodes.
func (obj *Object) EnsureChild(ids []string) *Object { func (obj *Object) EnsureChild(ids []string) *Object {
switch obj.Attributes.Shape.Value {
case d2target.ShapeClass, d2target.ShapeSQLTable:
// This will only be called for connecting edges where we want to truncate to the
// container.
return obj
}
_, is := ReservedKeywordHolders[ids[0]] _, is := ReservedKeywordHolders[ids[0]]
if len(ids) == 1 && !is { if len(ids) == 1 && !is {
_, ok := ReservedKeywords[ids[0]] _, ok := ReservedKeywords[ids[0]]
@ -954,7 +960,7 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label
return nil, errors.New("connections within sequence diagrams can connect only to other objects within the same sequence diagram") return nil, errors.New("connections within sequence diagrams can connect only to other objects within the same sequence diagram")
} }
edge := &Edge{ e := &Edge{
Attributes: &Attributes{ Attributes: &Attributes{
Label: Scalar{ Label: Scalar{
Value: label, Value: label,
@ -965,10 +971,40 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label
Dst: dst, Dst: dst,
DstArrow: dstArrow, DstArrow: dstArrow,
} }
edge.initIndex() e.initIndex()
obj.Graph.Edges = append(obj.Graph.Edges, edge) if src.Attributes.Shape.Value == d2target.ShapeSQLTable {
return edge, nil objAbsID := obj.AbsIDArray()
srcAbsID := src.AbsIDArray()
if len(objAbsID) + len(srcID) > len(srcAbsID) {
for i, d2col := range src.SQLTable.Columns {
if d2col.Name.Label == srcID[len(srcID)-1] {
d2col.Reference = dst.AbsID()
e.SrcTableColumnIndex = new(int)
*e.SrcTableColumnIndex = i
break
}
}
}
}
if dst.Attributes.Shape.Value == d2target.ShapeSQLTable {
objAbsID := obj.AbsIDArray()
dstAbsID := dst.AbsIDArray()
if len(objAbsID) + len(dstID) > len(dstAbsID) {
for i, d2col := range dst.SQLTable.Columns {
if d2col.Name.Label == dstID[len(dstID)-1] {
d2col.Reference = dst.AbsID()
e.DstTableColumnIndex = new(int)
*e.DstTableColumnIndex = i
break
}
}
}
}
obj.Graph.Edges = append(obj.Graph.Edges, e)
return e, nil
} }
// TODO: Treat undirectional/bidirectional edge here and in HasEdge flipped. Same with // TODO: Treat undirectional/bidirectional edge here and in HasEdge flipped. Same with

View file

@ -37,9 +37,7 @@ func (c *compiler) compileScenarios(m *Map) {
for _, sf := range scenarios.Fields { for _, sf := range scenarios.Fields {
if sf.Map() == nil { if sf.Map() == nil {
sf.Composite = &Map{ continue
parent: sf,
}
} }
base := m.CopyBase(sf) base := m.CopyBase(sf)
sf.Composite = Overlay(base, sf.Map()) sf.Composite = Overlay(base, sf.Map())
@ -59,9 +57,7 @@ func (c *compiler) compileSteps(m *Map) {
} }
for i, sf := range steps.Fields { for i, sf := range steps.Fields {
if sf.Map() == nil { if sf.Map() == nil {
sf.Composite = &Map{ continue
parent: sf,
}
} }
var base *Map var base *Map

View file

@ -18,13 +18,21 @@ import (
// to indicate the offending AST node. // to indicate the offending AST node.
type Node interface { type Node interface {
node() node()
Copy(parent Node) Node Copy(newParent Node) Node
Parent() Node Parent() Node
Primary() *Scalar Primary() *Scalar
Map() *Map Map() *Map
AST() d2ast.Node ast() d2ast.Node
fmt.Stringer fmt.Stringer
LastRef() Reference
LastPrimaryKey() *d2ast.Key
}
type Reference interface {
reference()
AST() d2ast.Node
} }
var _ Node = &Scalar{} var _ Node = &Scalar{}
@ -71,12 +79,20 @@ func (n *Map) Primary() *Scalar { return nil }
func (n *Scalar) Map() *Map { return nil } func (n *Scalar) Map() *Map { return nil }
func (n *Field) Map() *Map { func (n *Field) Map() *Map {
if n == nil {
return nil
}
if n.Composite == nil { if n.Composite == nil {
return nil return nil
} }
return n.Composite.Map() return n.Composite.Map()
} }
func (n *Edge) Map() *Map { return n.Map_ } func (n *Edge) Map() *Map {
if n == nil {
return nil
}
return n.Map_
}
func (n *Array) Map() *Map { return nil } func (n *Array) Map() *Map { return nil }
func (n *Map) Map() *Map { return n } func (n *Map) Map() *Map { return n }
@ -87,22 +103,22 @@ func (n *Map) value() {}
func (n *Array) composite() {} func (n *Array) composite() {}
func (n *Map) composite() {} func (n *Map) composite() {}
func (n *Scalar) String() string { return d2format.Format(n.AST()) } func (n *Scalar) String() string { return d2format.Format(n.ast()) }
func (n *Field) String() string { return d2format.Format(n.AST()) } func (n *Field) String() string { return d2format.Format(n.ast()) }
func (n *Edge) String() string { return d2format.Format(n.AST()) } func (n *Edge) String() string { return d2format.Format(n.ast()) }
func (n *Array) String() string { return d2format.Format(n.AST()) } func (n *Array) String() string { return d2format.Format(n.ast()) }
func (n *Map) String() string { return d2format.Format(n.AST()) } func (n *Map) String() string { return d2format.Format(n.ast()) }
type Scalar struct { type Scalar struct {
parent Node parent Node
Value d2ast.Scalar `json:"value"` Value d2ast.Scalar `json:"value"`
} }
func (s *Scalar) Copy(newp Node) Node { func (s *Scalar) Copy(newParent Node) Node {
tmp := *s tmp := *s
s = &tmp s = &tmp
s.parent = newp s.parent = newParent
return s return s
} }
@ -121,11 +137,11 @@ type Map struct {
Edges []*Edge `json:"edges"` Edges []*Edge `json:"edges"`
} }
func (m *Map) Copy(newp Node) Node { func (m *Map) Copy(newParent Node) Node {
tmp := *m tmp := *m
m = &tmp m = &tmp
m.parent = newp m.parent = newParent
pfields := m.Fields pfields := m.Fields
m.Fields = make([]*Field, 0, len(pfields)) m.Fields = make([]*Field, 0, len(pfields))
for _, f := range pfields { for _, f := range pfields {
@ -139,11 +155,11 @@ func (m *Map) Copy(newp Node) Node {
} }
// CopyBase copies the map m without layers/scenarios/steps. // CopyBase copies the map m without layers/scenarios/steps.
func (m *Map) CopyBase(newp Node) *Map { func (m *Map) CopyBase(newParent Node) *Map {
layers := m.DeleteField("layers") layers := m.DeleteField("layers")
scenarios := m.DeleteField("scenarios") scenarios := m.DeleteField("scenarios")
steps := m.DeleteField("steps") steps := m.DeleteField("steps")
m2 := m.Copy(newp).(*Map) m2 := m.Copy(newParent).(*Map)
if layers != nil { if layers != nil {
m.Fields = append(m.Fields, layers) m.Fields = append(m.Fields, layers)
} }
@ -161,6 +177,17 @@ func (m *Map) Root() bool {
return m.parent == nil return m.parent == nil
} }
func (f *Map) LastRef() *MapReference {
if f.parent == nil {
return nil
}
return f.References[len(f.References)-1]
}
func (f *Map) LastAST() d2ast.Node {
return f.LastRef().String
}
type LayerKind string type LayerKind string
const ( const (
@ -206,11 +233,11 @@ type Field struct {
References []FieldReference `json:"references,omitempty"` References []FieldReference `json:"references,omitempty"`
} }
func (f *Field) Copy(newp Node) Node { func (f *Field) Copy(newParent Node) Node {
tmp := *f tmp := *f
f = &tmp f = &tmp
f.parent = newp.(*Map) f.parent = newParent.(*Map)
f.References = append([]FieldReference(nil), f.References...) f.References = append([]FieldReference(nil), f.References...)
if f.Primary_ != nil { if f.Primary_ != nil {
f.Primary_ = f.Primary_.Copy(f).(*Scalar) f.Primary_ = f.Primary_.Copy(f).(*Scalar)
@ -221,6 +248,39 @@ func (f *Field) Copy(newp Node) Node {
return f return f
} }
func (f *Field) LastPrimaryRef() *FieldReference {
inEdge := ParentEdge(f) != nil
for i := len(f.References) - 1; i >= 0; i-- {
fr := f.References[i]
if inEdge && len(fr.Context.Key.Edges) > 0 {
if fr.String == fr.Context.Key.EdgeKey.Path[len(fr.Context.Key.EdgeKey.Path)-1].Unbox() {
return fr
}
} else {
if fr.String == fr.Context.Key.Key.Path[len(fr.Context.Key.Key.Path)-1].Unbox() {
return fr
}
}
}
return nil
}
func (f *Field) LastPrimaryKey() *d2ast.Key {
fr := f.LastModification()
if fr == nil {
return nil
}
return fr.Context.Key
}
func (f *Field) LastRef() *FieldReference {
return f.References[len(f.References)-1]
}
func (f *Field) LastAST() d2ast.Node {
return f.LastRef().String
}
type EdgeID struct { type EdgeID struct {
SrcPath []string `json:"src_path"` SrcPath []string `json:"src_path"`
SrcArrow bool `json:"src_arrow"` SrcArrow bool `json:"src_arrow"`
@ -339,11 +399,11 @@ type Edge struct {
References []EdgeReference `json:"references,omitempty"` References []EdgeReference `json:"references,omitempty"`
} }
func (e *Edge) Copy(newp Node) Node { func (e *Edge) Copy(newParent Node) Node {
tmp := *e tmp := *e
e = &tmp e = &tmp
e.parent = newp.(*Map) e.parent = newParent.(*Map)
e.References = append([]EdgeReference(nil), e.References...) e.References = append([]EdgeReference(nil), e.References...)
if e.Primary_ != nil { if e.Primary_ != nil {
e.Primary_ = e.Primary_.Copy(e).(*Scalar) e.Primary_ = e.Primary_.Copy(e).(*Scalar)
@ -354,16 +414,42 @@ func (e *Edge) Copy(newp Node) Node {
return e return e
} }
func (e *Edge) LastPrimaryRef() *EdgeReference {
for i := len(e.References) - 1; i >= 0; i-- {
fr := e.References[i]
if fr.Context.Key.EdgeKey == nil {
return fr
}
}
return nil
}
func (e *Edge) LastPrimaryKey() *d2ast.Key {
fr := f.LastModification()
if fr == nil {
return nil
}
return fr.Context.Key
}
func (e *Edge) LastRef() *EdgeReference {
return e.References[len(e.References)-1]
}
func (e *Edge) LastAST() d2ast.Node {
return e.LastRef().Context.Edge
}
type Array struct { type Array struct {
parent Node parent Node
Values []Value `json:"values"` Values []Value `json:"values"`
} }
func (a *Array) Copy(newp Node) Node { func (a *Array) Copy(newParent Node) Node {
tmp := *a tmp := *a
a = &tmp a = &tmp
a.parent = newp a.parent = newParent
a.Values = append([]Value(nil), a.Values...) a.Values = append([]Value(nil), a.Values...)
for i := range a.Values { for i := range a.Values {
a.Values[i] = a.Values[i].Copy(a).(Value) a.Values[i] = a.Values[i].Copy(a).(Value)
@ -400,8 +486,8 @@ type EdgeReference struct {
} }
type RefContext struct { type RefContext struct {
Key *d2ast.Key `json:"key"`
Edge *d2ast.Edge `json:"edge"` Edge *d2ast.Edge `json:"edge"`
Key *d2ast.Key `json:"key"`
Scope *d2ast.Map `json:"-"` Scope *d2ast.Map `json:"-"`
} }
@ -696,11 +782,11 @@ func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) (*Edge, error) {
return e, nil return e, nil
} }
func (s *Scalar) AST() d2ast.Node { func (s *Scalar) ast() d2ast.Node {
return s.Value return s.Value
} }
func (f *Field) AST() d2ast.Node { func (f *Field) ast() d2ast.Node {
k := &d2ast.Key{ k := &d2ast.Key{
Key: &d2ast.KeyPath{ Key: &d2ast.KeyPath{
Path: []*d2ast.StringBox{ Path: []*d2ast.StringBox{
@ -710,16 +796,16 @@ func (f *Field) AST() d2ast.Node {
} }
if f.Primary_ != nil { if f.Primary_ != nil {
k.Primary = d2ast.MakeValueBox(f.Primary_.AST().(d2ast.Value)).ScalarBox() k.Primary = d2ast.MakeValueBox(f.Primary_.ast().(d2ast.Value)).ScalarBox()
} }
if f.Composite != nil { if f.Composite != nil {
k.Value = d2ast.MakeValueBox(f.Composite.AST().(d2ast.Value)) k.Value = d2ast.MakeValueBox(f.Composite.ast().(d2ast.Value))
} }
return k return k
} }
func (e *Edge) AST() d2ast.Node { func (e *Edge) ast() d2ast.Node {
astEdge := &d2ast.Edge{} astEdge := &d2ast.Edge{}
astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath) astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath)
@ -736,27 +822,27 @@ func (e *Edge) AST() d2ast.Node {
} }
if e.Primary_ != nil { if e.Primary_ != nil {
k.Primary = d2ast.MakeValueBox(e.Primary_.AST().(d2ast.Value)).ScalarBox() k.Primary = d2ast.MakeValueBox(e.Primary_.ast().(d2ast.Value)).ScalarBox()
} }
if e.Map_ != nil { if e.Map_ != nil {
k.Value = d2ast.MakeValueBox(e.Map_.AST().(*d2ast.Map)) k.Value = d2ast.MakeValueBox(e.Map_.ast().(*d2ast.Map))
} }
return k return k
} }
func (a *Array) AST() d2ast.Node { func (a *Array) ast() d2ast.Node {
if a == nil { if a == nil {
return nil return nil
} }
astArray := &d2ast.Array{} astArray := &d2ast.Array{}
for _, av := range a.Values { for _, av := range a.Values {
astArray.Nodes = append(astArray.Nodes, d2ast.MakeArrayNodeBox(av.AST().(d2ast.ArrayNode))) astArray.Nodes = append(astArray.Nodes, d2ast.MakeArrayNodeBox(av.ast().(d2ast.ArrayNode)))
} }
return astArray return astArray
} }
func (m *Map) AST() d2ast.Node { func (m *Map) ast() d2ast.Node {
if m == nil { if m == nil {
return nil return nil
} }
@ -767,10 +853,10 @@ func (m *Map) AST() d2ast.Node {
astMap.Range = d2ast.MakeRange(",1:0:0-2:0:0") astMap.Range = d2ast.MakeRange(",1:0:0-2:0:0")
} }
for _, f := range m.Fields { for _, f := range m.Fields {
astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.AST().(d2ast.MapNode))) astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.ast().(d2ast.MapNode)))
} }
for _, e := range m.Edges { for _, e := range m.Edges {
astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(e.AST().(d2ast.MapNode))) astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(e.ast().(d2ast.MapNode)))
} }
return astMap return astMap
} }

View file

@ -377,7 +377,7 @@ container -> c: edge 1
} }
func TestSelfEdges(t *testing.T) { func TestSelfEdges(t *testing.T) {
g := d2graph.NewGraph(nil) g := d2graph.NewGraph()
g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
n1 := g.Root.EnsureChild([]string{"n1"}) n1 := g.Root.EnsureChild([]string{"n1"})
n1.Box = geo.NewBox(nil, 100, 100) n1.Box = geo.NewBox(nil, 100, 100)
@ -413,7 +413,7 @@ func TestSelfEdges(t *testing.T) {
} }
func TestSequenceToDescendant(t *testing.T) { func TestSequenceToDescendant(t *testing.T) {
g := d2graph.NewGraph(nil) g := d2graph.NewGraph()
g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
a := g.Root.EnsureChild([]string{"a"}) a := g.Root.EnsureChild([]string{"a"})
a.Box = geo.NewBox(nil, 100, 100) a.Box = geo.NewBox(nil, 100, 100)