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.
2023-01-09 21:26:11 +00:00
package d2ir
import (
2023-01-16 11:52:37 +00:00
"errors"
2023-01-11 19:05:50 +00:00
"fmt"
2023-01-09 21:26:11 +00:00
"strings"
2023-01-17 12:44:14 +00:00
"oss.terrastruct.com/util-go/go2"
2023-01-09 21:26:11 +00:00
"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-09 21:26:11 +00:00
)
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.
2023-01-09 21:26:11 +00:00
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-02-27 21:34:03 +00:00
Equal ( n2 Node ) bool
2023-01-17 12:44:14 +00:00
2023-06-16 23:23:57 +00:00
AST ( ) d2ast . Node
2023-01-16 13:31:18 +00:00
fmt . Stringer
2023-01-22 08:21:21 +00:00
LastRef ( ) Reference
LastPrimaryKey ( ) * d2ast . Key
}
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
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 }
2023-01-09 21:26:11 +00:00
func ( n * Scalar ) value ( ) { }
func ( n * Array ) value ( ) { }
func ( n * Map ) value ( ) { }
func ( n * Array ) composite ( ) { }
func ( n * Map ) composite ( ) { }
2023-06-16 23:23:57 +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-16 13:31:18 +00:00
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
2023-01-24 11:09:40 +00:00
Primary ( ) bool
2023-01-22 08:59:02 +00:00
}
var _ Reference = & FieldReference { }
var _ Reference = & EdgeReference { }
func ( r * FieldReference ) reference ( ) { }
func ( r * EdgeReference ) reference ( ) { }
2023-01-09 21:26:11 +00:00
type Scalar struct {
2023-01-17 12:44:14 +00:00
parent Node
2023-01-09 21:26:11 +00:00
Value d2ast . Scalar ` json:"value" `
}
2023-01-22 08:21:21 +00:00
func ( s * Scalar ) Copy ( newParent Node ) Node {
2023-01-09 21:26:11 +00:00
tmp := * s
s = & tmp
2023-01-22 08:21:21 +00:00
s . parent = newParent
2023-01-09 21:26:11 +00:00
return s
}
2023-02-27 21:34:03 +00:00
func ( s * Scalar ) Equal ( n2 Node ) bool {
s2 := n2 . ( * Scalar )
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 ( )
}
2023-01-09 21:26:11 +00:00
type Map struct {
2023-01-17 12:44:14 +00:00
parent Node
2023-01-09 21:26:11 +00:00
Fields [ ] * Field ` json:"fields" `
Edges [ ] * Edge ` json:"edges" `
}
2023-01-24 06:45:21 +00:00
func ( m * Map ) initRoot ( ) {
m . parent = & Field {
2023-03-01 22:53:58 +00:00
Name : "root" ,
2023-01-24 06:45:21 +00:00
References : [ ] * FieldReference { {
Context : & RefContext {
ScopeMap : m ,
} ,
} } ,
}
}
2023-01-22 08:21:21 +00:00
func ( m * Map ) Copy ( newParent Node ) Node {
2023-01-09 21:26:11 +00:00
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 ) )
2023-01-09 21:26:11 +00:00
}
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 ( )
}
2023-01-09 21:26:11 +00:00
return m
}
2023-01-22 02:52:33 +00:00
// CopyBase copies the map m without layers/scenarios/steps.
2023-01-22 08:21:21 +00:00
func ( m * Map ) CopyBase ( newParent Node ) * Map {
2023-02-27 21:34:03 +00:00
if m == nil {
return ( & Map { } ) . Copy ( newParent ) . ( * Map )
}
2023-01-22 02:52:33 +00:00
layers := m . DeleteField ( "layers" )
scenarios := m . DeleteField ( "scenarios" )
steps := m . DeleteField ( "steps" )
2023-06-19 06:57:08 +00:00
2023-01-22 08:21:21 +00:00
m2 := m . Copy ( newParent ) . ( * Map )
2023-01-22 02:52:33 +00:00
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
}
2023-01-09 21:26:11 +00:00
// 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
}
2023-01-24 11:09:40 +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-07-30 20:52:42 +00:00
var ok bool
f , ok = n . parent . ( * Field )
if ! ok {
return ""
}
2023-01-24 11:09:40 +00:00
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
}
2023-01-09 21:26:11 +00:00
}
type Field struct {
2023-01-22 09:08:13 +00:00
// *Map.
parent Node
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
2023-01-22 08:59:02 +00:00
References [ ] * FieldReference ` json:"references,omitempty" `
2023-01-09 21:26:11 +00:00
}
2023-01-22 08:21:21 +00:00
func ( f * Field ) Copy ( newParent Node ) Node {
2023-01-09 21:26:11 +00:00
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 )
2023-01-09 21:26:11 +00:00
}
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 -- {
2023-01-24 11:09:40 +00:00
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 ]
}
2023-01-09 21:26:11 +00:00
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" `
2023-07-27 09:51:43 +00:00
Glob bool ` json:"glob" `
2023-01-09 21:26:11 +00:00
}
2023-01-16 13:31:18 +00:00
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 ( ) ,
2023-01-16 13:31:18 +00:00
SrcArrow : ke . SrcArrow == "<" ,
2023-01-18 15:15:16 +00:00
DstPath : ke . Dst . IDA ( ) ,
2023-01-16 13:31:18 +00:00
DstArrow : ke . DstArrow == ">" ,
2023-01-18 15:15:16 +00:00
}
if k . EdgeIndex != nil {
eid . Index = k . EdgeIndex . Int
2023-07-27 09:51:43 +00:00
eid . Glob = k . EdgeIndex . Glob
2023-01-18 15:15:16 +00:00
}
eida = append ( eida , eid )
2023-01-16 13:31:18 +00:00
}
return eida
}
2023-01-09 21:26:11 +00:00
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
}
2023-01-09 21:26:11 +00:00
}
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-02-15 05:26:29 +00:00
// resolve resolves both underscores and commons in eid.
// It returns the new eid, containing map adjusted for underscores and common ida.
func ( eid * EdgeID ) resolve ( m * Map ) ( _ * EdgeID , _ * Map , common [ ] string , _ error ) {
2023-01-17 12:44:14 +00:00
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 {
2023-02-15 05:06:57 +00:00
return nil , nil , nil , errors . New ( "invalid underscore" )
2023-01-17 12:44:14 +00:00
}
}
2023-01-09 21:26:11 +00:00
for len ( eid . SrcPath ) > 1 && len ( eid . DstPath ) > 1 {
if ! strings . EqualFold ( eid . SrcPath [ 0 ] , eid . DstPath [ 0 ] ) {
2023-02-15 05:26:29 +00:00
return eid , m , common , nil
2023-01-09 21:26:11 +00:00
}
2023-02-15 05:26:29 +00:00
common = append ( common , eid . SrcPath [ 0 ] )
2023-01-09 21:26:11 +00:00
eid . SrcPath = eid . SrcPath [ 1 : ]
eid . DstPath = eid . DstPath [ 1 : ]
}
2023-02-15 05:06:57 +00:00
2023-02-15 05:26:29 +00:00
return eid , m , common , nil
2023-01-09 21:26:11 +00:00
}
type Edge struct {
2023-01-22 09:08:13 +00:00
// *Map
parent Node
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
2023-01-22 08:59:02 +00:00
References [ ] * EdgeReference ` json:"references,omitempty" `
2023-01-09 21:26:11 +00:00
}
2023-01-22 08:21:21 +00:00
func ( e * Edge ) Copy ( newParent Node ) Node {
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
}
2023-01-18 15:15:16 +00:00
if e . Map_ != nil {
e . Map_ = e . Map_ . Copy ( e ) . ( * Map )
2023-01-09 21:26:11 +00:00
}
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 ]
}
2023-01-09 21:26:11 +00:00
type Array struct {
2023-01-17 12:44:14 +00:00
parent Node
2023-01-09 21:26:11 +00:00
Values [ ] Value ` json:"values" `
}
2023-01-22 08:21:21 +00:00
func ( a * Array ) Copy ( newParent Node ) Node {
2023-01-09 21:26:11 +00:00
tmp := * a
a = & tmp
2023-01-22 08:21:21 +00:00
a . parent = newParent
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
2023-01-18 05:30:28 +00:00
Context * RefContext ` json:"context" `
2023-01-16 15:45:13 +00:00
}
2023-01-24 11:09:40 +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.
2023-01-24 11:09:40 +00:00
func ( fr * FieldReference ) Primary ( ) bool {
2023-01-24 05:48:43 +00:00
if fr . KeyPath == fr . Context . Key . Key {
2023-01-24 11:09:40 +00:00
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 {
2023-01-24 11:09:40 +00:00
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
2023-01-09 21:26:11 +00:00
}
type EdgeReference struct {
2023-01-18 05:30:28 +00:00
Context * RefContext ` json:"context" `
2023-01-09 21:26:11 +00:00
}
2023-01-22 08:59:02 +00:00
func ( er * EdgeReference ) AST ( ) d2ast . Node {
return er . Context . Edge
}
2023-01-24 11:09:40 +00:00
// 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
}
2023-01-09 21:26:11 +00:00
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-06-20 03:06:26 +00:00
ScopeAST * d2ast . 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-09 21:26:11 +00:00
}
2023-07-30 20:52:42 +00:00
func ( rc * RefContext ) Equal ( rc2 * RefContext ) bool {
2023-08-16 06:33:50 +00:00
// 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.
return rc . Key . Equals ( rc2 . Key ) && rc . Scope == rc2 . Scope && rc . ScopeMap == rc2 . ScopeMap && rc . ScopeAST == rc2 . ScopeAST
2023-07-30 20:52:42 +00:00
}
2023-01-16 11:52:37 +00:00
func ( m * Map ) FieldCountRecursive ( ) int {
if m == nil {
return 0
}
2023-01-09 21:26:11 +00:00
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 ( )
2023-01-09 21:26:11 +00:00
}
}
return acc
}
2023-07-30 08:15:34 +00:00
func ( m * Map ) IsContainer ( ) bool {
if m == nil {
return false
}
for _ , f := range m . Fields {
_ , isReserved := d2graph . ReservedKeywords [ f . Name ]
if ! isReserved {
return true
}
}
return false
}
2023-01-16 11:52:37 +00:00
func ( m * Map ) EdgeCountRecursive ( ) int {
if m == nil {
return 0
}
2023-01-09 21:26:11 +00:00
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
}
}
2023-01-09 21:26:11 +00:00
for _ , e := range m . Edges {
2023-01-18 15:15:16 +00:00
if e . Map_ != nil {
acc += e . Map_ . EdgeCountRecursive ( )
2023-01-09 21:26:11 +00:00
}
}
return acc
}
2023-02-06 21:32:08 +00:00
func ( m * Map ) GetClassMap ( name string ) * Map {
root := RootMap ( m )
classes := root . Map ( ) . GetField ( "classes" )
if classes != nil && classes . Map ( ) != nil {
class := classes . Map ( ) . GetField ( name )
if class != nil && class . Map ( ) != nil {
return class . Map ( )
}
}
return nil
}
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 {
2023-01-09 21:26:11 +00:00
if len ( ida ) == 0 {
2023-01-16 11:52:37 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
s := ida [ 0 ]
rest := ida [ 1 : ]
2023-01-17 12:44:14 +00:00
if s == "_" {
return nil
}
2023-01-09 21:26:11 +00:00
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-09 21:26:11 +00:00
}
2023-01-18 15:15:16 +00:00
if f . Map ( ) != nil {
return f . Map ( ) . getField ( rest )
2023-01-09 21:26:11 +00:00
}
}
2023-01-16 11:52:37 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
2023-07-29 23:45:30 +00:00
// EnsureField is a bit of a misnomer. It's more of a Query/Ensure combination function at this point.
2023-07-30 20:52:42 +00:00
func ( m * Map ) EnsureField ( kp * d2ast . KeyPath , refctx * RefContext , create bool , c * compiler ) ( [ ] * Field , error ) {
2023-01-18 10:06:44 +00:00
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-06-24 22:31:58 +00:00
2023-07-30 20:52:42 +00:00
var gctx * globContext
if refctx != nil && refctx . Key . HasGlob ( ) && c != nil {
gctx = c . getGlobContext ( refctx )
}
2023-06-24 22:31:58 +00:00
var fa [ ] * Field
2023-07-30 20:52:42 +00:00
err := m . ensureField ( i , kp , refctx , create , gctx , c , & fa )
if err != nil {
return fa , err
}
if len ( fa ) > 0 && create && c != nil {
for _ , gctx2 := range c . globContexts ( ) {
c . compileKey ( gctx2 . refctx )
}
}
return fa , nil
2023-01-17 12:44:14 +00:00
}
2023-07-30 20:52:42 +00:00
func ( m * Map ) ensureField ( i int , kp * d2ast . KeyPath , refctx * RefContext , create bool , gctx * globContext , c * compiler , fa * [ ] * Field ) error {
faAppend := func ( fa2 ... * Field ) {
for _ , f := range fa2 {
if gctx != nil {
// Always match all commons, sources and destinations for edge globs.
if len ( refctx . Key . Edges ) == 0 || kp == refctx . Key . EdgeKey {
ks := d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( f ) ) )
if _ , ok := gctx . appliedFields [ ks ] ; ok {
continue
}
gctx . appliedFields [ ks ] = struct { } { }
}
}
* fa = append ( * fa , f )
}
}
2023-06-24 22:31:58 +00:00
us , ok := kp . Path [ i ] . Unbox ( ) . ( * d2ast . UnquotedString )
if ok && us . Pattern != nil {
2023-07-30 20:52:42 +00:00
fa2 , ok := m . multiGlob ( us . Pattern )
2023-07-27 09:36:33 +00:00
if ok {
if i == len ( kp . Path ) - 1 {
2023-07-30 20:52:42 +00:00
faAppend ( fa2 ... )
2023-07-27 09:36:33 +00:00
} else {
for _ , f := range fa2 {
if f . Map ( ) == nil {
f . Composite = & Map {
parent : f ,
}
}
2023-07-30 20:52:42 +00:00
err := f . Map ( ) . ensureField ( i + 1 , kp , refctx , create , gctx , c , fa )
2023-07-27 09:36:33 +00:00
if err != nil {
return err
}
}
}
return nil
}
2023-06-24 22:31:58 +00:00
for _ , f := range m . Fields {
if matchPattern ( f . Name , us . Pattern ) {
if i == len ( kp . Path ) - 1 {
2023-07-30 20:52:42 +00:00
faAppend ( f )
2023-07-27 09:36:33 +00:00
} else {
if f . Map ( ) == nil {
f . Composite = & Map {
parent : f ,
}
}
2023-07-30 20:52:42 +00:00
err := f . Map ( ) . ensureField ( i + 1 , kp , refctx , create , gctx , c , fa )
2023-07-27 09:36:33 +00:00
if err != nil {
return err
}
2023-06-24 22:31:58 +00:00
}
}
}
return nil
}
2023-01-18 10:06:44 +00:00
head := kp . Path [ i ] . Unbox ( ) . ScalarString ( )
2023-01-09 21:26:11 +00:00
2023-03-05 17:43:42 +00:00
if _ , ok := d2graph . ReservedKeywords [ strings . ToLower ( head ) ] ; ok {
head = strings . ToLower ( head )
2023-06-01 06:28:33 +00:00
if _ , ok := d2graph . CompositeReservedKeywords [ head ] ; ! ok && i < len ( kp . Path ) - 1 {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( kp . Path [ i ] . Unbox ( ) , fmt . Sprintf ( ` "%s" must be the last part of the key ` , head ) )
2023-06-01 06:04:09 +00:00
}
2023-04-27 18:22:10 +00:00
}
2023-01-18 10:06:44 +00:00
if head == "_" {
2023-06-24 22:31:58 +00:00
return 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-09 21:26:11 +00:00
2023-02-06 21:32:08 +00:00
if head == "classes" && NodeBoardKind ( m ) == "" {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( kp . Path [ i ] . Unbox ( ) , "%s is only allowed at a board root" , head )
2023-02-06 21:32:08 +00:00
}
2023-01-28 01:19:12 +00:00
if findBoardKeyword ( head ) != - 1 && NodeBoardKind ( m ) == "" {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( kp . Path [ i ] . Unbox ( ) , "%s is only allowed at a board root" , head )
2023-01-17 12:44:14 +00:00
}
2023-01-09 21:26:11 +00:00
for _ , f := range m . Fields {
2023-01-18 10:06:44 +00:00
if ! strings . EqualFold ( f . Name , head ) {
2023-01-09 21:26:11 +00:00
continue
}
2023-01-18 10:06:44 +00:00
2023-01-24 11:09:40 +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-07-30 20:52:42 +00:00
faAppend ( f )
2023-06-24 22:31:58 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
2023-01-18 01:47:47 +00:00
if _ , ok := f . Composite . ( * Array ) ; ok {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( kp . Path [ i ] . Unbox ( ) , "cannot index into array" )
2023-01-09 21:26:11 +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-09 21:26:11 +00:00
}
2023-07-30 20:52:42 +00:00
return f . Map ( ) . ensureField ( i + 1 , kp , refctx , create , gctx , c , fa )
2023-01-09 21:26:11 +00:00
}
2023-07-29 23:16:05 +00:00
if ! create {
return nil
}
2023-01-09 21:26:11 +00:00
f := & Field {
parent : m ,
2023-01-18 10:06:44 +00:00
Name : head ,
2023-01-24 11:09:40 +00:00
}
// 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 ,
2023-01-24 11:09:40 +00:00
} )
2023-01-09 21:26:11 +00:00
}
m . Fields = append ( m . Fields , f )
2023-01-18 10:06:44 +00:00
if i + 1 == len ( kp . Path ) {
2023-06-24 22:31:58 +00:00
* fa = append ( * fa , f )
return nil
2023-01-09 21:26:11 +00:00
}
2023-07-30 20:52:42 +00:00
if f . Composite == nil {
f . Composite = & Map {
parent : f ,
}
2023-01-09 21:26:11 +00:00
}
2023-07-30 20:52:42 +00:00
return f . Map ( ) . ensureField ( i + 1 , kp , refctx , create , gctx , c , fa )
2023-01-09 21:26:11 +00:00
}
2023-06-26 18:57:18 +00:00
func ( m * Map ) DeleteEdge ( eid * EdgeID ) * Edge {
if eid == nil {
return nil
}
for i , e := range m . Edges {
if e . ID . Match ( eid ) {
m . Edges = append ( m . Edges [ : i ] , m . Edges [ i + 1 : ] ... )
return e
}
}
return nil
}
2023-01-22 02:52:33 +00:00
func ( m * Map ) DeleteField ( ida ... string ) * Field {
2023-01-09 21:26:11 +00:00
if len ( ida ) == 0 {
2023-01-22 02:52:33 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
s := ida [ 0 ]
rest := ida [ 1 : ]
for i , f := range m . Fields {
if ! strings . EqualFold ( f . Name , s ) {
continue
}
if len ( rest ) == 0 {
2023-06-26 18:57:18 +00:00
for _ , fr := range f . References {
2023-07-03 22:58:22 +00:00
for _ , e := range m . Edges {
2023-06-26 18:57:18 +00:00
for _ , er := range e . References {
if er . Context == fr . Context {
m . DeleteEdge ( e . ID )
break
}
}
}
}
2023-01-22 02:52:33 +00:00
m . Fields = append ( m . Fields [ : i ] , m . Fields [ i + 1 : ] ... )
2023-06-26 19:15:24 +00:00
// 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
parent := ParentField ( f )
2023-07-03 22:58:22 +00:00
for keywordHolder := range d2graph . ReservedKeywordHolders {
if parent != nil && parent . Name == keywordHolder && len ( parent . Map ( ) . Fields ) == 0 {
keywordHolderParentMap := ParentMap ( parent )
for i , f := range keywordHolderParentMap . Fields {
if f . Name == keywordHolder {
keywordHolderParentMap . Fields = append ( keywordHolderParentMap . Fields [ : i ] , keywordHolderParentMap . Fields [ i + 1 : ] ... )
2023-06-26 19:15:24 +00:00
break
}
}
}
}
2023-01-22 02:52:33 +00:00
return f
2023-01-09 21:26:11 +00:00
}
2023-01-18 15:15:16 +00:00
if f . Map ( ) != nil {
2023-01-22 02:52:33 +00:00
return f . Map ( ) . DeleteField ( rest ... )
2023-01-09 21:26:11 +00:00
}
}
2023-01-22 02:52:33 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
2023-07-29 23:16:05 +00:00
func ( m * Map ) GetEdges ( eid * EdgeID , refctx * RefContext ) [ ] * Edge {
if refctx != nil {
var ea [ ] * Edge
m . getEdges ( eid , refctx , & ea )
return ea
}
2023-02-15 05:26:29 +00:00
eid , m , common , err := eid . resolve ( m )
2023-01-17 12:44:14 +00:00
if err != nil {
return nil
}
2023-02-15 05:26:29 +00:00
if len ( common ) > 0 {
f := m . GetField ( common ... )
2023-01-16 11:52:37 +00:00
if f == nil {
return nil
2023-01-09 21:26:11 +00:00
}
2023-01-18 15:15:16 +00:00
if f . Map ( ) != nil {
2023-07-29 23:16:05 +00:00
return f . Map ( ) . GetEdges ( eid , nil )
2023-01-09 21:26:11 +00:00
}
2023-01-16 11:52:37 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
2023-01-16 11:52:37 +00:00
var ea [ ] * Edge
2023-01-09 21:26:11 +00:00
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-07-29 23:16:05 +00:00
func ( m * Map ) getEdges ( eid * EdgeID , refctx * RefContext , ea * [ ] * Edge ) error {
eid , m , common , err := eid . resolve ( m )
if err != nil {
return err
}
if len ( common ) > 0 {
commonKP := d2ast . MakeKeyPath ( common )
lastMatch := 0
for i , el := range commonKP . Path {
for j := lastMatch ; j < len ( refctx . Edge . Src . Path ) ; j ++ {
realEl := refctx . Edge . Src . Path [ j ]
if el . ScalarString ( ) == realEl . ScalarString ( ) {
commonKP . Path [ i ] = realEl
lastMatch += j + 1
}
}
}
2023-07-30 20:52:42 +00:00
fa , err := m . EnsureField ( commonKP , nil , false , nil )
2023-07-29 23:16:05 +00:00
if err != nil {
return nil
}
for _ , f := range fa {
if _ , ok := f . Composite . ( * Array ) ; ok {
return d2parser . Errorf ( refctx . Edge . Src , "cannot index into array" )
}
if f . Map ( ) == nil {
f . Composite = & Map {
parent : f ,
}
}
err = f . Map ( ) . getEdges ( eid , refctx , ea )
if err != nil {
return err
}
}
return nil
}
2023-07-30 20:52:42 +00:00
srcFA , err := refctx . ScopeMap . EnsureField ( refctx . Edge . Src , nil , false , nil )
2023-07-29 23:16:05 +00:00
if err != nil {
return err
}
2023-07-30 20:52:42 +00:00
dstFA , err := refctx . ScopeMap . EnsureField ( refctx . Edge . Dst , nil , false , nil )
2023-07-29 23:16:05 +00:00
if err != nil {
return err
}
for _ , src := range srcFA {
for _ , dst := range dstFA {
eid2 := eid . Copy ( )
eid2 . SrcPath = RelIDA ( m , src )
eid2 . DstPath = RelIDA ( m , dst )
ea2 := m . GetEdges ( eid2 , nil )
* ea = append ( * ea , ea2 ... )
}
}
return nil
}
2023-07-30 20:52:42 +00:00
func ( m * Map ) CreateEdge ( eid * EdgeID , refctx * RefContext , c * compiler ) ( [ ] * Edge , error ) {
2023-06-24 22:31:58 +00:00
var ea [ ] * Edge
2023-07-30 20:52:42 +00:00
var gctx * globContext
if refctx != nil && refctx . Key . HasGlob ( ) && c != nil {
2023-08-16 06:33:50 +00:00
gctx = c . ensureGlobContext ( refctx )
2023-07-30 20:52:42 +00:00
}
err := m . createEdge ( eid , refctx , gctx , c , & ea )
if err != nil {
return ea , err
}
if len ( ea ) > 0 && c != nil {
for _ , gctx2 := range c . globContexts ( ) {
c . compileKey ( gctx2 . refctx )
}
}
return ea , nil
2023-06-24 22:31:58 +00:00
}
2023-07-30 20:52:42 +00:00
func ( m * Map ) createEdge ( eid * EdgeID , refctx * RefContext , gctx * globContext , c * compiler , ea * [ ] * Edge ) error {
2023-01-18 15:15:16 +00:00
if ParentEdge ( m ) != nil {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( refctx . Edge , "cannot create edge inside edge" )
2023-01-18 15:15:16 +00:00
}
2023-02-15 05:26:29 +00:00
eid , m , common , err := eid . resolve ( m )
2023-01-17 12:44:14 +00:00
if err != nil {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( refctx . Edge , err . Error ( ) )
2023-01-17 12:44:14 +00:00
}
2023-02-15 05:26:29 +00:00
if len ( common ) > 0 {
2023-07-29 23:16:05 +00:00
commonKP := d2ast . MakeKeyPath ( common )
lastMatch := 0
for i , el := range commonKP . Path {
for j := lastMatch ; j < len ( refctx . Edge . Src . Path ) ; j ++ {
realEl := refctx . Edge . Src . Path [ j ]
if el . ScalarString ( ) == realEl . ScalarString ( ) {
commonKP . Path [ i ] = realEl
lastMatch += j + 1
}
}
}
2023-07-30 20:52:42 +00:00
fa , err := m . EnsureField ( commonKP , nil , true , c )
2023-01-16 11:52:37 +00:00
if err != nil {
2023-06-24 22:31:58 +00:00
return err
2023-01-16 11:52:37 +00:00
}
2023-06-24 22:31:58 +00:00
for _ , f := range fa {
if _ , ok := f . Composite . ( * Array ) ; ok {
return d2parser . Errorf ( refctx . Edge . Src , "cannot index into array" )
}
if f . Map ( ) == nil {
f . Composite = & Map {
parent : f ,
}
}
2023-07-30 20:52:42 +00:00
err = f . Map ( ) . createEdge ( eid , refctx , gctx , c , ea )
2023-06-24 22:31:58 +00:00
if err != nil {
return err
2023-01-18 01:47:47 +00:00
}
2023-01-16 11:52:37 +00:00
}
2023-06-24 22:31:58 +00:00
return nil
2023-01-09 21:26:11 +00:00
}
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-06-24 22:31:58 +00:00
return d2parser . Errorf ( refctx . Edge . Src . Path [ ij ] . Unbox ( ) , "reserved keywords are prohibited in edges" )
2023-01-28 01:19:12 +00:00
}
ij = findBoardKeyword ( eid . SrcPath ... )
if ij == len ( eid . SrcPath ) - 1 {
2023-06-24 22:31:58 +00:00
return 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
}
2023-06-24 22:31:58 +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-06-24 22:31:58 +00:00
return d2parser . Errorf ( refctx . Edge . Dst . Path [ ij ] . Unbox ( ) , "reserved keywords are prohibited in edges" )
2023-01-28 01:19:12 +00:00
}
ij = findBoardKeyword ( eid . DstPath ... )
if ij == len ( eid . DstPath ) - 1 {
2023-06-24 22:31:58 +00:00
return d2parser . Errorf ( refctx . Edge . Dst . Path [ ij ] . Unbox ( ) , "edge with board keyword alone doesn't make sense" )
}
2023-07-30 20:52:42 +00:00
srcFA , err := refctx . ScopeMap . EnsureField ( refctx . Edge . Src , refctx , true , c )
2023-06-24 22:31:58 +00:00
if err != nil {
return err
}
2023-07-30 20:52:42 +00:00
dstFA , err := refctx . ScopeMap . EnsureField ( refctx . Edge . Dst , refctx , true , c )
2023-06-24 22:31:58 +00:00
if err != nil {
return err
}
for _ , src := range srcFA {
for _ , dst := range dstFA {
2023-07-30 19:41:15 +00:00
if src == dst && ( refctx . Edge . Src . HasGlob ( ) || refctx . Edge . Dst . HasGlob ( ) ) {
2023-07-30 08:15:34 +00:00
// Globs do not make self edges.
continue
}
2023-07-30 20:52:42 +00:00
if refctx . Edge . Src . HasMultiGlob ( ) {
2023-07-30 19:41:15 +00:00
// If src has a double glob we only select leafs, those without children.
if src . Map ( ) . IsContainer ( ) {
continue
}
2023-07-30 20:52:42 +00:00
if NodeBoardKind ( src ) != "" || ParentBoard ( src ) != ParentBoard ( dst ) {
2023-07-30 19:41:15 +00:00
continue
}
}
2023-07-30 20:52:42 +00:00
if refctx . Edge . Dst . HasMultiGlob ( ) {
2023-07-30 19:41:15 +00:00
// If dst has a double glob we only select leafs, those without children.
if dst . Map ( ) . IsContainer ( ) {
2023-07-30 08:15:34 +00:00
continue
}
2023-07-30 20:52:42 +00:00
if NodeBoardKind ( dst ) != "" || ParentBoard ( src ) != ParentBoard ( dst ) {
2023-07-30 10:15:33 +00:00
continue
}
2023-07-30 08:15:34 +00:00
}
2023-06-24 22:31:58 +00:00
eid2 := eid . Copy ( )
eid2 . SrcPath = RelIDA ( m , src )
eid2 . DstPath = RelIDA ( m , dst )
2023-07-30 20:52:42 +00:00
e , err := m . createEdge2 ( eid2 , refctx , gctx , src , dst )
2023-06-24 22:31:58 +00:00
if err != nil {
return err
}
2023-07-30 20:52:42 +00:00
if e != nil {
* ea = append ( * ea , e )
}
2023-06-24 22:31:58 +00:00
}
}
return nil
}
2023-07-30 20:52:42 +00:00
func ( m * Map ) createEdge2 ( eid * EdgeID , refctx * RefContext , gctx * globContext , src , dst * Field ) ( * Edge , error ) {
2023-06-24 22:31:58 +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
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
2023-07-29 23:16:05 +00:00
eid . Glob = true
2023-07-30 19:41:15 +00:00
ea := m . GetEdges ( eid , nil )
2023-01-16 11:52:37 +00:00
index := len ( ea )
eid . Index = & index
2023-07-29 23:16:05 +00:00
eid . Glob = false
2023-01-16 11:52:37 +00:00
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
}
2023-07-30 20:52:42 +00:00
if gctx != nil {
2023-08-16 01:39:14 +00:00
ks := d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( e ) ) )
2023-07-30 20:52:42 +00:00
if _ , ok := gctx . appliedEdges [ ks ] ; ok {
return nil , nil
}
gctx . appliedEdges [ ks ] = struct { } { }
}
2023-01-16 11:52:37 +00:00
m . Edges = append ( m . Edges , e )
return e , nil
2023-01-09 21:26:11 +00:00
}
2023-01-11 19:05:50 +00:00
2023-06-16 23:23:57 +00:00
func ( s * Scalar ) AST ( ) d2ast . Node {
2023-01-16 13:31:18 +00:00
return s . Value
}
2023-06-16 23:23:57 +00:00
func ( f * Field ) AST ( ) d2ast . Node {
2023-01-16 13:31:18 +00:00
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-16 13:31:18 +00:00
2023-01-18 15:15:16 +00:00
if f . Primary_ != nil {
2023-06-16 23:23:57 +00:00
k . Primary = d2ast . MakeValueBox ( f . Primary_ . AST ( ) . ( d2ast . Value ) ) . ScalarBox ( )
2023-01-16 13:31:18 +00:00
}
if f . Composite != nil {
2023-06-16 23:23:57 +00:00
k . Value = d2ast . MakeValueBox ( f . Composite . AST ( ) . ( d2ast . Value ) )
2023-01-16 13:31:18 +00:00
}
return k
2023-01-11 19:05:50 +00:00
}
2023-01-16 11:52:37 +00:00
2023-06-16 23:23:57 +00:00
func ( e * Edge ) AST ( ) d2ast . Node {
2023-01-16 13:31:18 +00:00
astEdge := & d2ast . Edge { }
astEdge . Src = d2ast . MakeKeyPath ( e . ID . SrcPath )
if e . ID . SrcArrow {
astEdge . SrcArrow = "<"
2023-01-16 11:52:37 +00:00
}
2023-01-16 13:31:18 +00:00
astEdge . Dst = d2ast . MakeKeyPath ( e . ID . DstPath )
if e . ID . DstArrow {
astEdge . DstArrow = ">"
2023-01-16 11:52:37 +00:00
}
2023-01-16 13:31:18 +00:00
k := & d2ast . Key {
Edges : [ ] * d2ast . Edge { astEdge } ,
}
2023-01-18 15:15:16 +00:00
if e . Primary_ != nil {
2023-06-16 23:23:57 +00:00
k . Primary = d2ast . MakeValueBox ( e . Primary_ . AST ( ) . ( d2ast . Value ) ) . ScalarBox ( )
2023-01-16 13:31:18 +00:00
}
2023-01-18 15:15:16 +00:00
if e . Map_ != nil {
2023-06-16 23:23:57 +00:00
k . Value = d2ast . MakeValueBox ( e . Map_ . AST ( ) . ( * d2ast . Map ) )
2023-01-16 13:31:18 +00:00
}
return k
}
2023-06-16 23:23:57 +00:00
func ( a * Array ) AST ( ) d2ast . Node {
2023-01-16 13:31:18 +00:00
if a == nil {
return nil
}
astArray := & d2ast . Array { }
for _ , av := range a . Values {
2023-06-16 23:23:57 +00:00
astArray . Nodes = append ( astArray . Nodes , d2ast . MakeArrayNodeBox ( av . AST ( ) . ( d2ast . ArrayNode ) ) )
2023-01-16 13:31:18 +00:00
}
return astArray
}
2023-06-16 23:23:57 +00:00
func ( m * Map ) AST ( ) d2ast . Node {
2023-01-16 13:31:18 +00:00
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" )
2023-01-16 13:31:18 +00:00
}
for _ , f := range m . Fields {
2023-06-16 23:23:57 +00:00
astMap . Nodes = append ( astMap . Nodes , d2ast . MakeMapNodeBox ( f . AST ( ) . ( d2ast . MapNode ) ) )
2023-01-16 13:31:18 +00:00
}
for _ , e := range m . Edges {
2023-06-16 23:23:57 +00:00
astMap . Nodes = append ( astMap . Nodes , d2ast . MakeMapNodeBox ( e . AST ( ) . ( d2ast . MapNode ) ) )
2023-01-16 13:31:18 +00:00
}
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
}
}
2023-02-06 21:32:08 +00:00
func RootMap ( m * Map ) * Map {
if m . Root ( ) {
return m
}
return RootMap ( ParentMap ( m ) )
}
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-07-13 17:22:37 +00:00
func IsVar ( n Node ) bool {
for {
if n == nil {
return false
}
if NodeBoardKind ( n ) != "" {
return false
}
if f , ok := n . ( * Field ) ; ok && f . Name == "vars" {
return true
}
n = n . Parent ( )
}
}
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
2023-03-03 01:56:32 +00:00
// BoardIDA returns the absolute path to n from the nearest board root.
func BoardIDA ( n Node ) ( ida [ ] string ) {
2023-01-24 05:48:43 +00:00
for {
2023-08-16 01:39:14 +00:00
switch n := n . ( type ) {
case * Field :
if n . Root ( ) || NodeBoardKind ( n ) != "" {
2023-01-24 11:09:40 +00:00
reverseIDA ( ida )
2023-01-24 05:48:43 +00:00
return ida
}
2023-08-16 01:39:14 +00:00
ida = append ( ida , n . Name )
case * Edge :
ida = append ( ida , n . String ( ) )
2023-01-24 05:48:43 +00:00
}
2023-08-16 01:39:14 +00:00
n = n . Parent ( )
if n == nil {
2023-01-24 11:09:40 +00:00
reverseIDA ( ida )
2023-01-24 05:48:43 +00:00
return ida
}
}
}
2023-01-24 11:09:40 +00:00
2023-03-03 02:25:14 +00:00
// IDA returns the absolute path to n.
2023-03-03 01:56:32 +00:00
func IDA ( n Node ) ( ida [ ] string ) {
for {
2023-08-16 01:39:14 +00:00
switch n := n . ( type ) {
case * Field :
ida = append ( ida , n . Name )
if n . Root ( ) {
2023-03-03 01:56:32 +00:00
reverseIDA ( ida )
return ida
}
2023-08-16 01:39:14 +00:00
case * Edge :
ida = append ( ida , n . String ( ) )
2023-03-03 01:56:32 +00:00
}
2023-08-16 01:39:14 +00:00
n = n . Parent ( )
if n == nil {
2023-03-03 01:56:32 +00:00
reverseIDA ( ida )
return ida
}
}
}
2023-07-30 19:41:15 +00:00
// RelIDA returns the path to n relative to p.
2023-06-24 22:31:58 +00:00
func RelIDA ( p , n Node ) ( ida [ ] string ) {
for {
2023-08-16 01:39:14 +00:00
switch n := n . ( type ) {
case * Field :
ida = append ( ida , n . Name )
if n . Root ( ) {
2023-06-24 22:31:58 +00:00
reverseIDA ( ida )
return ida
}
2023-08-16 01:39:14 +00:00
case * Edge :
ida = append ( ida , n . String ( ) )
2023-06-24 22:31:58 +00:00
}
2023-08-16 06:33:50 +00:00
n = n . Parent ( )
2023-08-16 01:39:14 +00:00
f , fok := n . ( * Field )
e , eok := n . ( * Edge )
if n == nil || ( fok && ( f . Root ( ) || f == p || f . Composite == p ) ) || ( eok && ( e == p || e . Map_ == p ) ) {
2023-06-24 22:31:58 +00:00
reverseIDA ( ida )
return ida
}
}
}
2023-01-24 11:09:40 +00:00
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
}
}
2023-02-27 21:34:03 +00:00
func ( f * Field ) Equal ( n2 Node ) bool {
f2 := n2 . ( * Field )
if f . Name != f2 . Name {
return false
}
if ! f . Primary_ . Equal ( f2 . Primary_ ) {
return false
}
if ! f . Composite . Equal ( f2 . Composite ) {
return false
}
return true
}
func ( e * Edge ) Equal ( n2 Node ) bool {
e2 := n2 . ( * Edge )
if ! e . ID . Match ( e2 . ID ) {
return false
}
if ! e . Primary_ . Equal ( e2 . Primary_ ) {
return false
}
if ! e . Map_ . Equal ( e2 . Map_ ) {
return false
}
return true
}
func ( a * Array ) Equal ( n2 Node ) bool {
a2 := n2 . ( * Array )
if len ( a . Values ) != len ( a2 . Values ) {
return false
}
for i := range a . Values {
if ! a . Values [ i ] . Equal ( a2 . Values [ i ] ) {
return false
}
}
return true
}
func ( m * Map ) Equal ( n2 Node ) bool {
m2 := n2 . ( * Map )
if len ( m . Fields ) != len ( m2 . Fields ) {
return false
}
if len ( m . Edges ) != len ( m2 . Edges ) {
return false
}
for i := range m . Fields {
if ! m . Fields [ i ] . Equal ( m2 . Fields [ i ] ) {
return false
}
}
for i := range m . Edges {
if ! m . Edges [ i ] . Equal ( m2 . Edges [ i ] ) {
return false
}
}
return true
}
2023-04-18 01:55:43 +00:00
func ( m * Map ) InClass ( key * d2ast . Key ) bool {
classes := m . Map ( ) . GetField ( "classes" )
if classes == nil || classes . Map ( ) == nil {
return false
}
for _ , class := range classes . Map ( ) . Fields {
if class . Map ( ) == nil {
continue
}
classF := class . Map ( ) . GetField ( key . Key . IDA ( ) ... )
if classF == nil {
continue
}
for _ , ref := range classF . References {
if ref . Context . Key == key {
return true
}
}
}
return false
}
2023-04-26 19:38:41 +00:00
func ( m * Map ) IsClass ( ) bool {
parentBoard := ParentBoard ( m )
if parentBoard . Map ( ) == nil {
return false
}
classes := parentBoard . Map ( ) . GetField ( "classes" )
if classes == nil || classes . Map ( ) == nil {
return false
}
for _ , class := range classes . Map ( ) . Fields {
if class . Map ( ) == m {
return true
}
}
return false
}