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-18 10:06:44 +00:00
"oss.terrastruct.com/d2/d2parser"
2023-08-20 04:20:32 +00:00
"oss.terrastruct.com/d2/d2target"
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
2023-08-17 21:10:09 +00:00
LastPrimaryRef ( ) Reference
2023-01-22 08:21:21 +00:00
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
2023-08-17 21:10:09 +00:00
func ( n * Scalar ) LastPrimaryRef ( ) Reference { return parentPrimaryRef ( n ) }
func ( n * Map ) LastPrimaryRef ( ) Reference { return parentPrimaryRef ( n ) }
func ( n * Array ) LastPrimaryRef ( ) Reference { return parentPrimaryRef ( 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-08-17 21:10:09 +00:00
Context ( ) * RefContext
// Result of a glob in Context or from above.
DueToGlob ( ) bool
2023-08-20 04:20:32 +00:00
DueToLazyGlob ( ) bool
2023-01-22 08:59:02 +00:00
}
var _ Reference = & FieldReference { }
var _ Reference = & EdgeReference { }
2023-08-17 21:10:09 +00:00
func ( r * FieldReference ) reference ( ) { }
func ( r * EdgeReference ) reference ( ) { }
func ( r * FieldReference ) Context ( ) * RefContext { return r . Context_ }
func ( r * EdgeReference ) Context ( ) * RefContext { return r . Context_ }
func ( r * FieldReference ) DueToGlob ( ) bool { return r . DueToGlob_ }
func ( r * EdgeReference ) DueToGlob ( ) bool { return r . DueToGlob_ }
2023-08-20 04:20:32 +00:00
func ( r * FieldReference ) DueToLazyGlob ( ) bool { return r . DueToLazyGlob_ }
func ( r * EdgeReference ) DueToLazyGlob ( ) bool { return r . DueToLazyGlob_ }
2023-01-22 08:59:02 +00:00
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 {
2024-10-19 05:56:00 +00:00
parent Node
importAST d2ast . Node
Fields [ ] * Field ` json:"fields" `
Edges [ ] * Edge ` json:"edges" `
2023-09-04 05:51:38 +00:00
globs [ ] * globContext
2023-01-09 21:26:11 +00:00
}
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 { {
2023-08-17 21:10:09 +00:00
Context_ : & RefContext {
2023-01-24 06:45:21 +00:00
ScopeMap : m ,
} ,
} } ,
}
}
2024-10-19 05:56:00 +00:00
func ( m * Map ) ImportAST ( ) d2ast . Node {
return m . importAST
}
func ( m * Map ) SetImportAST ( node d2ast . Node ) {
m . importAST = node
for _ , f := range m . Fields {
f . SetImportAST ( node )
}
for _ , e := range m . Edges {
e . SetImportAST ( node )
}
}
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 :
2024-09-25 03:41:47 +00:00
if n == nil {
return ""
}
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
}
2024-10-19 05:56:00 +00:00
type Importable interface {
ImportAST ( ) d2ast . Node
SetImportAST ( d2ast . Node )
}
var _ Importable = & Edge { }
var _ Importable = & Field { }
var _ Importable = & Map { }
2023-01-09 21:26:11 +00:00
type Field struct {
2023-01-22 09:08:13 +00:00
// *Map.
2024-10-19 05:56:00 +00:00
parent Node
importAST d2ast . 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
}
2024-10-19 05:56:00 +00:00
func ( f * Field ) ImportAST ( ) d2ast . Node {
return f . importAST
}
func ( f * Field ) SetImportAST ( node d2ast . Node ) {
f . importAST = node
if f . Map ( ) != nil {
f . Map ( ) . SetImportAST ( node )
}
}
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-08-17 21:10:09 +00:00
func ( f * Field ) LastPrimaryRef ( ) Reference {
for i := len ( f . References ) - 1 ; i >= 0 ; i -- {
2023-10-31 19:26:01 +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-08-17 21:10:09 +00:00
fr := f . LastPrimaryRef ( )
2023-01-22 08:21:21 +00:00
if fr == nil {
return nil
}
2023-08-17 21:10:09 +00:00
return fr . ( * FieldReference ) . Context_ . Key
2023-01-22 08:21:21 +00:00
}
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 {
2024-08-06 00:34:41 +00:00
if ! strings . EqualFold ( eid . SrcPath [ 0 ] , eid . DstPath [ 0 ] ) || strings . Contains ( eid . SrcPath [ 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
2024-10-19 05:56:00 +00:00
parent Node
importAST d2ast . 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
}
2024-10-19 05:56:00 +00:00
func ( e * Edge ) ImportAST ( ) d2ast . Node {
return e . importAST
}
func ( e * Edge ) SetImportAST ( node d2ast . Node ) {
e . importAST = node
if e . Map ( ) != nil {
e . Map ( ) . SetImportAST ( node )
}
}
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-08-17 21:10:09 +00:00
func ( e * Edge ) LastPrimaryRef ( ) Reference {
for i := len ( e . References ) - 1 ; i >= 0 ; i -- {
fr := e . References [ i ]
2023-08-20 04:20:32 +00:00
if fr . Context_ . Key . EdgeKey == nil && ! fr . DueToLazyGlob ( ) {
2023-01-22 08:21:21 +00:00
return fr
}
}
return nil
}
func ( e * Edge ) LastPrimaryKey ( ) * d2ast . Key {
2023-08-17 21:10:09 +00:00
er := e . LastPrimaryRef ( )
2023-01-22 08:59:02 +00:00
if er == nil {
2023-01-22 08:21:21 +00:00
return nil
}
2023-08-17 21:10:09 +00:00
return er . ( * EdgeReference ) . 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-08-20 04:20:32 +00:00
Context_ * RefContext ` json:"context" `
DueToGlob_ bool ` json:"due_to_glob" `
DueToLazyGlob_ bool ` json:"due_to_lazy_glob" `
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-08-17 21:10:09 +00:00
if fr . KeyPath == fr . Context_ . Key . Key {
return len ( fr . Context_ . Key . Edges ) == 0 && fr . KeyPathIndex ( ) == len ( fr . KeyPath . Path ) - 1
} else if fr . KeyPath == fr . Context_ . Key . EdgeKey {
return len ( fr . Context_ . Key . Edges ) == 1 && fr . KeyPathIndex ( ) == len ( fr . KeyPath . Path ) - 1
2023-01-24 05:48:43 +00:00
}
return false
}
2023-01-22 08:59:02 +00:00
func ( fr * FieldReference ) KeyPathIndex ( ) int {
for i , sb := range fr . KeyPath . Path {
if sb . Unbox ( ) == fr . String {
2023-01-16 15:45:13 +00:00
return i
}
}
panic ( "d2ir.KeyReference.KeyPathIndex: String not in KeyPath?" )
}
2023-01-22 08:59:02 +00:00
func ( fr * FieldReference ) EdgeDest ( ) bool {
2023-08-17 21:10:09 +00:00
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 {
2023-08-17 21:10:09 +00:00
return fr . Context_ . Edge != nil
2023-01-22 08:59:02 +00:00
}
func ( fr * FieldReference ) AST ( ) d2ast . Node {
if fr . String == nil {
// Root map.
2023-08-17 21:10:09 +00:00
return fr . Context_ . Scope
2023-01-22 08:59:02 +00:00
}
return fr . String
2023-01-09 21:26:11 +00:00
}
type EdgeReference struct {
2023-08-20 04:20:32 +00:00
Context_ * RefContext ` json:"context" `
DueToGlob_ bool ` json:"due_to_glob" `
DueToLazyGlob_ bool ` json:"due_to_lazy_glob" `
2023-01-09 21:26:11 +00:00
}
2023-01-22 08:59:02 +00:00
func ( er * EdgeReference ) AST ( ) d2ast . Node {
2023-08-17 21:10:09 +00:00
return er . Context_ . Edge
2023-01-22 08:59:02 +00:00
}
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 {
2023-08-17 21:10:09 +00:00
return len ( er . Context_ . Key . Edges ) == 1 && er . Context_ . Key . EdgeKey == nil
2023-01-24 11:09:40 +00:00
}
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.
2023-08-16 07:02:09 +00:00
// Same with ScopeMap.
return rc . Key . Equals ( rc2 . Key ) && rc . Scope == rc2 . Scope && 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 {
2024-09-15 16:43:10 +00:00
_ , isReserved := d2ast . ReservedKeywords [ f . Name ]
2023-07-30 08:15:34 +00:00
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 )
2023-08-30 12:08:22 +00:00
if len ( fa ) > 0 && c != nil && len ( c . globRefContextStack ) == 0 {
for _ , gctx2 := range c . globContexts ( ) {
old := c . lazyGlobBeingApplied
c . lazyGlobBeingApplied = true
c . compileKey ( gctx2 . refctx )
c . lazyGlobBeingApplied = old
}
}
2023-08-16 22:34:48 +00:00
return fa , err
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 {
2023-08-16 07:02:09 +00:00
filter := func ( f * Field , passthrough bool ) bool {
if gctx != nil {
2023-08-16 22:34:48 +00:00
var ks string
if refctx . Key . HasTripleGlob ( ) {
ks = d2format . Format ( d2ast . MakeKeyPath ( IDA ( f ) ) )
} else {
ks = d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( f ) ) )
}
2023-08-16 07:02:09 +00:00
if ! kp . HasGlob ( ) {
2023-08-16 09:21:33 +00:00
if ! passthrough {
gctx . appliedFields [ ks ] = struct { } { }
}
2023-08-16 07:02:09 +00:00
return true
}
2023-08-20 04:20:32 +00:00
// 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.
2023-08-16 07:02:09 +00:00
lastEl := kp . Path [ len ( kp . Path ) - 1 ]
if len ( refctx . Key . Edges ) == 0 || lastEl . UnquotedString == nil || len ( lastEl . UnquotedString . Pattern ) == 0 || kp == refctx . Key . Key || kp == refctx . Key . EdgeKey {
if _ , ok := gctx . appliedFields [ ks ] ; ok {
return false
}
2023-08-16 09:21:33 +00:00
}
if ! passthrough {
gctx . appliedFields [ ks ] = struct { } { }
2023-07-30 20:52:42 +00:00
}
2023-08-16 07:02:09 +00:00
}
return true
}
faAppend := func ( fa2 ... * Field ) {
for _ , f := range fa2 {
if filter ( f , false ) {
* fa = append ( * fa , f )
}
2023-07-30 20:52:42 +00:00
}
}
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 {
2023-08-16 07:02:09 +00:00
if ! filter ( f , true ) {
2023-08-16 09:21:33 +00:00
continue
2023-08-16 07:02:09 +00:00
}
2023-07-27 09:36:33 +00:00
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 {
2023-08-16 07:02:09 +00:00
if ! filter ( f , true ) {
2023-08-16 09:21:33 +00:00
continue
2023-08-16 07:02:09 +00:00
}
2023-07-27 09:36:33 +00:00
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
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . ReservedKeywords [ strings . ToLower ( head ) ] ; ok {
2023-03-05 17:43:42 +00:00
head = strings . ToLower ( head )
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . 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 {
2023-08-20 04:20:32 +00:00
String : kp . Path [ i ] . Unbox ( ) ,
KeyPath : kp ,
Context_ : refctx ,
DueToGlob_ : len ( c . globRefContextStack ) > 0 ,
DueToLazyGlob_ : c . lazyGlobBeingApplied ,
2023-01-24 11:09:40 +00:00
} )
}
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-08-16 07:02:09 +00:00
if ! filter ( f , true ) {
return nil
}
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-08-20 04:20:32 +00:00
shape := ParentShape ( m )
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . ReservedKeywords [ strings . ToLower ( head ) ] ; ! ok && len ( c . globRefContextStack ) > 0 {
2023-08-20 04:20:32 +00:00
if shape == d2target . ShapeClass || shape == d2target . ShapeSQLTable {
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
}
2023-08-20 04:20:32 +00:00
defer func ( ) {
if i < kp . FirstGlob ( ) {
return
}
for _ , grefctx := range c . globRefContextStack {
var ks string
if grefctx . Key . HasTripleGlob ( ) {
ks = d2format . Format ( d2ast . MakeKeyPath ( IDA ( f ) ) )
} else {
ks = d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( f ) ) )
}
gctx2 := c . getGlobContext ( grefctx )
gctx2 . appliedFields [ ks ] = struct { } { }
}
} ( )
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-08-20 04:20:32 +00:00
String : kp . Path [ i ] . Unbox ( ) ,
KeyPath : kp ,
Context_ : refctx ,
DueToGlob_ : len ( c . globRefContextStack ) > 0 ,
DueToLazyGlob_ : c . lazyGlobBeingApplied ,
2023-01-24 11:09:40 +00:00
} )
2023-01-09 21:26:11 +00:00
}
2023-08-20 20:14:13 +00:00
if ! filter ( f , true ) {
return nil
}
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-08-16 07:02:09 +00:00
faAppend ( f )
return nil
}
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 {
2024-02-09 06:21:56 +00:00
currM := m
for currM != nil {
for _ , e := range currM . Edges {
for _ , er := range e . References {
if er . Context_ == fr . Context_ {
currM . DeleteEdge ( e . ID )
break
}
2023-06-26 18:57:18 +00:00
}
}
2024-02-09 06:21:56 +00:00
if NodeBoardKind ( currM ) != "" {
break
}
currM = ParentMap ( currM )
2023-06-26 18:57:18 +00:00
}
}
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 )
2024-09-15 16:43:10 +00:00
for keywordHolder := range d2ast . ReservedKeywordHolders {
2023-07-03 22:58:22 +00:00
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-08-17 02:17:41 +00:00
func ( m * Map ) GetEdges ( eid * EdgeID , refctx * RefContext , c * compiler ) [ ] * Edge {
2023-07-29 23:16:05 +00:00
if refctx != nil {
2023-08-17 02:17:41 +00:00
var gctx * globContext
if refctx . Key . HasGlob ( ) && c != nil {
gctx = c . ensureGlobContext ( refctx )
}
2023-07-29 23:16:05 +00:00
var ea [ ] * Edge
2023-08-17 02:17:41 +00:00
m . getEdges ( eid , refctx , gctx , & ea )
2023-07-29 23:16:05 +00:00
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-08-17 02:17:41 +00:00
return f . Map ( ) . GetEdges ( eid , nil , 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-08-17 02:17:41 +00:00
func ( m * Map ) getEdges ( eid * EdgeID , refctx * RefContext , gctx * globContext , ea * [ ] * Edge ) error {
2023-07-29 23:16:05 +00:00
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 ,
}
}
2023-08-17 02:17:41 +00:00
err = f . Map ( ) . getEdges ( eid , refctx , gctx , ea )
2023-07-29 23:16:05 +00:00
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 )
2023-08-17 02:17:41 +00:00
ea2 := m . GetEdges ( eid2 , nil , nil )
for _ , e := range ea2 {
if gctx != nil {
var ks string
if refctx . Key . HasTripleGlob ( ) {
ks = d2format . Format ( d2ast . MakeKeyPath ( IDA ( e ) ) )
} else {
ks = d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( e ) ) )
}
if _ , ok := gctx . appliedEdges [ ks ] ; ok {
continue
}
gctx . appliedEdges [ ks ] = struct { } { }
}
2024-03-05 01:51:05 +00:00
* ea = append ( * ea , e )
2023-08-17 02:17:41 +00:00
}
2023-07-29 23:16:05 +00:00
}
}
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 )
2023-08-30 12:08:22 +00:00
if len ( ea ) > 0 && c != nil && len ( c . globRefContextStack ) == 0 {
for _ , gctx2 := range c . globContexts ( ) {
old := c . lazyGlobBeingApplied
c . lazyGlobBeingApplied = true
c . compileKey ( gctx2 . refctx )
c . lazyGlobBeingApplied = old
}
}
2023-08-16 22:34:48 +00:00
return ea , err
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
2024-08-06 00:34:41 +00:00
es , err := m . createEdge2 ( eid2 , refctx , gctx , c , src , dst )
2023-06-24 22:31:58 +00:00
if err != nil {
return err
}
2024-08-06 00:34:41 +00:00
for _ , e := range es {
2023-07-30 20:52:42 +00:00
* ea = append ( * ea , e )
}
2023-06-24 22:31:58 +00:00
}
}
return nil
}
2024-08-06 00:34:41 +00:00
func ( m * Map ) createEdge2 ( eid * EdgeID , refctx * RefContext , gctx * globContext , c * compiler , 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
}
2024-08-06 00:34:41 +00:00
eid , m , common , err := eid . resolve ( m )
if err != nil {
return nil , d2parser . Errorf ( refctx . Edge , err . Error ( ) )
}
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
}
}
}
fa , err := m . EnsureField ( commonKP , nil , true , c )
if err != nil {
return nil , err
}
var edges [ ] * Edge
for _ , f := range fa {
if _ , ok := f . Composite . ( * Array ) ; ok {
return nil , d2parser . Errorf ( refctx . Edge . Src , "cannot index into array" )
}
if f . Map ( ) == nil {
f . Composite = & Map {
parent : f ,
}
}
edges2 , err := f . Map ( ) . createEdge2 ( eid , refctx , gctx , c , src , dst )
if err != nil {
return nil , err
}
edges = append ( edges , edges2 ... )
}
return edges , nil
}
2023-01-16 11:52:37 +00:00
eid . Index = nil
2023-07-29 23:16:05 +00:00
eid . Glob = true
2023-08-17 02:17:41 +00:00
ea := m . GetEdges ( eid , nil , 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-08-20 04:20:32 +00:00
Context_ : refctx ,
DueToGlob_ : len ( c . globRefContextStack ) > 0 ,
DueToLazyGlob_ : c . lazyGlobBeingApplied ,
2023-01-18 10:06:44 +00:00
} } ,
2023-01-16 11:52:37 +00:00
}
2023-07-30 20:52:42 +00:00
if gctx != nil {
2023-08-16 22:34:48 +00:00
var ks string
2023-08-18 16:35:55 +00:00
// We only ever want to create one of the edge per glob so we filter without the edge index.
e2 := e . Copy ( e . Parent ( ) ) . ( * Edge )
e2 . ID = e2 . ID . Copy ( )
e2 . ID . Index = nil
2023-08-16 22:34:48 +00:00
if refctx . Key . HasTripleGlob ( ) {
2023-08-18 16:35:55 +00:00
ks = d2format . Format ( d2ast . MakeKeyPath ( IDA ( e2 ) ) )
2023-08-16 22:34:48 +00:00
} else {
2023-08-18 16:35:55 +00:00
ks = d2format . Format ( d2ast . MakeKeyPath ( BoardIDA ( e2 ) ) )
2023-08-16 22:34:48 +00:00
}
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 )
2024-08-06 00:34:41 +00:00
return [ ] * Edge { 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-08-17 02:17:41 +00:00
func ( e * Edge ) IDString ( ) string {
ast := e . AST ( ) . ( * d2ast . Key )
2023-08-18 16:35:55 +00:00
if e . ID . Index != nil {
ast . EdgeIndex = & d2ast . EdgeIndex {
Int : e . ID . Index ,
}
}
2023-08-17 02:17:41 +00:00
ast . Primary = d2ast . ScalarBox { }
ast . Value = d2ast . ValueBox { }
return d2format . Format ( ast )
}
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
}
2024-05-29 17:39:03 +00:00
astMap := & d2ast . Map {
Range : d2ast . MakeRange ( ",0:0:0-1: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-08-17 21:10:09 +00:00
func ( m * Map ) appendFieldReferences ( i int , kp * d2ast . KeyPath , refctx * RefContext , c * compiler ) {
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-08-20 04:20:32 +00:00
String : sb . Unbox ( ) ,
KeyPath : kp ,
Context_ : refctx ,
DueToGlob_ : len ( c . globRefContextStack ) > 0 ,
DueToLazyGlob_ : c . lazyGlobBeingApplied ,
2023-01-16 15:45:13 +00:00
} )
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 {
2023-08-17 21:10:09 +00:00
f . Map ( ) . appendFieldReferences ( i + 1 , kp , refctx , c )
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
}
2024-09-25 03:41:47 +00:00
if n == ( * Map ) ( nil ) {
return false
}
2023-07-13 17:22:37 +00:00
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-08-20 04:20:32 +00:00
func ParentShape ( n Node ) string {
for {
f , ok := n . ( * Field )
if ok {
if f . Map ( ) != nil {
shapef := f . Map ( ) . GetField ( "shape" )
if shapef != nil && shapef . Primary ( ) != nil {
return shapef . Primary ( ) . Value . ScalarString ( )
}
}
}
n = n . Parent ( )
if n == nil {
return ""
}
}
}
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 {
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . BoardKeywords [ ida [ i ] ] ; ok {
2023-01-28 01:19:12 +00:00
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 {
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . SimpleReservedKeywords [ ida [ i ] ] ; ok {
2023-01-28 01:19:12 +00:00
return i
}
2024-09-15 16:43:10 +00:00
if _ , ok := d2ast . 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
}
2023-08-17 21:10:09 +00:00
func parentPrimaryRef ( n Node ) Reference {
f := ParentField ( n )
if f != nil {
return f . LastPrimaryRef ( )
}
e := ParentEdge ( n )
if e != nil {
return e . LastPrimaryRef ( )
}
return nil
}
2023-01-22 08:59:02 +00:00
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 :
2023-08-17 02:17:41 +00:00
ida = append ( ida , n . IDString ( ) )
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 :
2023-08-17 02:17:41 +00:00
ida = append ( ida , n . IDString ( ) )
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 {
2023-08-17 21:10:09 +00:00
if ref . Context_ . Key == key {
2023-04-18 01:55:43 +00:00
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
}
2024-10-12 00:42:23 +00:00
func ( m * Map ) FindBoardRoot ( path [ ] string ) * Map {
if m == nil {
return nil
}
if len ( path ) == 0 {
return m
}
layersf := m . GetField ( "layers" )
scenariosf := m . GetField ( "scenarios" )
stepsf := m . GetField ( "steps" )
if layersf != nil && layersf . Map ( ) != nil {
for _ , f := range layersf . Map ( ) . Fields {
if f . Name == path [ 0 ] {
if len ( path ) == 1 {
return f . Map ( )
}
2024-10-30 01:08:22 +00:00
return f . Map ( ) . FindBoardRoot ( path [ 1 : ] )
2024-10-12 00:42:23 +00:00
}
}
}
if scenariosf != nil && scenariosf . Map ( ) != nil {
for _ , f := range scenariosf . Map ( ) . Fields {
if f . Name == path [ 0 ] {
if len ( path ) == 1 {
return f . Map ( )
}
2024-10-30 01:08:22 +00:00
return f . Map ( ) . FindBoardRoot ( path [ 1 : ] )
2024-10-12 00:42:23 +00:00
}
}
}
if stepsf != nil && stepsf . Map ( ) != nil {
for _ , f := range stepsf . Map ( ) . Fields {
if f . Name == path [ 0 ] {
if len ( path ) == 1 {
return f . Map ( )
}
2024-10-30 01:08:22 +00:00
return f . Map ( ) . FindBoardRoot ( path [ 1 : ] )
2024-10-12 00:42:23 +00:00
}
}
}
return nil
}