// Package d2ir implements a tree data structure to keep track of the resolved value of D2 // keys.
package d2ir import ( ) // Most errors returned by a node should be created with d2parser.Errorf // to indicate the offending AST node. type Node interface { node() Copy(newParent Node) Node Parent() Node Primary() *Scalar Map() *Map Equal(n2 Node) bool AST() d2ast.Node fmt.Stringer LastRef() Reference LastPrimaryRef() 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 ( *Scalar) () {} func ( *Field) () {} func ( *Edge) () {} func ( *Array) () {} func ( *Map) () {} func ( *Scalar) () Node { return .parent } func ( *Field) () Node { return .parent } func ( *Edge) () Node { return .parent } func ( *Array) () Node { return .parent } func ( *Map) () Node { return .parent } func ( *Scalar) () *Scalar { return } func ( *Field) () *Scalar { return .Primary_ } func ( *Edge) () *Scalar { return .Primary_ } func ( *Array) () *Scalar { return nil } func ( *Map) () *Scalar { return nil } func ( *Scalar) () *Map { return nil } func ( *Field) () *Map { if == nil { return nil } if .Composite == nil { return nil } return .Composite.Map() } func ( *Edge) () *Map { if == nil { return nil } return .Map_ } func ( *Array) () *Map { return nil } func ( *Map) () *Map { return } func ( *Scalar) () {} func ( *Array) () {} func ( *Map) () {} func ( *Array) () {} func ( *Map) () {} func ( *Scalar) () string { return d2format.Format(.AST()) } func ( *Field) () string { return d2format.Format(.AST()) } func ( *Edge) () string { return d2format.Format(.AST()) } func ( *Array) () string { return d2format.Format(.AST()) } func ( *Map) () string { return d2format.Format(.AST()) } func ( *Scalar) () Reference { return parentRef() } func ( *Map) () Reference { return parentRef() } func ( *Array) () Reference { return parentRef() } func ( *Scalar) () Reference { return parentPrimaryRef() } func ( *Map) () Reference { return parentPrimaryRef() } func ( *Array) () Reference { return parentPrimaryRef() } func ( *Scalar) () *d2ast.Key { return parentPrimaryKey() } func ( *Map) () *d2ast.Key { return parentPrimaryKey() } func ( *Array) () *d2ast.Key { return parentPrimaryKey() } type Reference interface { reference() // Most specific AST node for the reference. AST() d2ast.Node Primary() bool Context() *RefContext // Result of a glob in Context or from above. DueToGlob() bool DueToLazyGlob() bool } var _ Reference = &FieldReference{} var _ Reference = &EdgeReference{} func ( *FieldReference) () {} func ( *EdgeReference) () {} func ( *FieldReference) () *RefContext { return .Context_ } func ( *EdgeReference) () *RefContext { return .Context_ } func ( *FieldReference) () bool { return .DueToGlob_ } func ( *EdgeReference) () bool { return .DueToGlob_ } func ( *FieldReference) () bool { return .DueToLazyGlob_ } func ( *EdgeReference) () bool { return .DueToLazyGlob_ } type Scalar struct { parent Node Value d2ast.Scalar `json:"value"` } func ( *Scalar) ( Node) Node { := * = & .parent = .Value = cloneASTScalar(.Value) return } func ( *Scalar) ( Node) bool { := .(*Scalar) if , := .Value.(d2ast.String); { if _, = .Value.(d2ast.String); { return .Value.ScalarString() == .Value.ScalarString() } } return .Value.Type() == .Value.Type() && .Value.ScalarString() == .Value.ScalarString() } type Map struct { parent Node importAST d2ast.Node // variableExpansion is shared across automatic copies created during one // compilation. It is intentionally omitted from JSON output. variableExpansion *variableExpansionBudget Fields []*Field `json:"fields"` Edges []*Edge `json:"edges"` globs []*globContext // Fields and Edges retain source order. These indexes are derived lookup // accelerators only; they never determine iteration order. fieldIndex map[fieldIndexKey]*Field fieldIndexSize int edgeIndex map[string][]*Edge edgeIndexSize int // structureVersion changes whenever a field or edge is added or removed in // this map or any descendant. Lazy globs use it instead of recursively // recounting the whole IR around every compiled key. structureVersion uint64 } type fieldIndexKey struct { folded string unquotedReserved bool } func foldString( string) string { := true := false for := 0; < len(); ++ { if [] >= 0x80 { = false break } if [] >= 'a' && [] <= 'z' { = true } } if { if ! { return } := []byte() for := range { if [] >= 'a' && [] <= 'z' { [] -= 'a' - 'A' } } return string() } var strings.Builder .Grow(len()) for , := range { := for := unicode.SimpleFold(); != ; = unicode.SimpleFold() { if < { = } } .WriteRune() } return .String() } func makeFieldIndexKey( d2ast.String) fieldIndexKey { := .ScalarString() , := d2ast.ReservedKeywords[strings.ToLower()] return fieldIndexKey{ folded: foldString(), unquotedReserved: && .IsUnquoted(), } } func ( *Map) () { if .fieldIndex != nil && .fieldIndexSize == len(.Fields) { return } .fieldIndex = make(map[fieldIndexKey]*Field, len(.Fields)) for , := range .Fields { if == nil || .Name == nil { continue } := makeFieldIndexKey(.Name) if , := .fieldIndex[]; ! { .fieldIndex[] = } } .fieldIndexSize = len(.Fields) } func ( *Map) ( d2ast.String) *Field { if == nil || == nil { return nil } .ensureFieldIndex() return .fieldIndex[makeFieldIndexKey()] } func appendEdgePathKey( *strings.Builder, []d2ast.String) { .WriteString(strconv.Itoa(len())) .WriteByte(':') for , := range { := foldString(.ScalarString()) .WriteString(strconv.Itoa(len())) .WriteByte(':') .WriteString() } } func makeEdgeIndexKey( *EdgeID) string { var , strings.Builder appendEdgePathKey(&, .SrcPath) if .SrcArrow { .WriteByte('*') } else { .WriteByte('-') } appendEdgePathKey(&, .DstPath) if .DstArrow { .WriteByte('*') } else { .WriteByte('-') } , := .String(), .String() if > { , = , } var strings.Builder .WriteString(strconv.Itoa(len())) .WriteByte(':') .WriteString() .WriteString(strconv.Itoa(len())) .WriteByte(':') .WriteString() return .String() } func ( *Map) () { if .edgeIndex != nil && .edgeIndexSize == len(.Edges) { return } .edgeIndex = make(map[string][]*Edge, len(.Edges)) for , := range .Edges { if == nil || .ID == nil { continue } := makeEdgeIndexKey(.ID) .edgeIndex[] = append(.edgeIndex[], ) } .edgeIndexSize = len(.Edges) } func ( *Map) () { for := ; != nil; = ParentMap() { .structureVersion++ } } func ( *Map) ( *Field) { := len(.Fields) .Fields = append(.Fields, ) if .fieldIndex != nil && .fieldIndexSize == && != nil && .Name != nil { := makeFieldIndexKey(.Name) if , := .fieldIndex[]; ! { .fieldIndex[] = } .fieldIndexSize++ } else { .fieldIndex = nil .fieldIndexSize = 0 } .markStructureChanged() } func ( *Map) ( int, *Field) { := len(.Fields) .Fields = append(.Fields, nil) copy(.Fields[+1:], .Fields[:]) .Fields[] = if .fieldIndex != nil && .fieldIndexSize == && != nil && .Name != nil { := makeFieldIndexKey(.Name) if , := .fieldIndex[]; ! { .fieldIndex[] = } .fieldIndexSize++ } else { .fieldIndex = nil .fieldIndexSize = 0 } .markStructureChanged() } func ( *Map) ( int) *Field { := .Fields[] copy(.Fields[:], .Fields[+1:]) .Fields = .Fields[:len(.Fields)-1] .fieldIndex = nil .fieldIndexSize = 0 .markStructureChanged() return } func ( *Map) ( *Edge) { := len(.Edges) .Edges = append(.Edges, ) if .edgeIndex != nil && .edgeIndexSize == && != nil && .ID != nil { := makeEdgeIndexKey(.ID) .edgeIndex[] = append(.edgeIndex[], ) .edgeIndexSize++ } else { .edgeIndex = nil .edgeIndexSize = 0 } .markStructureChanged() } func ( *Map) ( int) *Edge { := .Edges[] copy(.Edges[:], .Edges[+1:]) .Edges = .Edges[:len(.Edges)-1] .edgeIndex = nil .edgeIndexSize = 0 .markStructureChanged() return } func ( *Map) () { .parent = &Field{ Name: d2ast.FlatUnquotedString("root"), References: []*FieldReference{{ Context_: &RefContext{ ScopeMap: , }, }}, } } func ( *Map) () d2ast.Node { return .importAST } func ( *Map) ( d2ast.Node) { .importAST = for , := range .Fields { .SetImportAST() } for , := range .Edges { .SetImportAST() } } func ( *Map) ( Node) Node { var *variableExpansionBudget if == nil { = variableExpansionBudgetFor() } := * = & .fieldIndex = nil .fieldIndexSize = 0 .edgeIndex = nil .edgeIndexSize = 0 .structureVersion = 0 .parent = := .Fields .Fields = make([]*Field, 0, len()) for , := range { .Fields = append(.Fields, .Copy().(*Field)) } .Edges = append([]*Edge(nil), .Edges...) for := range .Edges { .Edges[] = .Edges[].Copy().(*Edge) } if .parent == nil { .variableExpansion = .initRoot() } return } // CopyBase copies the map m without layers/scenarios/steps. func ( *Map) ( Node) *Map { if == nil { return (&Map{}).Copy().(*Map) } // The historical delete-and-restore implementation also canonicalized board // fields to layers, scenarios, steps at the end of the source map. Preserve // that observable ordering without reporting a structural mutation: the // field set is unchanged, and compile generations are for glob membership. := make([]*Field, 0, len(.Fields)) var , , *Field for , := range .Fields { if .Name != nil { switch { case == nil && strings.EqualFold(.Name.ScalarString(), "layers"): = continue case == nil && strings.EqualFold(.Name.ScalarString(), "scenarios"): = continue case == nil && strings.EqualFold(.Name.ScalarString(), "steps"): = continue } } = append(, ) } for , := range []*Field{, , } { if != nil { .deleteEdgesForFieldReferences() } } := * .Fields = := .Copy().(*Map) .Fields = append(.Fields[:0], ...) for , := range []*Field{, , } { if != nil { .Fields = append(.Fields, ) } } .fieldIndex = nil .fieldIndexSize = 0 return } func ( *Map) ( *Field) { for , := range .References { := for != nil { for , := range .Edges { for , := range .References { if .Context_ == .Context_ { .DeleteEdge(.ID) break } } } if NodeBoardKind() != "" { break } = ParentMap() } } } // Root reports whether the Map is the root of the D2 tree. func ( *Map) () bool { // 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. , := .parent.(*Field) if ! { return false } return .Root() } func ( *Field) () bool { return .parent == nil } type BoardKind string const ( BoardLayer BoardKind = "layer" BoardScenario BoardKind = "scenario" BoardStep BoardKind = "step" ) // NodeBoardKind reports whether n represents the root of a board. // n should be *Field or *Map func ( Node) BoardKind { var *Field switch n := .(type) { case *Field: if .parent == nil { return BoardLayer } = ParentField() case *Map: if == nil { return "" } var bool , = .parent.(*Field) if ! { return "" } if .Root() { return BoardLayer } = ParentField() } if == nil { return "" } switch .Name.ScalarString() { case "layers": return BoardLayer case "scenarios": return BoardScenario case "steps": return BoardStep default: return "" } } type Importable interface { ImportAST() d2ast.Node SetImportAST(d2ast.Node) } var _ Importable = &Edge{} var _ Importable = &Field{} var _ Importable = &Map{} type Field struct { // *Map. parent Node importAST d2ast.Node suspended bool Name d2ast.String `json:"name"` // Primary_ to avoid clashing with Primary(). We need to keep it exported for // encoding/json to marshal it so cannot prefix _ instead. Primary_ *Scalar `json:"primary,omitempty"` Composite Composite `json:"composite,omitempty"` References []*FieldReference `json:"references,omitempty"` } func ( *Field) () d2ast.Node { return .importAST } func ( *Field) ( d2ast.Node) { .importAST = if .Map() != nil { .Map().SetImportAST() } } func ( *Field) ( Node) Node { := * = & .parent = .References = append([]*FieldReference(nil), .References...) if .Primary_ != nil { .Primary_ = .Primary_.Copy().(*Scalar) } if .Composite != nil { .Composite = .Composite.Copy().(Composite) } return } func ( *Field) () Reference { for := len(.References) - 1; >= 0; -- { if .References[].Primary() { return .References[] } } return nil } func ( *Field) () *d2ast.Key { := .LastPrimaryRef() if == nil { return nil } return .(*FieldReference).Context_.Key } func ( *Field) () Reference { return .References[len(.References)-1] } type EdgeID struct { SrcPath []d2ast.String `json:"src_path"` SrcArrow bool `json:"src_arrow"` DstPath []d2ast.String `json:"dst_path"` DstArrow bool `json:"dst_arrow"` // If nil, then any EdgeID with equal src/dst/arrows matches. Index *int `json:"index"` Glob bool `json:"glob"` } func ( *d2ast.Key) ( []*EdgeID) { for , := range .Edges { := &EdgeID{ SrcPath: .Src.IDA(), SrcArrow: .SrcArrow == "<", DstPath: .Dst.IDA(), DstArrow: .DstArrow == ">", } if .EdgeIndex != nil { .Index = .EdgeIndex.Int .Glob = .EdgeIndex.Glob } = append(, ) } return } func ( *EdgeID) () *EdgeID { := * = & .SrcPath = append([]d2ast.String(nil), .SrcPath...) .DstPath = append([]d2ast.String(nil), .DstPath...) if .Index != nil { := *.Index .Index = & } return } func ( *EdgeID) ( *EdgeID) bool { if .Index != nil && .Index != nil { if *.Index != *.Index { return false } } := edgePathEqual(.SrcPath, .SrcPath) && .SrcArrow == .SrcArrow && edgePathEqual(.DstPath, .DstPath) && .DstArrow == .DstArrow if { return true } return edgePathEqual(.SrcPath, .DstPath) && .SrcArrow == .DstArrow && edgePathEqual(.DstPath, .SrcPath) && .DstArrow == .SrcArrow } func edgePathEqual(, []d2ast.String) bool { if len() != len() { return false } for , := range { if !strings.EqualFold(.ScalarString(), [].ScalarString()) { return false } } return true } // resolve resolves both underscores and commons in eid. // It returns the new eid, containing map adjusted for underscores and common ida. func ( *EdgeID) ( *Map) ( *EdgeID, *Map, []d2ast.String, error) { = .Copy() := go2.Max(countUnderscores(.SrcPath), countUnderscores(.DstPath)) for := 0; < ; ++ { if .SrcPath[0].ScalarString() == "_" && .SrcPath[0].IsUnquoted() { .SrcPath = .SrcPath[1:] } else { := ParentField() .SrcPath = append([]d2ast.String{.Name}, .SrcPath...) } if .DstPath[0].ScalarString() == "_" && .DstPath[0].IsUnquoted() { .DstPath = .DstPath[1:] } else { := ParentField() .DstPath = append([]d2ast.String{.Name}, .DstPath...) } = ParentMap() if == nil { return nil, nil, nil, errors.New("invalid underscore") } } for len(.SrcPath) > 1 && len(.DstPath) > 1 { if !strings.EqualFold(.SrcPath[0].ScalarString(), .DstPath[0].ScalarString()) || strings.Contains(.SrcPath[0].ScalarString(), "*") { return , , , nil } = append(, .SrcPath[0]) .SrcPath = .SrcPath[1:] .DstPath = .DstPath[1:] } return , , , nil } type Edge struct { // *Map parent Node importAST d2ast.Node suspended bool ID *EdgeID `json:"edge_id"` Primary_ *Scalar `json:"primary,omitempty"` Map_ *Map `json:"map,omitempty"` References []*EdgeReference `json:"references,omitempty"` } func ( *Edge) () d2ast.Node { return .importAST } func ( *Edge) ( d2ast.Node) { .importAST = if .Map() != nil { .Map().SetImportAST() } } func ( *Edge) ( Node) Node { := * = & .parent = if .ID != nil { .ID = .ID.Copy() } .References = append([]*EdgeReference(nil), .References...) if .Primary_ != nil { .Primary_ = .Primary_.Copy().(*Scalar) } if .Map_ != nil { .Map_ = .Map_.Copy().(*Map) } return } func ( *Edge) () Reference { for := len(.References) - 1; >= 0; -- { := .References[] if .Context_.Key.EdgeKey == nil && !.DueToLazyGlob() { return } } return nil } func ( *Edge) () *d2ast.Key { := .LastPrimaryRef() if == nil { return nil } return .(*EdgeReference).Context_.Key } func ( *Edge) () Reference { return .References[len(.References)-1] } type Array struct { parent Node Values []Value `json:"values"` } func ( *Array) ( Node) Node { := * = & .parent = .Values = append([]Value(nil), .Values...) for := range .Values { .Values[] = .Values[].Copy().(Value) } return } type FieldReference struct { String d2ast.String `json:"string"` KeyPath *d2ast.KeyPath `json:"key_path"` Context_ *RefContext `json:"context"` DueToGlob_ bool `json:"due_to_glob"` DueToLazyGlob_ bool `json:"due_to_lazy_glob"` } // appendReference records a source reference once. Lazy glob rules are // reevaluated as the IR grows, but replaying the same rule against the same // field does not create a new source location. Keeping one reference per // (context, key-path element) prevents glob evaluation order from inflating // reference lists quadratically while preserving every distinct source. func ( *Field) ( *FieldReference) { if .DueToLazyGlob_ { for , := range .References { if sameReferenceContext(.Context_, .Context_) && .KeyPath == .KeyPath && .String == .String && .DueToGlob_ == .DueToGlob_ && .DueToLazyGlob_ == .DueToLazyGlob_ { return } } } .References = append(.References, ) } // Primary returns true if the Value in Context.Key.Value corresponds to the Field // represented by String. func ( *FieldReference) () bool { if .KeyPath == .Context_.Key.Key { return len(.Context_.Key.Edges) == 0 && .KeyPathIndex() == len(.KeyPath.Path)-1 } else if .KeyPath == .Context_.Key.EdgeKey { return len(.Context_.Key.Edges) == 1 && .KeyPathIndex() == len(.KeyPath.Path)-1 } return false } func ( *FieldReference) () int { for , := range .KeyPath.Path { if .Unbox() == .String { return } } panic("d2ir.KeyReference.KeyPathIndex: String not in KeyPath?") } func ( *FieldReference) () bool { return .KeyPath == .Context_.Edge.Dst } func ( *FieldReference) () bool { return .Context_.Edge != nil } func ( *FieldReference) () d2ast.Node { if .String == nil { // Root map. return .Context_.Scope } return .String } type EdgeReference struct { Context_ *RefContext `json:"context"` DueToGlob_ bool `json:"due_to_glob"` DueToLazyGlob_ bool `json:"due_to_lazy_glob"` } func ( *Edge) ( *EdgeReference) { if .DueToLazyGlob_ { for , := range .References { if sameReferenceContext(.Context_, .Context_) && .DueToGlob_ == .DueToGlob_ && .DueToLazyGlob_ == .DueToLazyGlob_ { return } } } .References = append(.References, ) } func sameReferenceContext(, *RefContext) bool { return == || != nil && != nil && .Key == .Key && .Edge == .Edge && .Scope == .Scope && .ScopeMap == .ScopeMap && .ScopeAST == .ScopeAST } func ( *EdgeReference) () d2ast.Node { return .Context_.Edge } // Primary returns true if the Value in Context.Key.Value corresponds to the *Edge // represented by Context.Edge func ( *EdgeReference) () bool { return len(.Context_.Key.Edges) == 1 && .Context_.Key.EdgeKey == nil } type RefContext struct { Edge *d2ast.Edge `json:"edge"` Key *d2ast.Key `json:"key"` Scope *d2ast.Map `json:"-"` ScopeMap *Map `json:"-"` ScopeAST *d2ast.Map `json:"-"` } func ( *RefContext) () *RefContext { := * return & } func ( *RefContext) () int { for , := range .Key.Edges { if == .Edge { return } } return -1 } func ( *RefContext) ( *RefContext) bool { // We intentionally ignore edges here because the same glob can produce multiple RefContexts that should be treated the same with only the edge as the difference. // Same with ScopeMap. if !(.Key.Equals(.Key) && .Scope == .Scope && .ScopeAST == .ScopeAST) { return false } // Check if suspension values match for suspension operations // We don't want these two to equal // 1. *: suspend // 2. *: unsuspend := (.Key.Primary.Suspension != nil || .Key.Value.Suspension != nil) := (.Key.Primary.Suspension != nil || .Key.Value.Suspension != nil) if || { var , bool if .Key.Primary.Suspension != nil { = .Key.Primary.Suspension.Value } else if .Key.Value.Suspension != nil { = .Key.Value.Suspension.Value } if .Key.Primary.Suspension != nil { = .Key.Primary.Suspension.Value } else if .Key.Value.Suspension != nil { = .Key.Value.Suspension.Value } if && && != { return false } if != { return false } } return true } func ( *Map) () int { if == nil { return 0 } := len(.Fields) for , := range .Fields { if .Map() != nil { += .Map().() } } for , := range .Edges { if .Map_ != nil { += .Map_.() } } return } func ( *compiler) ( *Map) bool { if == nil { return false } // Check references as the fields and edges may not be compiled yet := .Parent().(*Field) for , := range .References { if .Primary() && .Context_.Key != nil && .Context_.Key.Value.Map != nil { for , := range .Context_.Key.Value.Map.Nodes { if .MapKey == nil { if .Import != nil { , := .peekImport(.Import) if { for , := range .Fields { , := d2ast.ReservedKeywords[.Name.ScalarString()] if !( && .Name.IsUnquoted()) { return true } } } } continue } if len(.MapKey.Edges) > 0 { return true } if .MapKey.Key != nil { , := d2ast.ReservedKeywords[.MapKey.Key.Path[0].Unbox().ScalarString()] if !( && .Name.IsUnquoted()) { return true } } } } } for , := range .Fields { , := d2ast.ReservedKeywords[.Name.ScalarString()] if !( && .Name.IsUnquoted()) { return true } } return false } func ( *Map) () int { if == nil { return 0 } := len(.Edges) for , := range .Fields { if .Map() != nil { += .Map().() } } for , := range .Edges { if .Map_ != nil { += .Map_.() } } return } func ( *Map) ( string) *Map { := RootMap() := .Map().GetField(d2ast.FlatUnquotedString("classes")) if != nil && .Map() != nil { := .Map().GetField(d2ast.FlatUnquotedString()) if != nil && .Map() != nil { return .Map() } } return nil } func ( *Map) ( ...d2ast.String) *Field { for len() > 0 && [0].ScalarString() == "_" && [0].IsUnquoted() { = ParentMap() if == nil { return nil } } return .getFieldLinear() } // getFieldLinear keeps the exported lookup safe for maps assembled or mutated // directly through the public Fields slice. Compiler-owned lookups use the // derived index below while the IR is being built single-threadedly. func ( *Map) ( []d2ast.String) *Field { if len() == 0 { return nil } := [0] := [1:] if .ScalarString() == "_" && .IsUnquoted() { return nil } for , := range .Fields { if == nil || .Name == nil || !strings.EqualFold(.Name.ScalarString(), .ScalarString()) { continue } if , := d2ast.ReservedKeywords[strings.ToLower(.ScalarString())]; && .Name.IsUnquoted() != .IsUnquoted() { continue } if len() == 0 { return } if .Map() != nil { return .Map().() } continue } return nil } func ( *Map) ( ...d2ast.String) *Field { for len() > 0 && [0].ScalarString() == "_" && [0].IsUnquoted() { = ParentMap() if == nil { return nil } } if len() == 0 { return nil } := .lookupField([0]) if == nil || len() == 1 { return } if .Map() == nil { return nil } return .Map().([1:]...) } // EnsureField is a bit of a misnomer. It's more of a Query/Ensure combination function at this point. func ( *Map) ( *d2ast.KeyPath, *RefContext, bool, *compiler) ([]*Field, error) { return .ensureFieldMode(, , , , false) } func ( *Map) ( *d2ast.KeyPath, *RefContext, bool, *compiler) ([]*Field, error) { return .ensureFieldMode(, , , , true) } func ( *Map) ( *d2ast.KeyPath, *RefContext, bool, *compiler, bool) ([]*Field, error) { := 0 for .Path[].Unbox().ScalarString() == "_" && .Path[].Unbox().IsUnquoted() { = ParentMap() if == nil { return nil, d2parser.Errorf(.Path[].Unbox(), "invalid underscore: no parent") } if +1 == len(.Path) { return nil, d2parser.Errorf(.Path[].Unbox(), "field key must contain more than underscores") } ++ } var *globContext if != nil && .Key.HasGlob() && != nil { = .getGlobContext() } var []*Field var []*Field := .ensureField(, , , , , , , &, &) if != nil && .applyingLazyWorklist && len(.globRefContextStack) > 0 { if len() > 0 { .enqueueLazyGlobFields(...) } } if len() > 0 && != nil && len(.globRefContextStack) == 0 { if len() > 0 { .enqueueLazyPostTargets(...) .applyLazyGlobs() } else { .applyLazyGlobs(nil) } } return , } func ( *Map) ( int, *d2ast.KeyPath, *RefContext, bool, *globContext, *compiler, bool, , *[]*Field) error { := func(*Field) bool { if == nil || == nil { return true } return .reserveGlobWork(.globSource(), 1) } := func( *Field, bool) bool { if != nil { var string if .Key.HasTripleGlob() { = d2format.FormatKeyPath(IDA()) } else { = d2format.FormatKeyPath(BoardIDA()) } if !.HasGlob() { if ! { .appliedFields[] = struct{}{} } return true } // For globs with edges, we only ignore duplicate fields if the glob is not at the terminal of the keypath, the glob is on the common key or the glob is on the edge key. And only for globs with edge indexes. := .Path[len(.Path)-1] if len(.Key.Edges) == 0 || .UnquotedString == nil || len(.UnquotedString.Pattern) == 0 || == .Key.Key || == .Key.EdgeKey { if , := .appliedFields[]; { return false } } if ! { .appliedFields[] = struct{}{} } } return true } := func( ...*Field) { for , := range { if (, false) { * = append(*, ) } } } , := .Path[].Unbox().(*d2ast.UnquotedString) if && .Pattern != nil { var []*Field var bool if != nil && .lazyGlobTarget != nil && != nil && (d2ast.IsDoubleGlob(.Pattern) || d2ast.IsTripleGlob(.Pattern)) { = .multiGlobMatchesToward(.lazyGlobTarget, .Pattern, ) = true } else { , = .multiGlob(.Pattern, ) } if != nil && .stopped() { return nil } if { if == len(.Path)-1 { (...) } else { for , := range { if !(, true) { continue } if .Map() == nil { .Composite = &Map{ parent: , } } := .Map().(+1, , , , , , , , ) if != nil { return } } } return nil } := .Fields if != nil && .lazyGlobTarget != nil && != nil { if := .directChildToward(.lazyGlobTarget); != nil { = []*Field{} } else { = nil } } for , := range { if !() { return nil } if .Name == nil { continue } if matchPattern(.Name.ScalarString(), .Pattern) { if == len(.Path)-1 { () } else { if !(, true) { continue } if .Map() == nil { .Composite = &Map{ parent: , } } := .Map().(+1, , , , , , , , ) if != nil { return } } } } return nil } := .Path[].Unbox() := .ScalarString() if , := d2ast.ReservedKeywords[strings.ToLower(.ScalarString())]; && .IsUnquoted() { = strings.ToLower(.ScalarString()) if , := d2ast.CompositeReservedKeywords[]; ! && < len(.Path)-1 { return d2parser.Errorf(.Path[].Unbox(), fmt.Sprintf(`"%s" must be the last part of the key`, )) } } if == "_" && .IsUnquoted() { return d2parser.Errorf(.Path[].Unbox(), `parent "_" can only be used in the beginning of paths, e.g. "_.x"`) } if == "classes" && .IsUnquoted() && NodeBoardKind() == "" { return d2parser.Errorf(.Path[].Unbox(), "%s must be declared at a board root scope", ) } if findBoardKeyword() != -1 && .IsUnquoted() && NodeBoardKind() == "" { return d2parser.Errorf(.Path[].Unbox(), "%s must be declared at a board root scope", ) } var *Field if { = .lookupField() } else { = .getFieldLinear([]d2ast.String{}) } if := ; != nil { // Don't add references for fake common KeyPath from trimCommon in CreateEdge. if != nil { := != nil && len(.globRefContextStack) > 0 := != nil && .lazyGlobBeingApplied .appendReference(&FieldReference{ String: .Path[].Unbox(), KeyPath: , Context_: , DueToGlob_: , DueToLazyGlob_: , }) } if +1 == len(.Path) { () return nil } if !(, true) { return nil } if , := .Composite.(*Array); { return d2parser.Errorf(.Path[].Unbox(), "cannot index into array") } if .Map() == nil { .Composite = &Map{ parent: , } } return .Map().(+1, , , , , , , , ) } if ! { return nil } if , := d2ast.ReservedKeywords[strings.ToLower(.ScalarString())]; !( && .IsUnquoted()) && != nil && len(.globRefContextStack) > 0 { := ParentShape() if == d2target.ShapeClass || == d2target.ShapeSQLTable { return nil } } var d2ast.Node if != nil && len(.globRefContextStack) > 0 { = .globSource() if !.reserveGlobGeneratedFieldWork(, ) { return nil } } := &Field{ parent: , Name: .Path[].Unbox(), } defer func() { if == nil || < .FirstGlob() { return } for , := range .globRefContextStack { var string if .Key.HasTripleGlob() { = d2format.FormatKeyPath(IDA()) } else { = d2format.FormatKeyPath(BoardIDA()) } := .getGlobContext() .appliedFields[] = struct{}{} } }() // Don't add references for fake common KeyPath from trimCommon in CreateEdge. if != nil { := != nil && len(.globRefContextStack) > 0 := != nil && .lazyGlobBeingApplied .appendReference(&FieldReference{ String: .Path[].Unbox(), KeyPath: , Context_: , DueToGlob_: , DueToLazyGlob_: , }) } if !(, true) { return nil } if != nil && len(.globRefContextStack) > 0 { if !.reserveGlobField() { return nil } } .appendField() * = append(*, ) if +1 == len(.Path) { () return nil } if .Composite == nil { .Composite = &Map{ parent: , } } return .Map().(+1, , , , , , , , ) } func ( *Map) ( *EdgeID) *Edge { if == nil { return nil } , , , := .resolve() if != nil { return nil } if len() > 0 { := .GetField(...) if == nil { return nil } if .Map() == nil { return nil } return .Map().() } for , := range .Edges { if .ID.Match() { return .removeEdge() } } return nil } func ( *Map) ( ...string) *Field { if len() == 0 { return nil } := [0] := [1:] for , := range .Fields { if !strings.EqualFold(.Name.ScalarString(), ) { continue } if len() == 0 { .deleteEdgesForFieldReferences() .removeField() // If a field was deleted from a keyword-holder keyword and that holder is empty, // then that holder becomes meaningless and should be deleted too := ParentField() for := range d2ast.ReservedKeywordHolders { if != nil && .Name.ScalarString() == && .Name.IsUnquoted() && len(.Map().Fields) == 0 { := ParentMap() for , := range .Fields { if .Name.ScalarString() == && .Name.IsUnquoted() { .removeField() break } } } } return } if .Map() != nil { return .Map().(...) } } return nil } func ( *Map) ( *EdgeID, *RefContext, *compiler) []*Edge { if != nil { return .getEdgesMode(, , , false) } , , , := .resolve() if != nil { return nil } if len() > 0 { := .GetField(...) if == nil { return nil } if .Map() != nil { return .Map().(, nil, nil) } return nil } var []*Edge for , := range .Edges { if .ID.Match() { = append(, ) } } return } func ( *Map) ( *EdgeID, *RefContext, *compiler) []*Edge { return .getEdgesMode(, , , true) } func ( *Map) ( *EdgeID, *RefContext, *compiler, bool) []*Edge { var *globContext if .Key.HasGlob() && != nil { = .ensureGlobContext() } var []*Edge .getEdges(, , , , , &) return } func ( *Map) ( *EdgeID) []*Edge { , , , := .resolve() if != nil { return nil } if len() > 0 { := .getFieldIndexed(...) if == nil || .Map() == nil { return nil } return .Map().() } .ensureEdgeIndex() var []*Edge for , := range .edgeIndex[makeEdgeIndexKey()] { if .ID.Match() { = append(, ) } } return } func ( *Map) ( *EdgeID, *RefContext, *globContext, *compiler, bool, *[]*Edge) error { if != nil && .stopped() { return nil } , , , := .resolve() if != nil { return } if len() > 0 { := d2ast.MakeKeyPathString() := 0 for , := range .Path { for := ; < len(.Edge.Src.Path); ++ { := .Edge.Src.Path[] if .ScalarString() == .ScalarString() { .Path[] = += + 1 } } } , := .ensureFieldMode(, nil, false, , ) if != nil { return nil } for , := range { if != nil && .stopped() { return nil } if , := .Composite.(*Array); { return d2parser.Errorf(.Edge.Src, "cannot index into array") } if .Map() == nil { .Composite = &Map{ parent: , } } = .Map().(, , , , , ) if != nil { return } } return nil } , := .ScopeMap.ensureFieldMode(.Edge.Src, nil, false, , ) if != nil { return } , := .ScopeMap.ensureFieldMode(.Edge.Dst, nil, false, , ) if != nil { return } for , := range { for , := range { if != nil && .stopped() { return nil } if != nil && (.Edge.Src.HasGlob() || .Edge.Dst.HasGlob()) && !.reserveEdgeExpansion(.Edge, , , , true) { return nil } := .Copy() .SrcPath = RelIDA(, ) .DstPath = RelIDA(, ) var []*Edge if { = .getEdgesIndexed() } else { = .GetEdges(, nil, nil) } for , := range { if != nil { var string if .Key.HasTripleGlob() { = d2format.FormatKeyPath(IDA()) } else { = d2format.FormatKeyPath(BoardIDA()) } if , := .appliedEdges[]; { continue } .appliedEdges[] = struct{}{} } * = append(*, ) } } } return nil } func ( *Map) ( *EdgeID, *RefContext, *compiler) ([]*Edge, error) { return .createEdgeMode(, , , false) } func ( *Map) ( *EdgeID, *RefContext, *compiler) ([]*Edge, error) { return .createEdgeMode(, , , true) } func ( *Map) ( *EdgeID, *RefContext, *compiler, bool) ([]*Edge, error) { if != nil && .stopped() { return nil, nil } var []*Edge var *globContext if != nil && .Key.HasGlob() && != nil { = .ensureGlobContext() } := .createEdge(, , , , , &) if len() > 0 && != nil && len(.globRefContextStack) == 0 { for , := range .globContexts() { if .stopped() { return nil, nil } := .lazyGlobBeingApplied .lazyGlobBeingApplied = true .compileKey(.refctx) .lazyGlobBeingApplied = } } return , } func ( *Map) ( *EdgeID, *RefContext, *globContext, *compiler, bool, *[]*Edge) error { if != nil && .stopped() { return nil } if ParentEdge() != nil { return d2parser.Errorf(.Edge, "cannot create edge inside edge") } , , , := .resolve() if != nil { return d2parser.Errorf(.Edge, .Error()) } if len() > 0 { := d2ast.MakeKeyPathString() := 0 for , := range .Path { for := ; < len(.Edge.Src.Path); ++ { := .Edge.Src.Path[] if .ScalarString() == .ScalarString() { .Path[] = += + 1 } } } , := .ensureFieldMode(, nil, true, , ) if != nil { return } for , := range { if != nil && .stopped() { return nil } if , := .Composite.(*Array); { return d2parser.Errorf(.Edge.Src, "cannot index into array") } if .Map() == nil { .Composite = &Map{ parent: , } } = .Map().(, , , , , ) if != nil { return } } return nil } := findProhibitedEdgeKeyword(.SrcPath...) if != -1 { return d2parser.Errorf(.Edge.Src.Path[].Unbox(), "reserved keywords are prohibited in edges") } = findBoardKeyword(.SrcPath...) if == len(.SrcPath)-1 { return d2parser.Errorf(.Edge.Src.Path[].Unbox(), "edge with board keyword alone doesn't make sense") } = findProhibitedEdgeKeyword(.DstPath...) if != -1 { return d2parser.Errorf(.Edge.Dst.Path[].Unbox(), "reserved keywords are prohibited in edges") } = findBoardKeyword(.DstPath...) if == len(.DstPath)-1 { return d2parser.Errorf(.Edge.Dst.Path[].Unbox(), "edge with board keyword alone doesn't make sense") } , := .ScopeMap.ensureFieldMode(.Edge.Src, , true, , ) if != nil { return } , := .ScopeMap.ensureFieldMode(.Edge.Dst, , true, , ) if != nil { return } for , := range { for , := range { if != nil && .stopped() { return nil } if != nil && (.Edge.Src.HasGlob() || .Edge.Dst.HasGlob()) && !.reserveEdgeExpansion(.Edge, , , , false) { return nil } if == && (.Edge.Src.HasGlob() || .Edge.Dst.HasGlob()) { // Globs do not make self edges. continue } if .Edge.Src.HasMultiGlob() { // If src has a double glob we only select leafs, those without children. if .IsContainer(.Map()) { continue } if .stopped() { return nil } if NodeBoardKind() != "" || ParentBoard() != ParentBoard() { continue } } if .Edge.Dst.HasMultiGlob() { // If dst has a double glob we only select leafs, those without children. if .IsContainer(.Map()) { continue } if .stopped() { return nil } if NodeBoardKind() != "" || ParentBoard() != ParentBoard() { continue } } := .Copy() .SrcPath = RelIDA(, ) .DstPath = RelIDA(, ) , := .createEdge2(, , , , , , ) if != nil { return } for , := range { * = append(*, ) } } } return nil } func ( *Map) ( *EdgeID, *RefContext, *globContext, *compiler, bool, , *Field) ([]*Edge, error) { if NodeBoardKind() != "" { return nil, d2parser.Errorf(.Edge.Src, "cannot create edges between boards") } if NodeBoardKind() != "" { return nil, d2parser.Errorf(.Edge.Dst, "cannot create edges between boards") } if ParentBoard() != ParentBoard() { return nil, d2parser.Errorf(.Edge, "cannot create edges between boards") } , , , := .resolve() if != nil { return nil, d2parser.Errorf(.Edge, .Error()) } if len() > 0 { := d2ast.MakeKeyPathString() := 0 for , := range .Path { for := ; < len(.Edge.Src.Path); ++ { := .Edge.Src.Path[] if .ScalarString() == .ScalarString() { .Path[] = += + 1 } } } , := .ensureFieldMode(, nil, true, , ) if != nil { return nil, } var []*Edge for , := range { if , := .Composite.(*Array); { return nil, d2parser.Errorf(.Edge.Src, "cannot index into array") } if .Map() == nil { .Composite = &Map{ parent: , } } , := .Map().(, , , , , , ) if != nil { return nil, } = append(, ...) } return , nil } .Index = nil .Glob = true var []*Edge if { = .getEdgesIndexed() } else { = .GetEdges(, nil, nil) } := len() .Index = & .Glob = false := &Edge{ parent: , ID: , References: []*EdgeReference{{ Context_: , DueToGlob_: len(.globRefContextStack) > 0, DueToLazyGlob_: .lazyGlobBeingApplied, }}, } if != nil { var string // We only ever want to create one of the edge per glob so we filter without the edge index. := .Copy(.Parent()).(*Edge) .ID = .ID.Copy() .ID.Index = nil if .Key.HasTripleGlob() { = d2format.FormatKeyPath(IDA()) } else { = d2format.FormatKeyPath(BoardIDA()) } if , := .appliedEdges[]; { return nil, nil } .appliedEdges[] = struct{}{} } .appendEdge() return []*Edge{}, nil } func ( *Scalar) () d2ast.Node { return .Value } func ( *Field) () d2ast.Node { := &d2ast.Key{ Key: &d2ast.KeyPath{ Path: []*d2ast.StringBox{ d2ast.MakeValueBox(.Name).StringBox(), }, }, } if .Primary_ != nil { .Primary = d2ast.MakeValueBox(.Primary_.AST().(d2ast.Value)).ScalarBox() } if .Composite != nil { := .Composite.AST().(d2ast.Value) if , := .(*d2ast.Map); { := .Range.Path // Treat it as multi-line, but not file-map (line 0) .Range = d2ast.MakeRange(",1:0:0-2:0:0") .Range.Path = } .Value = d2ast.MakeValueBox() } return } func ( *Edge) () d2ast.Node { := &d2ast.Edge{} .Src = d2ast.MakeKeyPathString(.ID.SrcPath) if .ID.SrcArrow { .SrcArrow = "<" } .Dst = d2ast.MakeKeyPathString(.ID.DstPath) if .ID.DstArrow { .DstArrow = ">" } := &d2ast.Key{ Edges: []*d2ast.Edge{}, } if .Primary_ != nil { .Primary = d2ast.MakeValueBox(.Primary_.AST().(d2ast.Value)).ScalarBox() } if .Map_ != nil { .Value = d2ast.MakeValueBox(.Map_.AST().(*d2ast.Map)) } return } func ( *Edge) () d2ast.String { := .AST().(*d2ast.Key) if .ID.Index != nil { .EdgeIndex = &d2ast.EdgeIndex{ Int: .ID.Index, } } .Primary = d2ast.ScalarBox{} .Value = d2ast.ValueBox{} := d2format.Format() return d2ast.FlatUnquotedString() } func ( *Array) () d2ast.Node { if == nil { return nil } := &d2ast.Array{} for , := range .Values { .Nodes = append(.Nodes, d2ast.MakeArrayNodeBox(.AST().(d2ast.ArrayNode))) } return } func ( *Map) () d2ast.Node { if == nil { return nil } := &d2ast.Map{ Range: d2ast.MakeRange(",0:0:0-1:0:0"), } if .parent != nil && NodeBoardKind() != "" { , := .parent.(*Field) if { .Range.Path = .Name.GetRange().Path } } for , := range .Fields { .Nodes = append(.Nodes, d2ast.MakeMapNodeBox(.AST().(d2ast.MapNode))) } for , := range .Edges { .Nodes = append(.Nodes, d2ast.MakeMapNodeBox(.AST().(d2ast.MapNode))) } return } func ( *Map) ( int, *d2ast.KeyPath, *RefContext, *compiler) { := .Path[] := .GetField(.Unbox()) if == nil { return } .appendReference(&FieldReference{ String: .Unbox(), KeyPath: , Context_: , DueToGlob_: len(.globRefContextStack) > 0, DueToLazyGlob_: .lazyGlobBeingApplied, }) if +1 == len(.Path) { return } if .Map() != nil { .Map().(+1, , , ) } } func ( *Map) *Map { if .Root() { return } return (ParentMap()) } func ( Node) *Map { for { = .Parent() if == nil { return nil } if , := .(*Map); { return } } } func ( Node) *Field { for { = .Parent() if == nil { return nil } if , := .(*Field); { return } } } func ( Node) bool { for { if == nil { return false } if NodeBoardKind() != "" { return false } if , := .(*Field); && .Name.ScalarString() == "vars" && .Name.IsUnquoted() { return true } if == (*Map)(nil) { return false } = .Parent() } } func ( Node) Node { for { = .Parent() if == nil { return nil } if NodeBoardKind() != "" { return } } } func ( Node) *Edge { for { = .Parent() if == nil { return nil } if , := .(*Edge); { return } } } func ( Node) string { for { , := .(*Field) if { if .Map() != nil { := .Map().GetField(d2ast.FlatUnquotedString("shape")) if != nil && .Primary() != nil { return .Primary().Value.ScalarString() } } } = .Parent() if == nil { return "" } } } func countUnderscores( []d2ast.String) int { for , := range { if .ScalarString() != "_" || !.IsUnquoted() { return } } return 0 } func findBoardKeyword( ...d2ast.String) int { for := range { if , := d2ast.BoardKeywords[strings.ToLower([].ScalarString())]; && [].IsUnquoted() { return } } return -1 } func findProhibitedEdgeKeyword( ...d2ast.String) int { for := range { if , := d2ast.SimpleReservedKeywords[[].ScalarString()]; && [].IsUnquoted() { return } if , := d2ast.ReservedKeywordHolders[[].ScalarString()]; && [].IsUnquoted() { return } } return -1 } func parentRef( Node) Reference { := ParentField() if != nil { return .LastRef() } := ParentEdge() if != nil { return .LastRef() } return nil } func parentPrimaryRef( Node) Reference { := ParentField() if != nil { return .LastPrimaryRef() } := ParentEdge() if != nil { return .LastPrimaryRef() } return nil } func parentPrimaryKey( Node) *d2ast.Key { := ParentField() if != nil { return .LastPrimaryKey() } := ParentEdge() if != nil { return .LastPrimaryKey() } return nil } // BoardIDA returns the absolute path to n from the nearest board root. func ( Node) ( []d2ast.String) { for { switch n := .(type) { case *Field: if .Root() || NodeBoardKind() != "" { reverseIDA() return } = append(, .Name) case *Edge: = append(, .IDString()) } = .Parent() if == nil { reverseIDA() return } } } // IDA returns the absolute path to n. func ( Node) ( []d2ast.String) { for { switch n := .(type) { case *Field: = append(, .Name) if .Root() { reverseIDA() return } case *Edge: = append(, .IDString()) } = .Parent() if == nil { reverseIDA() return } } } // RelIDA returns the path to n relative to p. func (, Node) ( []d2ast.String) { for { switch n := .(type) { case *Field: = append(, .Name) if .Root() { reverseIDA() return } case *Edge: = append(, d2ast.FlatUnquotedString(.String())) } = .Parent() , := .(*Field) , := .(*Edge) if == nil || ( && (.Root() || == || .Composite == )) || ( && ( == || .Map_ == )) { reverseIDA() return } } } func reverseIDA[ any]( []) { for := 0; < len()/2; ++ { := [] [] = [len()--1] [len()--1] = } } func ( *Field) ( Node) bool { := .(*Field) if .Name != .Name { return false } if !.Primary_.Equal(.Primary_) { return false } if !.Composite.Equal(.Composite) { return false } return true } func ( *Edge) ( Node) bool { := .(*Edge) if !.ID.Match(.ID) { return false } if !.Primary_.Equal(.Primary_) { return false } if !.Map_.Equal(.Map_) { return false } return true } func ( *Array) ( Node) bool { := .(*Array) if len(.Values) != len(.Values) { return false } for := range .Values { if !.Values[].Equal(.Values[]) { return false } } return true } func ( *Map) ( Node) bool { := .(*Map) if len(.Fields) != len(.Fields) { return false } if len(.Edges) != len(.Edges) { return false } for := range .Fields { if !.Fields[].Equal(.Fields[]) { return false } } for := range .Edges { if !.Edges[].Equal(.Edges[]) { return false } } return true } func ( *Map) ( *d2ast.Key) bool { := .Map().GetField(d2ast.FlatUnquotedString("classes")) if == nil || .Map() == nil { return false } for , := range .Map().Fields { if .Map() == nil { continue } := .Map().GetField(.Key.IDA()...) if == nil { continue } for , := range .References { if .Context_.Key == { return true } } } return false } func ( *Map) () bool { := ParentBoard() if .Map() == nil { return false } := .Map().GetField(d2ast.FlatUnquotedString("classes")) if == nil || .Map() == nil { return false } for , := range .Map().Fields { if .Map() == { return true } } return false } func ( *Map) ( []string) *Map { if == nil { return nil } if len() == 0 { return } := .GetField(d2ast.FlatUnquotedString("layers")) := .GetField(d2ast.FlatUnquotedString("scenarios")) := .GetField(d2ast.FlatUnquotedString("steps")) if != nil && .Map() != nil { for , := range .Map().Fields { if .Name.ScalarString() == [0] { if len() == 1 { return .Map() } return .Map().([1:]) } } } if != nil && .Map() != nil { for , := range .Map().Fields { if .Name.ScalarString() == [0] { if len() == 1 { return .Map() } return .Map().([1:]) } } } if != nil && .Map() != nil { for , := range .Map().Fields { if .Name.ScalarString() == [0] { if len() == 1 { return .Map() } return .Map().([1:]) } } } return nil }