d2/d2ir/d2ir.go

1062 lines
22 KiB
Go
Raw Normal View History

2023-01-18 10:06:44 +00:00
// Package d2ir implements a tree data structure to keep track of the resolved value of D2
// keys.
package d2ir
import (
2023-01-16 11:52:37 +00:00
"errors"
2023-01-11 19:05:50 +00:00
"fmt"
"strings"
2023-01-17 12:44:14 +00:00
"oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2ast"
2023-01-11 23:50:49 +00:00
"oss.terrastruct.com/d2/d2format"
2023-01-28 01:19:12 +00:00
"oss.terrastruct.com/d2/d2graph"
2023-01-18 10:06:44 +00:00
"oss.terrastruct.com/d2/d2parser"
)
2023-01-18 10:06:44 +00:00
// Most errors returned by a node should be created with d2parser.Errorf
// to indicate the offending AST node.
type Node interface {
node()
2023-01-22 08:21:21 +00:00
Copy(newParent Node) Node
2023-01-17 12:44:14 +00:00
Parent() Node
2023-01-18 15:15:16 +00:00
Primary() *Scalar
Map() *Map
2023-01-17 12:44:14 +00:00
2023-01-22 08:21:21 +00:00
ast() d2ast.Node
fmt.Stringer
2023-01-22 08:21:21 +00:00
LastRef() Reference
LastPrimaryKey() *d2ast.Key
}
var _ Node = &Scalar{}
var _ Node = &Field{}
var _ Node = &Edge{}
var _ Node = &Array{}
var _ Node = &Map{}
type Value interface {
Node
value()
}
var _ Value = &Scalar{}
var _ Value = &Array{}
var _ Value = &Map{}
type Composite interface {
Node
Value
composite()
}
var _ Composite = &Array{}
var _ Composite = &Map{}
func (n *Scalar) node() {}
func (n *Field) node() {}
func (n *Edge) node() {}
func (n *Array) node() {}
func (n *Map) node() {}
2023-01-17 12:44:14 +00:00
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 }
2023-01-18 15:15:16 +00:00
func (n *Scalar) Primary() *Scalar { return n }
func (n *Field) Primary() *Scalar { return n.Primary_ }
func (n *Edge) Primary() *Scalar { return n.Primary_ }
func (n *Array) Primary() *Scalar { return nil }
func (n *Map) Primary() *Scalar { return nil }
func (n *Scalar) Map() *Map { return nil }
func (n *Field) Map() *Map {
2023-01-22 08:21:21 +00:00
if n == nil {
return nil
}
2023-01-18 15:15:16 +00:00
if n.Composite == nil {
return nil
}
return n.Composite.Map()
}
2023-01-22 08:21:21 +00:00
func (n *Edge) Map() *Map {
if n == nil {
return nil
}
return n.Map_
}
2023-01-18 15:15:16 +00:00
func (n *Array) Map() *Map { return nil }
func (n *Map) Map() *Map { return n }
func (n *Scalar) value() {}
func (n *Array) value() {}
func (n *Map) value() {}
func (n *Array) composite() {}
func (n *Map) composite() {}
2023-01-22 08:21:21 +00:00
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()) }
2023-01-22 08:59:02 +00:00
func (n *Scalar) LastRef() Reference { return parentRef(n) }
2023-01-22 09:43:25 +00:00
func (n *Map) LastRef() Reference { return parentRef(n) }
func (n *Array) LastRef() Reference { return parentRef(n) }
2023-01-22 08:59:02 +00:00
func (n *Scalar) LastPrimaryKey() *d2ast.Key { return parentPrimaryKey(n) }
2023-01-22 09:43:25 +00:00
func (n *Map) LastPrimaryKey() *d2ast.Key { return parentPrimaryKey(n) }
func (n *Array) LastPrimaryKey() *d2ast.Key { return parentPrimaryKey(n) }
2023-01-22 08:59:02 +00:00
type Reference interface {
reference()
// Most specific AST node for the reference.
AST() d2ast.Node
Primary() bool
2023-01-22 08:59:02 +00:00
}
var _ Reference = &FieldReference{}
var _ Reference = &EdgeReference{}
func (r *FieldReference) reference() {}
func (r *EdgeReference) reference() {}
type Scalar struct {
2023-01-17 12:44:14 +00:00
parent Node
Value d2ast.Scalar `json:"value"`
}
2023-01-22 08:21:21 +00:00
func (s *Scalar) Copy(newParent Node) Node {
tmp := *s
s = &tmp
2023-01-22 08:21:21 +00:00
s.parent = newParent
return s
}
func (s *Scalar) Equal(s2 *Scalar) bool {
2023-01-11 23:50:49 +00:00
if _, ok := s.Value.(d2ast.String); ok {
if _, ok = s2.Value.(d2ast.String); ok {
return s.Value.ScalarString() == s2.Value.ScalarString()
}
}
return s.Value.Type() == s2.Value.Type() && s.Value.ScalarString() == s2.Value.ScalarString()
}
type Map struct {
2023-01-17 12:44:14 +00:00
parent Node
Fields []*Field `json:"fields"`
Edges []*Edge `json:"edges"`
}
2023-01-24 06:45:21 +00:00
func (m *Map) initRoot() {
m.parent = &Field{
Name: "",
References: []*FieldReference{{
Context: &RefContext{
ScopeMap: m,
},
}},
}
}
2023-01-22 08:21:21 +00:00
func (m *Map) Copy(newParent Node) Node {
tmp := *m
m = &tmp
2023-01-22 08:21:21 +00:00
m.parent = newParent
2023-01-18 15:15:16 +00:00
pfields := m.Fields
m.Fields = make([]*Field, 0, len(pfields))
for _, f := range pfields {
m.Fields = append(m.Fields, f.Copy(m).(*Field))
}
m.Edges = append([]*Edge(nil), m.Edges...)
for i := range m.Edges {
m.Edges[i] = m.Edges[i].Copy(m).(*Edge)
}
2023-02-02 18:30:54 +00:00
if m.parent == nil {
m.initRoot()
}
return m
}
// CopyBase copies the map m without layers/scenarios/steps.
2023-01-22 08:21:21 +00:00
func (m *Map) CopyBase(newParent Node) *Map {
layers := m.DeleteField("layers")
scenarios := m.DeleteField("scenarios")
steps := m.DeleteField("steps")
2023-01-22 08:21:21 +00:00
m2 := m.Copy(newParent).(*Map)
if layers != nil {
m.Fields = append(m.Fields, layers)
}
if scenarios != nil {
m.Fields = append(m.Fields, scenarios)
}
if steps != nil {
m.Fields = append(m.Fields, steps)
}
return m2
}
// Root reports whether the Map is the root of the D2 tree.
func (m *Map) Root() bool {
2023-01-22 08:59:02 +00:00
// m.parent exists even on the root map as we store the root AST in
// m.parent.References[0].Context.Map for reporting error messages about the whole IR.
// Or if otherwise needed.
f, ok := m.parent.(*Field)
if !ok {
return false
2023-01-22 08:21:21 +00:00
}
return f.Root()
}
func (f *Field) Root() bool {
return f.parent == nil
2023-01-22 08:21:21 +00:00
}
2023-01-28 01:19:12 +00:00
type BoardKind string
2023-01-18 12:42:34 +00:00
const (
2023-01-28 01:19:12 +00:00
BoardLayer BoardKind = "layer"
BoardScenario BoardKind = "scenario"
BoardStep BoardKind = "step"
2023-01-18 12:42:34 +00:00
)
2023-01-28 01:19:12 +00:00
// NodeBoardKind reports whether n represents the root of a board.
2023-01-18 12:42:34 +00:00
// n should be *Field or *Map
2023-01-28 01:19:12 +00:00
func NodeBoardKind(n Node) BoardKind {
2023-01-22 08:59:02 +00:00
var f *Field
2023-01-18 12:32:12 +00:00
switch n := n.(type) {
case *Field:
2023-02-10 03:02:21 +00:00
if n.parent == nil {
2023-01-28 01:19:12 +00:00
return BoardLayer
2023-01-24 06:45:21 +00:00
}
f = ParentField(n)
2023-01-18 12:32:12 +00:00
case *Map:
2023-01-22 08:59:02 +00:00
f = ParentField(n)
if f.Root() {
2023-01-28 01:19:12 +00:00
return BoardLayer
2023-01-24 06:45:21 +00:00
}
f = ParentField(f)
2023-01-22 08:59:02 +00:00
}
if f == nil {
return ""
}
switch f.Name {
case "layers":
2023-01-28 01:19:12 +00:00
return BoardLayer
2023-01-22 08:59:02 +00:00
case "scenarios":
2023-01-28 01:19:12 +00:00
return BoardScenario
2023-01-22 08:59:02 +00:00
case "steps":
2023-01-28 01:19:12 +00:00
return BoardStep
2023-01-24 06:45:21 +00:00
default:
return ""
2023-01-18 11:45:34 +00:00
}
}
type Field struct {
2023-01-22 09:08:13 +00:00
// *Map.
parent Node
Name string `json:"name"`
2023-01-27 20:37:08 +00:00
// Primary_ to avoid clashing with Primary(). We need to keep it exported for
// encoding/json to marshal it so cannot prefix _ instead.
2023-01-18 15:15:16 +00:00
Primary_ *Scalar `json:"primary,omitempty"`
2023-01-11 19:05:50 +00:00
Composite Composite `json:"composite,omitempty"`
2023-01-22 08:59:02 +00:00
References []*FieldReference `json:"references,omitempty"`
}
2023-01-22 08:21:21 +00:00
func (f *Field) Copy(newParent Node) Node {
tmp := *f
f = &tmp
2023-01-22 09:08:13 +00:00
f.parent = newParent
2023-01-22 08:59:02 +00:00
f.References = append([]*FieldReference(nil), f.References...)
2023-01-18 15:15:16 +00:00
if f.Primary_ != nil {
f.Primary_ = f.Primary_.Copy(f).(*Scalar)
}
if f.Composite != nil {
f.Composite = f.Composite.Copy(f).(Composite)
}
return f
}
2023-01-22 08:59:02 +00:00
func (f *Field) lastPrimaryRef() *FieldReference {
2023-01-22 08:21:21 +00:00
for i := len(f.References) - 1; i >= 0; i-- {
if f.References[i].Primary() {
2023-01-24 05:48:43 +00:00
return f.References[i]
2023-01-22 08:21:21 +00:00
}
}
return nil
}
func (f *Field) LastPrimaryKey() *d2ast.Key {
2023-01-22 08:59:02 +00:00
fr := f.lastPrimaryRef()
2023-01-22 08:21:21 +00:00
if fr == nil {
return nil
}
return fr.Context.Key
}
2023-01-22 08:59:02 +00:00
func (f *Field) LastRef() Reference {
2023-01-22 08:21:21 +00:00
return f.References[len(f.References)-1]
}
type EdgeID struct {
SrcPath []string `json:"src_path"`
SrcArrow bool `json:"src_arrow"`
DstPath []string `json:"dst_path"`
DstArrow bool `json:"dst_arrow"`
2023-01-16 11:52:37 +00:00
// If nil, then any EdgeID with equal src/dst/arrows matches.
Index *int `json:"index"`
}
func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) {
for _, ke := range k.Edges {
2023-01-18 15:15:16 +00:00
eid := &EdgeID{
SrcPath: ke.Src.IDA(),
SrcArrow: ke.SrcArrow == "<",
2023-01-18 15:15:16 +00:00
DstPath: ke.Dst.IDA(),
DstArrow: ke.DstArrow == ">",
2023-01-18 15:15:16 +00:00
}
if k.EdgeIndex != nil {
eid.Index = k.EdgeIndex.Int
}
eida = append(eida, eid)
}
return eida
}
func (eid *EdgeID) Copy() *EdgeID {
tmp := *eid
eid = &tmp
eid.SrcPath = append([]string(nil), eid.SrcPath...)
eid.DstPath = append([]string(nil), eid.DstPath...)
return eid
}
2023-01-16 11:52:37 +00:00
func (eid *EdgeID) Match(eid2 *EdgeID) bool {
if eid.Index != nil && eid2.Index != nil {
if *eid.Index != *eid2.Index {
return false
}
}
if len(eid.SrcPath) != len(eid2.SrcPath) {
return false
}
if eid.SrcArrow != eid2.SrcArrow {
return false
}
for i, s := range eid.SrcPath {
if !strings.EqualFold(s, eid2.SrcPath[i]) {
return false
}
}
if len(eid.DstPath) != len(eid2.DstPath) {
return false
}
if eid.DstArrow != eid2.DstArrow {
return false
}
for i, s := range eid.DstPath {
if !strings.EqualFold(s, eid2.DstPath[i]) {
return false
}
}
return true
}
2023-01-17 12:44:14 +00:00
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 {
2023-01-18 01:39:31 +00:00
mf := ParentField(m)
2023-01-17 12:44:14 +00:00
eid.SrcPath = append([]string{mf.Name}, eid.SrcPath...)
}
if eid.DstPath[0] == "_" {
eid.DstPath = eid.DstPath[1:]
} else {
2023-01-18 01:39:31 +00:00
mf := ParentField(m)
2023-01-17 12:44:14 +00:00
eid.DstPath = append([]string{mf.Name}, eid.DstPath...)
}
2023-01-18 01:39:31 +00:00
m = ParentMap(m)
2023-01-17 12:44:14 +00:00
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 {
if !strings.EqualFold(eid.SrcPath[0], eid.DstPath[0]) {
return common, eid
}
common = append(common, eid.SrcPath[0])
eid.SrcPath = eid.SrcPath[1:]
eid.DstPath = eid.DstPath[1:]
}
return common, eid
}
type Edge struct {
2023-01-22 09:08:13 +00:00
// *Map
parent Node
ID *EdgeID `json:"edge_id"`
2023-01-18 15:15:16 +00:00
Primary_ *Scalar `json:"primary,omitempty"`
Map_ *Map `json:"map,omitempty"`
2023-01-22 08:59:02 +00:00
References []*EdgeReference `json:"references,omitempty"`
}
2023-01-22 08:21:21 +00:00
func (e *Edge) Copy(newParent Node) Node {
tmp := *e
e = &tmp
2023-01-22 09:08:13 +00:00
e.parent = newParent
2023-01-22 08:59:02 +00:00
e.References = append([]*EdgeReference(nil), e.References...)
2023-01-18 15:15:16 +00:00
if e.Primary_ != nil {
e.Primary_ = e.Primary_.Copy(e).(*Scalar)
}
2023-01-18 15:15:16 +00:00
if e.Map_ != nil {
e.Map_ = e.Map_.Copy(e).(*Map)
}
return e
}
2023-01-22 08:59:02 +00:00
func (e *Edge) lastPrimaryRef() *EdgeReference {
2023-01-22 08:21:21 +00:00
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 {
2023-01-22 08:59:02 +00:00
er := e.lastPrimaryRef()
if er == nil {
2023-01-22 08:21:21 +00:00
return nil
}
2023-01-22 08:59:02 +00:00
return er.Context.Key
2023-01-22 08:21:21 +00:00
}
2023-01-22 08:59:02 +00:00
func (e *Edge) LastRef() Reference {
2023-01-22 08:21:21 +00:00
return e.References[len(e.References)-1]
}
type Array struct {
2023-01-17 12:44:14 +00:00
parent Node
Values []Value `json:"values"`
}
2023-01-22 08:21:21 +00:00
func (a *Array) Copy(newParent Node) Node {
tmp := *a
a = &tmp
2023-01-22 08:21:21 +00:00
a.parent = newParent
a.Values = append([]Value(nil), a.Values...)
for i := range a.Values {
a.Values[i] = a.Values[i].Copy(a).(Value)
}
return a
}
2023-01-17 12:44:14 +00:00
type FieldReference struct {
2023-01-18 12:32:12 +00:00
String d2ast.String `json:"string"`
KeyPath *d2ast.KeyPath `json:"key_path"`
2023-01-18 05:30:28 +00:00
Context *RefContext `json:"context"`
2023-01-16 15:45:13 +00:00
}
// Primary returns true if the Value in Context.Key.Value corresponds to the Field
2023-01-24 05:48:43 +00:00
// represented by String.
func (fr *FieldReference) Primary() bool {
2023-01-24 05:48:43 +00:00
if fr.KeyPath == fr.Context.Key.Key {
return len(fr.Context.Key.Edges) == 0 && fr.KeyPathIndex() == len(fr.KeyPath.Path)-1
2023-01-24 05:48:43 +00:00
} else if fr.KeyPath == fr.Context.Key.EdgeKey {
return len(fr.Context.Key.Edges) == 1 && fr.KeyPathIndex() == len(fr.KeyPath.Path)-1
2023-01-24 05:48:43 +00:00
}
return false
}
2023-01-22 08:59:02 +00:00
func (fr *FieldReference) KeyPathIndex() int {
for i, sb := range fr.KeyPath.Path {
if sb.Unbox() == fr.String {
2023-01-16 15:45:13 +00:00
return i
}
}
panic("d2ir.KeyReference.KeyPathIndex: String not in KeyPath?")
}
2023-01-22 08:59:02 +00:00
func (fr *FieldReference) EdgeDest() bool {
return fr.KeyPath == fr.Context.Edge.Dst
2023-01-16 15:45:13 +00:00
}
2023-01-22 08:59:02 +00:00
func (fr *FieldReference) InEdge() bool {
return fr.Context.Edge != nil
}
func (fr *FieldReference) AST() d2ast.Node {
if fr.String == nil {
// Root map.
return fr.Context.Scope
}
return fr.String
}
type EdgeReference struct {
2023-01-18 05:30:28 +00:00
Context *RefContext `json:"context"`
}
2023-01-22 08:59:02 +00:00
func (er *EdgeReference) AST() d2ast.Node {
return er.Context.Edge
}
// Primary returns true if the Value in Context.Key.Value corresponds to the *Edge
// represented by Context.Edge
func (er *EdgeReference) Primary() bool {
return len(er.Context.Key.Edges) == 1 && er.Context.Key.EdgeKey == nil
}
type RefContext struct {
2023-01-24 05:48:43 +00:00
Edge *d2ast.Edge `json:"edge"`
Key *d2ast.Key `json:"key"`
Scope *d2ast.Map `json:"-"`
ScopeMap *Map `json:"-"`
2023-01-18 01:39:31 +00:00
}
2023-01-16 15:45:13 +00:00
2023-01-18 05:28:33 +00:00
func (rc *RefContext) Copy() *RefContext {
tmp := *rc
return &tmp
}
2023-01-18 01:39:31 +00:00
func (rc *RefContext) EdgeIndex() int {
2023-01-17 11:05:56 +00:00
for i, e := range rc.Key.Edges {
if e == rc.Edge {
2023-01-16 15:45:13 +00:00
return i
}
}
2023-01-22 09:43:25 +00:00
return -1
}
2023-01-16 11:52:37 +00:00
func (m *Map) FieldCountRecursive() int {
if m == nil {
return 0
}
acc := len(m.Fields)
for _, f := range m.Fields {
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
acc += f.Map().FieldCountRecursive()
2023-01-16 11:52:37 +00:00
}
}
for _, e := range m.Edges {
2023-01-18 15:15:16 +00:00
if e.Map_ != nil {
acc += e.Map_.FieldCountRecursive()
}
}
return acc
}
2023-01-16 11:52:37 +00:00
func (m *Map) EdgeCountRecursive() int {
if m == nil {
return 0
}
acc := len(m.Edges)
2023-01-17 12:44:14 +00:00
for _, f := range m.Fields {
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
acc += f.Map().EdgeCountRecursive()
2023-01-17 12:44:14 +00:00
}
}
for _, e := range m.Edges {
2023-01-18 15:15:16 +00:00
if e.Map_ != nil {
acc += e.Map_.EdgeCountRecursive()
}
}
return acc
}
2023-01-18 10:06:44 +00:00
func (m *Map) GetField(ida ...string) *Field {
2023-01-17 12:44:14 +00:00
for len(ida) > 0 && ida[0] == "_" {
2023-01-18 01:39:31 +00:00
m = ParentMap(m)
2023-01-17 12:44:14 +00:00
if m == nil {
return nil
}
}
return m.getField(ida)
}
func (m *Map) getField(ida []string) *Field {
if len(ida) == 0 {
2023-01-16 11:52:37 +00:00
return nil
}
s := ida[0]
rest := ida[1:]
2023-01-17 12:44:14 +00:00
if s == "_" {
return nil
}
for _, f := range m.Fields {
if !strings.EqualFold(f.Name, s) {
continue
}
if len(rest) == 0 {
2023-01-16 11:52:37 +00:00
return f
}
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
return f.Map().getField(rest)
}
}
2023-01-16 11:52:37 +00:00
return nil
}
2023-01-18 10:06:44 +00:00
func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext) (*Field, error) {
i := 0
for kp.Path[i].Unbox().ScalarString() == "_" {
2023-01-18 01:39:31 +00:00
m = ParentMap(m)
2023-01-17 12:44:14 +00:00
if m == nil {
2023-01-18 10:06:44 +00:00
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "invalid underscore: no parent")
}
if i+1 == len(kp.Path) {
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "field key must contain more than underscores")
2023-01-17 12:44:14 +00:00
}
2023-01-18 10:06:44 +00:00
i++
2023-01-17 12:44:14 +00:00
}
2023-01-18 10:06:44 +00:00
return m.ensureField(i, kp, refctx)
2023-01-17 12:44:14 +00:00
}
2023-01-18 10:06:44 +00:00
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field, error) {
head := kp.Path[i].Unbox().ScalarString()
2023-01-18 10:06:44 +00:00
if head == "_" {
2023-01-18 11:45:34 +00:00
return nil, d2parser.Errorf(kp.Path[i].Unbox(), `parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
2023-01-18 10:06:44 +00:00
}
2023-01-28 01:19:12 +00:00
if findBoardKeyword(head) != -1 && NodeBoardKind(m) == "" {
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", head)
2023-01-17 12:44:14 +00:00
}
for _, f := range m.Fields {
2023-01-18 10:06:44 +00:00
if !strings.EqualFold(f.Name, head) {
continue
}
2023-01-18 10:06:44 +00:00
// Don't add references for fake common KeyPath from trimCommon in CreateEdge.
if refctx != nil {
f.References = append(f.References, &FieldReference{
String: kp.Path[i].Unbox(),
KeyPath: kp,
Context: refctx,
})
}
2023-01-18 10:06:44 +00:00
if i+1 == len(kp.Path) {
2023-01-16 11:52:37 +00:00
return f, nil
}
2023-01-18 01:47:47 +00:00
if _, ok := f.Composite.(*Array); ok {
2023-01-18 11:45:34 +00:00
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "cannot index into array")
}
2023-01-18 15:15:16 +00:00
if f.Map() == nil {
f.Composite = &Map{
2023-01-18 01:47:47 +00:00
parent: f,
}
}
2023-01-18 15:15:16 +00:00
return f.Map().ensureField(i+1, kp, refctx)
}
f := &Field{
parent: m,
2023-01-18 10:06:44 +00:00
Name: head,
}
// Don't add references for fake common KeyPath from trimCommon in CreateEdge.
if refctx != nil {
f.References = append(f.References, &FieldReference{
2023-01-18 10:06:44 +00:00
String: kp.Path[i].Unbox(),
KeyPath: kp,
Context: refctx,
})
}
m.Fields = append(m.Fields, f)
2023-01-18 10:06:44 +00:00
if i+1 == len(kp.Path) {
2023-01-16 11:52:37 +00:00
return f, nil
}
2023-01-18 15:15:16 +00:00
f.Composite = &Map{
parent: f,
}
2023-01-18 15:15:16 +00:00
return f.Map().ensureField(i+1, kp, refctx)
}
func (m *Map) DeleteField(ida ...string) *Field {
if len(ida) == 0 {
return nil
}
s := ida[0]
rest := ida[1:]
for i, f := range m.Fields {
if !strings.EqualFold(f.Name, s) {
continue
}
if len(rest) == 0 {
m.Fields = append(m.Fields[:i], m.Fields[i+1:]...)
return f
}
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
return f.Map().DeleteField(rest...)
}
}
return nil
}
2023-01-16 11:52:37 +00:00
func (m *Map) GetEdges(eid *EdgeID) []*Edge {
2023-01-17 12:44:14 +00:00
eid, m, err := eid.resolveUnderscores(m)
if err != nil {
return nil
}
common, eid := eid.trimCommon()
if len(common) > 0 {
2023-01-18 10:06:44 +00:00
f := m.GetField(common...)
2023-01-16 11:52:37 +00:00
if f == nil {
return nil
}
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
return f.Map().GetEdges(eid)
}
2023-01-16 11:52:37 +00:00
return nil
}
2023-01-16 11:52:37 +00:00
var ea []*Edge
for _, e := range m.Edges {
2023-01-16 11:52:37 +00:00
if e.ID.Match(eid) {
ea = append(ea, e)
}
}
return ea
}
2023-01-18 10:06:44 +00:00
func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) (*Edge, error) {
2023-01-18 15:15:16 +00:00
if ParentEdge(m) != nil {
return nil, d2parser.Errorf(refctx.Edge, "cannot create edge inside edge")
}
2023-01-17 12:44:14 +00:00
eid, m, err := eid.resolveUnderscores(m)
if err != nil {
2023-01-18 10:06:44 +00:00
return nil, d2parser.Errorf(refctx.Edge, err.Error())
2023-01-17 12:44:14 +00:00
}
2023-01-16 11:52:37 +00:00
common, eid := eid.trimCommon()
if len(common) > 0 {
2023-01-18 10:06:44 +00:00
tmp := *refctx.Edge.Src
kp := &tmp
kp.Path = kp.Path[:len(common)]
f, err := m.EnsureField(kp, nil)
2023-01-16 11:52:37 +00:00
if err != nil {
return nil, err
}
2023-01-18 01:47:47 +00:00
if _, ok := f.Composite.(*Array); ok {
2023-01-18 11:45:34 +00:00
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot index into array")
2023-01-16 11:52:37 +00:00
}
2023-01-18 15:15:16 +00:00
if f.Map() == nil {
f.Composite = &Map{
2023-01-18 01:47:47 +00:00
parent: f,
}
2023-01-16 11:52:37 +00:00
}
2023-01-18 15:15:16 +00:00
return f.Map().CreateEdge(eid, refctx)
}
2023-01-16 11:52:37 +00:00
2023-01-28 01:19:12 +00:00
ij := findProhibitedEdgeKeyword(eid.SrcPath...)
2023-01-18 12:32:12 +00:00
if ij != -1 {
2023-01-28 01:19:12 +00:00
return nil, d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
}
ij = findBoardKeyword(eid.SrcPath...)
if ij == len(eid.SrcPath)-1 {
return nil, d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
2023-01-18 12:32:12 +00:00
}
src := m.GetField(eid.SrcPath...)
2023-01-28 01:19:12 +00:00
if NodeBoardKind(src) != "" {
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot create edges between boards")
2023-01-18 12:32:12 +00:00
}
2023-01-28 01:19:12 +00:00
ij = findProhibitedEdgeKeyword(eid.DstPath...)
2023-01-18 12:32:12 +00:00
if ij != -1 {
2023-01-28 01:19:12 +00:00
return nil, d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
}
ij = findBoardKeyword(eid.DstPath...)
if ij == len(eid.DstPath)-1 {
return nil, d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
2023-01-18 12:32:12 +00:00
}
dst := m.GetField(eid.DstPath...)
2023-01-28 01:19:12 +00:00
if NodeBoardKind(dst) != "" {
return nil, d2parser.Errorf(refctx.Edge.Dst, "cannot create edges between boards")
2023-01-18 12:32:12 +00:00
}
2023-01-28 01:19:12 +00:00
if ParentBoard(src) != ParentBoard(dst) {
return nil, d2parser.Errorf(refctx.Edge, "cannot create edges between boards")
2023-01-18 12:32:12 +00:00
}
2023-01-16 11:52:37 +00:00
eid.Index = nil
ea := m.GetEdges(eid)
index := len(ea)
eid.Index = &index
e := &Edge{
parent: m,
ID: eid,
2023-01-22 08:59:02 +00:00
References: []*EdgeReference{{
2023-01-18 10:06:44 +00:00
Context: refctx,
}},
2023-01-16 11:52:37 +00:00
}
m.Edges = append(m.Edges, e)
return e, nil
}
2023-01-11 19:05:50 +00:00
2023-01-22 08:21:21 +00:00
func (s *Scalar) ast() d2ast.Node {
return s.Value
}
2023-01-22 08:21:21 +00:00
func (f *Field) ast() d2ast.Node {
k := &d2ast.Key{
Key: &d2ast.KeyPath{
Path: []*d2ast.StringBox{
d2ast.MakeValueBox(d2ast.RawString(f.Name, true)).StringBox(),
},
},
2023-01-11 19:05:50 +00:00
}
2023-01-18 15:15:16 +00:00
if f.Primary_ != nil {
2023-01-22 08:21:21 +00:00
k.Primary = d2ast.MakeValueBox(f.Primary_.ast().(d2ast.Value)).ScalarBox()
}
if f.Composite != nil {
2023-01-22 08:21:21 +00:00
k.Value = d2ast.MakeValueBox(f.Composite.ast().(d2ast.Value))
}
return k
2023-01-11 19:05:50 +00:00
}
2023-01-16 11:52:37 +00:00
2023-01-22 08:21:21 +00:00
func (e *Edge) ast() d2ast.Node {
astEdge := &d2ast.Edge{}
astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath)
if e.ID.SrcArrow {
astEdge.SrcArrow = "<"
2023-01-16 11:52:37 +00:00
}
astEdge.Dst = d2ast.MakeKeyPath(e.ID.DstPath)
if e.ID.DstArrow {
astEdge.DstArrow = ">"
2023-01-16 11:52:37 +00:00
}
k := &d2ast.Key{
Edges: []*d2ast.Edge{astEdge},
}
2023-01-18 15:15:16 +00:00
if e.Primary_ != nil {
2023-01-22 08:21:21 +00:00
k.Primary = d2ast.MakeValueBox(e.Primary_.ast().(d2ast.Value)).ScalarBox()
}
2023-01-18 15:15:16 +00:00
if e.Map_ != nil {
2023-01-22 08:21:21 +00:00
k.Value = d2ast.MakeValueBox(e.Map_.ast().(*d2ast.Map))
}
return k
}
2023-01-22 08:21:21 +00:00
func (a *Array) ast() d2ast.Node {
if a == nil {
return nil
}
astArray := &d2ast.Array{}
for _, av := range a.Values {
2023-01-22 08:21:21 +00:00
astArray.Nodes = append(astArray.Nodes, d2ast.MakeArrayNodeBox(av.ast().(d2ast.ArrayNode)))
}
return astArray
}
2023-01-22 08:21:21 +00:00
func (m *Map) ast() d2ast.Node {
if m == nil {
return nil
}
astMap := &d2ast.Map{}
2023-01-18 01:39:31 +00:00
if m.Root() {
2023-01-17 12:44:14 +00:00
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 {
2023-01-22 08:21:21 +00:00
astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.ast().(d2ast.MapNode)))
}
for _, e := range m.Edges {
2023-01-22 08:21:21 +00:00
astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(e.ast().(d2ast.MapNode)))
}
return astMap
2023-01-16 11:52:37 +00:00
}
2023-01-16 15:45:13 +00:00
2023-01-17 12:44:14 +00:00
func (m *Map) appendFieldReferences(i int, kp *d2ast.KeyPath, refctx *RefContext) {
2023-01-16 15:45:13 +00:00
sb := kp.Path[i]
2023-01-18 10:06:44 +00:00
f := m.GetField(sb.Unbox().ScalarString())
2023-01-16 15:45:13 +00:00
if f == nil {
return
}
2023-01-22 08:59:02 +00:00
f.References = append(f.References, &FieldReference{
2023-01-18 10:06:44 +00:00
String: sb.Unbox(),
2023-01-16 15:45:13 +00:00
KeyPath: kp,
Context: refctx,
})
2023-01-17 11:05:56 +00:00
if i+1 == len(kp.Path) {
return
}
2023-01-18 15:15:16 +00:00
if f.Map() != nil {
f.Map().appendFieldReferences(i+1, kp, refctx)
2023-01-18 01:39:31 +00:00
}
}
func ParentMap(n Node) *Map {
2023-01-22 09:08:13 +00:00
for {
2023-01-17 12:44:14 +00:00
n = n.Parent()
2023-01-22 09:08:13 +00:00
if n == nil {
return nil
}
2023-01-18 15:15:16 +00:00
if m, ok := n.(*Map); ok {
return m
2023-01-17 12:44:14 +00:00
}
}
}
2023-01-18 01:39:31 +00:00
func ParentField(n Node) *Field {
2023-01-22 09:08:13 +00:00
for {
2023-01-17 12:44:14 +00:00
n = n.Parent()
2023-01-22 09:08:13 +00:00
if n == nil {
return nil
}
2023-01-18 15:15:16 +00:00
if f, ok := n.(*Field); ok {
return f
2023-01-17 12:44:14 +00:00
}
}
}
2023-01-28 01:19:12 +00:00
func ParentBoard(n Node) Node {
2023-01-18 11:51:16 +00:00
for {
2023-01-22 09:43:25 +00:00
n = n.Parent()
if n == nil {
2023-01-18 11:51:16 +00:00
return nil
2023-01-18 11:45:34 +00:00
}
2023-01-28 01:19:12 +00:00
if NodeBoardKind(n) != "" {
2023-01-22 09:43:25 +00:00
return n
2023-01-18 11:51:16 +00:00
}
2023-01-18 11:45:34 +00:00
}
}
2023-01-18 15:15:16 +00:00
func ParentEdge(n Node) *Edge {
2023-01-22 09:08:13 +00:00
for {
2023-01-18 15:15:16 +00:00
n = n.Parent()
2023-01-22 09:08:13 +00:00
if n == nil {
return nil
}
2023-01-18 15:15:16 +00:00
if e, ok := n.(*Edge); ok {
return e
}
}
}
2023-01-17 12:44:14 +00:00
func countUnderscores(p []string) int {
2023-01-28 01:19:12 +00:00
for i, el := range p {
2023-01-17 12:44:14 +00:00
if el != "_" {
2023-01-28 01:19:12 +00:00
return i
}
}
return 0
}
func findBoardKeyword(ida ...string) int {
for i := range ida {
if _, ok := d2graph.BoardKeywords[ida[i]]; ok {
return i
2023-01-17 12:44:14 +00:00
}
}
2023-01-28 01:19:12 +00:00
return -1
2023-01-16 15:45:13 +00:00
}
2023-01-18 12:32:12 +00:00
2023-01-28 01:19:12 +00:00
func findProhibitedEdgeKeyword(ida ...string) int {
2023-01-18 12:32:12 +00:00
for i := range ida {
2023-01-28 01:19:12 +00:00
if _, ok := d2graph.SimpleReservedKeywords[ida[i]]; ok {
return i
}
if _, ok := d2graph.ReservedKeywordHolders[ida[i]]; ok {
2023-01-18 12:32:12 +00:00
return i
}
}
return -1
}
2023-01-22 08:59:02 +00:00
func parentRef(n Node) Reference {
f := ParentField(n)
if f != nil {
return f.LastRef()
}
e := ParentEdge(n)
if e != nil {
return e.LastRef()
}
return nil
}
func parentPrimaryKey(n Node) *d2ast.Key {
f := ParentField(n)
if f != nil {
return f.LastPrimaryKey()
}
e := ParentEdge(n)
if e != nil {
return e.LastPrimaryKey()
}
return nil
}
2023-01-24 05:48:43 +00:00
func IDA(n Node) (ida []string) {
for {
f, ok := n.(*Field)
if ok {
if f.Root() {
reverseIDA(ida)
2023-01-24 05:48:43 +00:00
return ida
}
ida = append(ida, f.Name)
}
f = ParentField(n)
if f == nil {
reverseIDA(ida)
2023-01-24 05:48:43 +00:00
return ida
}
n = f
}
}
func reverseIDA(ida []string) {
for i := 0; i < len(ida)/2; i++ {
tmp := ida[i]
ida[i] = ida[len(ida)-i-1]
ida[len(ida)-i-1] = tmp
}
}