d2compiler: Integrate d2ir (wip)
This commit is contained in:
parent
afa26752e6
commit
3e7bdc5468
5 changed files with 170 additions and 51 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
152
d2ir/d2ir.go
152
d2ir/d2ir.go
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue