diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go index d824ef367..dbbee2ff8 100644 --- a/d2ast/d2ast.go +++ b/d2ast/d2ast.go @@ -650,6 +650,14 @@ type KeyPath struct { Path []*StringBox `json:"path"` } +func MakeKeyPath(a []string) *KeyPath { + var kp *KeyPath + for _, el := range a { + kp.Path = append(kp.Path, MakeValueBox(RawString(el, true)).StringBox()) + } + return kp +} + type Edge struct { Range Range `json:"range"` @@ -731,6 +739,37 @@ type ArrayNodeBox struct { Map *Map `json:"map,omitempty"` } +func MakeArrayNodeBox(an ArrayNode) ArrayNodeBox { + var ab ArrayNodeBox + switch an := an.(type) { + case *Comment: + ab.Comment = an + case *BlockComment: + ab.BlockComment = an + case *Substitution: + ab.Substitution = an + case *Null: + ab.Null = an + case *Boolean: + ab.Boolean = an + case *Number: + ab.Number = an + case *UnquotedString: + ab.UnquotedString = an + case *DoubleQuotedString: + ab.DoubleQuotedString = an + case *SingleQuotedString: + ab.SingleQuotedString = an + case *BlockString: + ab.BlockString = an + case *Array: + ab.Array = an + case *Map: + ab.Map = an + } + return ab +} + func (ab ArrayNodeBox) Unbox() ArrayNode { switch { case ab.Comment != nil: diff --git a/d2ir/compile_test.go b/d2ir/compile_test.go index e1174d46c..0d2c07e5b 100644 --- a/d2ir/compile_test.go +++ b/d2ir/compile_test.go @@ -214,12 +214,7 @@ func testCompileField(t *testing.T) { assertField(t, m, 1, 0, nil) f := assertField(t, m, 0, 0, nil, "x") - f_a, ok := f.Composite.(*d2ir.Array) - if !ok { - t.Fatalf("unexpected type: %T", f.Composite) - } else { - assert.Equal(t, 4, len(f_a.Values)) - } + assert.String(t, `[1; 2; 3; 4]`, f.Composite.String()) }, }, } diff --git a/d2ir/d2ir.go b/d2ir/d2ir.go index 3bb9154fd..783b1be9e 100644 --- a/d2ir/d2ir.go +++ b/d2ir/d2ir.go @@ -1,7 +1,6 @@ package d2ir import ( - "encoding/json" "errors" "fmt" "strings" @@ -12,7 +11,9 @@ import ( type Node interface { node() + ast() d2ast.Node Copy(newp Parent) Node + fmt.Stringer } var _ Node = &Scalar{} @@ -68,6 +69,12 @@ 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()) } + type Scalar struct { parent Parent Value d2ast.Scalar `json:"value"` @@ -91,10 +98,6 @@ func (s *Scalar) Equal(s2 *Scalar) bool { } -func (s *Scalar) String() string { - return d2format.Format(s.Value) -} - type Map struct { parent Parent Fields []*Field `json:"fields"` @@ -160,6 +163,21 @@ type EdgeID struct { Index *int `json:"index"` } +func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) { + for _, ke := range k.Edges { + eida = append(eida, &EdgeID{ + SrcPath: d2format.KeyPath(ke.Src), + SrcArrow: ke.SrcArrow == "<", + DstPath: d2format.KeyPath(ke.Dst), + DstArrow: ke.DstArrow == ">", + }) + } + if k.EdgeIndex != nil && k.EdgeIndex.Int != nil { + eida[0].Index = k.EdgeIndex.Int + } + return eida +} + func (eid *EdgeID) Copy() *EdgeID { tmp := *eid eid = &tmp @@ -447,25 +465,79 @@ func (m *Map) EnsureEdge(eid *EdgeID) (*Edge, error) { return e, nil } -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) +func (s *Scalar) ast() d2ast.Node { + return s.Value } -func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) { - for _, ke := range k.Edges { - eida = append(eida, &EdgeID{ - SrcPath: d2format.KeyPath(ke.Src), - SrcArrow: ke.SrcArrow == "<", - DstPath: d2format.KeyPath(ke.Dst), - DstArrow: ke.DstArrow == ">", - }) +func (f *Field) ast() d2ast.Node { + k := &d2ast.Key{ + Key: &d2ast.KeyPath{ + Path: []*d2ast.StringBox{ + d2ast.MakeValueBox(d2ast.RawString(f.Name, true)).StringBox(), + }, + }, } - if k.EdgeIndex != nil && k.EdgeIndex.Int != nil { - eida[0].Index = k.EdgeIndex.Int + + if f.Primary != nil { + k.Primary = d2ast.MakeValueBox(f.Primary.ast().(d2ast.Value)).ScalarBox() } - return eida + if f.Composite != nil { + k.Value = d2ast.MakeValueBox(f.Composite.ast().(d2ast.Value)) + } + + return k +} + +func (e *Edge) ast() d2ast.Node { + astEdge := &d2ast.Edge{} + + astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath) + if e.ID.SrcArrow { + astEdge.SrcArrow = "<" + } + astEdge.Dst = d2ast.MakeKeyPath(e.ID.DstPath) + if e.ID.DstArrow { + astEdge.DstArrow = ">" + } + + k := &d2ast.Key{ + Edges: []*d2ast.Edge{astEdge}, + } + + if e.Primary != nil { + k.Primary = d2ast.MakeValueBox(e.Primary.ast().(d2ast.Value)).ScalarBox() + } + if e.Map != nil { + k.Value = d2ast.MakeValueBox(e.Map.ast().(*d2ast.Map)) + } + + return k +} + +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))) + } + return astArray +} + +func (m *Map) ast() d2ast.Node { + if m == nil { + return nil + } + astMap := &d2ast.Map{} + if m.parent != nil { + astMap.Range = d2ast.MakeRange(",1:0:0-1:0:0") + } + for _, f := range m.Fields { + 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))) + } + return astMap }