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) {
gs := &dslGenState{
rand: mathrand.New(mathrand.NewSource(time.Now().UnixNano())),
g: d2graph.NewGraph(&d2ast.Map{}),
g: d2graph.NewGraph(),
nodeShapes: make(map[string]string),
nodeContainer: make(map[string]string),
}
gs.g.AST = &d2ast.Map{}
err = gs.gen(maxi)
if err != nil {
return "", err

View file

@ -25,7 +25,8 @@ const INNER_LABEL_PADDING int = 5
const DEFAULT_SHAPE_PADDING = 100.
type Graph struct {
AST *d2ast.Map `json:"ast"`
Name string `json:"name"`
AST *d2ast.Map `json:"ast"`
Root *Object `json:"root"`
Edges []*Edge `json:"edges"`
@ -36,10 +37,8 @@ type Graph struct {
Steps []*Graph `json:"steps,omitempty"`
}
func NewGraph(ast *d2ast.Map) *Graph {
d := &Graph{
AST: ast,
}
func NewGraph() *Graph {
d := &Graph{}
d.Root = &Object{
Graph: d,
Parent: nil,
@ -552,6 +551,7 @@ func (obj *Object) HasEdge(mk *d2ast.Key) (*Edge, bool) {
return nil, false
}
// TODO: remove once not used anywhere
func ResolveUnderscoreKey(ida []string, obj *Object) (resolvedObj *Object, resolvedIDA []string, _ error) {
if len(ida) > 0 && !obj.IsSequenceDiagram() {
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
// intermediate nodes.
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]]
if len(ids) == 1 && !is {
_, 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")
}
edge := &Edge{
e := &Edge{
Attributes: &Attributes{
Label: Scalar{
Value: label,
@ -965,10 +971,40 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label
Dst: dst,
DstArrow: dstArrow,
}
edge.initIndex()
e.initIndex()
obj.Graph.Edges = append(obj.Graph.Edges, edge)
return edge, nil
if src.Attributes.Shape.Value == d2target.ShapeSQLTable {
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

View file

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

View file

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

View file

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