1 package d2ir
2
3 import (
4 "io/fs"
5 "strconv"
6 "strings"
7
8 "oss.terrastruct.com/util-go/go2"
9
10 "oss.terrastruct.com/d2/d2ast"
11 "oss.terrastruct.com/d2/d2format"
12 "oss.terrastruct.com/d2/d2parser"
13 "oss.terrastruct.com/d2/d2themes"
14 "oss.terrastruct.com/d2/d2themes/d2themescatalog"
15 )
16
17 type globContext struct {
18 root *globContext
19 refctx *RefContext
20
21
22 appliedFields map[string]struct{}
23
24 appliedEdges map[string]struct{}
25 }
26
27 type compiler struct {
28 err *d2parser.ParseError
29
30 fs fs.FS
31
32 importStack []string
33
34 importCache map[string]*Map
35 utf16Pos bool
36
37
38 globContextStack [][]*globContext
39
40 globRefContextStack []*RefContext
41
42 mapRefContextStack []*RefContext
43 lazyGlobBeingApplied bool
44 }
45
46 type CompileOptions struct {
47 UTF16Pos bool
48
49 FS fs.FS
50 }
51
52 func (c *compiler) errorf(n d2ast.Node, f string, v ...interface{}) {
53 c.err.Errors = append(c.err.Errors, d2parser.Errorf(n, f, v...).(d2ast.Error))
54 }
55
56 func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, error) {
57 if opts == nil {
58 opts = &CompileOptions{}
59 }
60 c := &compiler{
61 err: &d2parser.ParseError{},
62 fs: opts.FS,
63
64 importCache: make(map[string]*Map),
65 utf16Pos: opts.UTF16Pos,
66 }
67 m := &Map{}
68 m.initRoot()
69 m.parent.(*Field).References[0].Context_.Scope = ast
70 m.parent.(*Field).References[0].Context_.ScopeAST = ast
71
72 c.pushImportStack(&d2ast.Import{
73 Path: []*d2ast.StringBox{d2ast.RawStringBox(ast.GetRange().Path, true)},
74 })
75 defer c.popImportStack()
76
77 c.compileMap(m, ast, ast)
78 c.compileSubstitutions(m, nil)
79 c.overlayClasses(m)
80 if !c.err.Empty() {
81 return nil, c.err
82 }
83 return m, nil
84 }
85
86 func (c *compiler) overlayClasses(m *Map) {
87 classes := m.GetField("classes")
88 if classes == nil || classes.Map() == nil {
89 return
90 }
91
92 layersField := m.GetField("layers")
93 if layersField == nil {
94 return
95 }
96 layers := layersField.Map()
97 if layers == nil {
98 return
99 }
100
101 for _, lf := range layers.Fields {
102 if lf.Map() == nil || lf.Primary() != nil {
103 c.errorf(lf.References[0].Context_.Key, "invalid layer")
104 continue
105 }
106 l := lf.Map()
107 lClasses := l.GetField("classes")
108
109 if lClasses == nil {
110 lClasses = classes.Copy(l).(*Field)
111 l.Fields = append(l.Fields, lClasses)
112 } else {
113 base := classes.Copy(l).(*Field)
114 OverlayMap(base.Map(), lClasses.Map())
115 l.DeleteField("classes")
116 l.Fields = append(l.Fields, base)
117 }
118
119 c.overlayClasses(l)
120 }
121 }
122
123 func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
124 for _, f := range m.Fields {
125 if f.Name == "vars" && f.Map() != nil {
126 varsStack = append([]*Map{f.Map()}, varsStack...)
127 }
128 }
129 for _, f := range m.Fields {
130 if f.Primary() != nil {
131 c.resolveSubstitutions(varsStack, f)
132 }
133 if arr, ok := f.Composite.(*Array); ok {
134 for _, val := range arr.Values {
135 if scalar, ok := val.(*Scalar); ok {
136 c.resolveSubstitutions(varsStack, scalar)
137 }
138 }
139 } else if f.Map() != nil {
140
141 if f.Name == "vars" {
142 c.compileSubstitutions(f.Map(), varsStack[1:])
143 c.validateConfigs(f.Map().GetField("d2-config"))
144 } else {
145 c.compileSubstitutions(f.Map(), varsStack)
146 }
147 }
148 }
149 for _, e := range m.Edges {
150 if e.Primary() != nil {
151 c.resolveSubstitutions(varsStack, e)
152 }
153 if e.Map() != nil {
154 c.compileSubstitutions(e.Map(), varsStack)
155 }
156 }
157 }
158
159 func (c *compiler) validateConfigs(configs *Field) {
160 if configs == nil || configs.Map() == nil {
161 return
162 }
163
164 if NodeBoardKind(ParentMap(ParentMap(configs))) == "" {
165 c.errorf(configs.LastRef().AST(), `"%s" can only appear at root vars`, configs.Name)
166 return
167 }
168
169 for _, f := range configs.Map().Fields {
170 var val string
171 if f.Primary() == nil {
172 if f.Name != "theme-overrides" && f.Name != "dark-theme-overrides" {
173 c.errorf(f.LastRef().AST(), `"%s" needs a value`, f.Name)
174 continue
175 }
176 } else {
177 val = f.Primary().Value.ScalarString()
178 }
179
180 switch f.Name {
181 case "sketch", "center":
182 _, err := strconv.ParseBool(val)
183 if err != nil {
184 c.errorf(f.LastRef().AST(), `expected a boolean for "%s", got "%s"`, f.Name, val)
185 continue
186 }
187 case "theme-overrides", "dark-theme-overrides":
188 if f.Map() == nil {
189 c.errorf(f.LastRef().AST(), `"%s" needs a map`, f.Name)
190 continue
191 }
192 case "theme-id", "dark-theme-id":
193 valInt, err := strconv.Atoi(val)
194 if err != nil {
195 c.errorf(f.LastRef().AST(), `expected an integer for "%s", got "%s"`, f.Name, val)
196 continue
197 }
198 if d2themescatalog.Find(int64(valInt)) == (d2themes.Theme{}) {
199 c.errorf(f.LastRef().AST(), `%d is not a valid theme ID`, valInt)
200 continue
201 }
202 case "pad":
203 _, err := strconv.Atoi(val)
204 if err != nil {
205 c.errorf(f.LastRef().AST(), `expected an integer for "%s", got "%s"`, f.Name, val)
206 continue
207 }
208 case "layout-engine":
209 default:
210 c.errorf(f.LastRef().AST(), `"%s" is not a valid config`, f.Name)
211 }
212 }
213 }
214
215 func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) {
216 var subbed bool
217 var resolvedField *Field
218
219 switch s := node.Primary().Value.(type) {
220 case *d2ast.UnquotedString:
221 for i, box := range s.Value {
222 if box.Substitution != nil {
223 for _, vars := range varsStack {
224 resolvedField = c.resolveSubstitution(vars, box.Substitution)
225 if resolvedField != nil {
226 if resolvedField.Primary() != nil {
227 if _, ok := resolvedField.Primary().Value.(*d2ast.Null); ok {
228 resolvedField = nil
229 }
230 }
231 break
232 }
233 }
234 if resolvedField == nil {
235 c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
236 return
237 }
238 if box.Substitution.Spread {
239 if resolvedField.Composite == nil {
240 c.errorf(box.Substitution, "cannot spread non-composite")
241 continue
242 }
243 switch n := node.(type) {
244 case *Scalar:
245 resolvedArr, ok := resolvedField.Composite.(*Array)
246 if !ok {
247 c.errorf(box.Substitution, "cannot spread non-array into array")
248 continue
249 }
250 arr := n.parent.(*Array)
251 for i, s := range arr.Values {
252 if s == n {
253 arr.Values = append(append(arr.Values[:i], resolvedArr.Values...), arr.Values[i+1:]...)
254 break
255 }
256 }
257 case *Field:
258 if resolvedField.Map() != nil {
259 OverlayMap(ParentMap(n), resolvedField.Map())
260 }
261
262 m := n.parent.(*Map)
263 for i, f2 := range m.Fields {
264 if n == f2 {
265 m.Fields = append(m.Fields[:i], m.Fields[i+1:]...)
266 break
267 }
268 }
269 }
270 }
271 if resolvedField.Primary() == nil {
272 if resolvedField.Composite == nil {
273 c.errorf(node.LastRef().AST(), `cannot substitute variable without value: "%s"`, strings.Join(box.Substitution.IDA(), "."))
274 return
275 }
276 if len(s.Value) > 1 {
277 c.errorf(node.LastRef().AST(), `cannot substitute composite variable "%s" as part of a string`, strings.Join(box.Substitution.IDA(), "."))
278 return
279 }
280 switch n := node.(type) {
281 case *Field:
282 n.Primary_ = nil
283 case *Edge:
284 n.Primary_ = nil
285 }
286 } else {
287 if i == 0 && len(s.Value) == 1 {
288 node.Primary().Value = resolvedField.Primary().Value
289 } else {
290 s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
291 subbed = true
292 }
293 }
294 if resolvedField.Composite != nil {
295 switch n := node.(type) {
296 case *Field:
297 n.Composite = resolvedField.Composite
298 case *Edge:
299 if resolvedField.Composite.Map() == nil {
300 c.errorf(node.LastRef().AST(), `cannot substitute array variable "%s" to an edge`, strings.Join(box.Substitution.IDA(), "."))
301 return
302 }
303 n.Map_ = resolvedField.Composite.Map()
304 }
305 }
306 }
307 }
308 if subbed {
309 s.Coalesce()
310 }
311 case *d2ast.DoubleQuotedString:
312 for i, box := range s.Value {
313 if box.Substitution != nil {
314 for _, vars := range varsStack {
315 resolvedField = c.resolveSubstitution(vars, box.Substitution)
316 if resolvedField != nil {
317 break
318 }
319 }
320 if resolvedField == nil {
321 c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
322 return
323 }
324 if resolvedField.Primary() == nil && resolvedField.Composite != nil {
325 c.errorf(node.LastRef().AST(), `cannot substitute map variable "%s" in quotes`, strings.Join(box.Substitution.IDA(), "."))
326 return
327 }
328 s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
329 subbed = true
330 }
331 }
332 if subbed {
333 s.Coalesce()
334 }
335 }
336 }
337
338 func (c *compiler) resolveSubstitution(vars *Map, substitution *d2ast.Substitution) *Field {
339 if vars == nil {
340 return nil
341 }
342
343 for i, p := range substitution.Path {
344 f := vars.GetField(p.Unbox().ScalarString())
345 if f == nil {
346 return nil
347 }
348
349 if i == len(substitution.Path)-1 {
350 return f
351 }
352 vars = f.Map()
353 }
354 return nil
355 }
356
357 func (c *compiler) overlay(base *Map, f *Field) {
358 if f.Map() == nil || f.Primary() != nil {
359 c.errorf(f.References[0].Context_.Key, "invalid %s", NodeBoardKind(f))
360 return
361 }
362 base = base.CopyBase(f)
363 OverlayMap(base, f.Map())
364 f.Composite = base
365 }
366
367 func (g *globContext) copy() *globContext {
368 g2 := *g
369 g2.refctx = g.root.refctx.Copy()
370 return &g2
371 }
372
373 func (g *globContext) prefixed(dst *Map) *globContext {
374 g2 := g.copy()
375 prefix := d2ast.MakeKeyPath(RelIDA(g2.refctx.ScopeMap, dst))
376 g2.refctx.Key = g2.refctx.Key.Copy()
377 if g2.refctx.Key.Key != nil {
378 prefix.Path = append(prefix.Path, g2.refctx.Key.Key.Path...)
379 }
380 if len(prefix.Path) > 0 {
381 g2.refctx.Key.Key = prefix
382 }
383 return g2
384 }
385
386 func (c *compiler) ampersandFilterMap(dst *Map, ast, scopeAST *d2ast.Map) bool {
387 for _, n := range ast.Nodes {
388 switch {
389 case n.MapKey != nil:
390 ok := c.ampersandFilter(&RefContext{
391 Key: n.MapKey,
392 Scope: ast,
393 ScopeMap: dst,
394 ScopeAST: scopeAST,
395 })
396 if !ok {
397 if len(c.mapRefContextStack) == 0 {
398 return false
399 }
400
401 gctx := c.getGlobContext(c.mapRefContextStack[len(c.mapRefContextStack)-1])
402 if gctx == nil {
403 return false
404 }
405 var ks string
406 if gctx.refctx.Key.HasTripleGlob() {
407 ks = d2format.Format(d2ast.MakeKeyPath(IDA(dst)))
408 } else {
409 ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(dst)))
410 }
411 delete(gctx.appliedFields, ks)
412 return false
413 }
414 }
415 }
416 return true
417 }
418
419 func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
420 var globs []*globContext
421 if len(c.globContextStack) > 0 {
422 previousGlobs := c.globContexts()
423
424
425 if NodeBoardKind(dst) == BoardLayer && !dst.Root() {
426 for _, g := range previousGlobs {
427 if g.refctx.Key.HasTripleGlob() {
428 globs = append(globs, g.prefixed(dst))
429 }
430 }
431 } else if NodeBoardKind(dst) != "" {
432
433 for _, g := range previousGlobs {
434 globs = append(globs, g.prefixed(dst))
435 }
436 } else {
437 globs = append(globs, previousGlobs...)
438 }
439 }
440 c.globContextStack = append(c.globContextStack, globs)
441 defer func() {
442 dst.globs = c.globContexts()
443 c.globContextStack = c.globContextStack[:len(c.globContextStack)-1]
444 }()
445
446 ok := c.ampersandFilterMap(dst, ast, scopeAST)
447 if !ok {
448 return
449 }
450
451 for _, n := range ast.Nodes {
452 switch {
453 case n.MapKey != nil:
454 c.compileKey(&RefContext{
455 Key: n.MapKey,
456 Scope: ast,
457 ScopeMap: dst,
458 ScopeAST: scopeAST,
459 })
460 case n.Substitution != nil:
461
462 f := &Field{
463 parent: dst,
464 Primary_: &Scalar{
465 Value: &d2ast.UnquotedString{
466 Value: []d2ast.InterpolationBox{{Substitution: n.Substitution}},
467 },
468 },
469 References: []*FieldReference{{
470 Context_: &RefContext{
471 Scope: ast,
472 ScopeMap: dst,
473 ScopeAST: scopeAST,
474 },
475 }},
476 }
477 dst.Fields = append(dst.Fields, f)
478 case n.Import != nil:
479 impn, ok := c._import(n.Import)
480 if !ok {
481 continue
482 }
483 if impn.Map() == nil {
484 c.errorf(n.Import, "cannot spread import non map into map")
485 continue
486 }
487
488 for _, gctx := range impn.Map().globs {
489 if !gctx.refctx.Key.HasTripleGlob() {
490 continue
491 }
492 gctx2 := gctx.copy()
493 gctx2.refctx.ScopeMap = dst
494 c.compileKey(gctx2.refctx)
495 c.ensureGlobContext(gctx2.refctx)
496 }
497
498 OverlayMap(dst, impn.Map())
499
500 if impnf, ok := impn.(*Field); ok {
501 if impnf.Primary_ != nil {
502 dstf := ParentField(dst)
503 if dstf != nil {
504 dstf.Primary_ = impnf.Primary_
505 }
506 }
507 }
508 }
509 }
510 }
511
512 func (c *compiler) globContexts() []*globContext {
513 return c.globContextStack[len(c.globContextStack)-1]
514 }
515
516 func (c *compiler) getGlobContext(refctx *RefContext) *globContext {
517 for _, gctx := range c.globContexts() {
518 if gctx.refctx.Equal(refctx) {
519 return gctx
520 }
521 }
522 return nil
523 }
524
525 func (c *compiler) ensureGlobContext(refctx *RefContext) *globContext {
526 gctx := c.getGlobContext(refctx)
527 if gctx != nil {
528 return gctx
529 }
530 gctx = &globContext{
531 refctx: refctx,
532 appliedFields: make(map[string]struct{}),
533 appliedEdges: make(map[string]struct{}),
534 }
535 gctx.root = gctx
536 c.globContextStack[len(c.globContextStack)-1] = append(c.globContexts(), gctx)
537 return gctx
538 }
539
540 func (c *compiler) compileKey(refctx *RefContext) {
541 if refctx.Key.HasGlob() {
542
543
544 for _, refctx2 := range c.globRefContextStack {
545
546 if refctx.Equal(refctx2) {
547
548 return
549 }
550
551 }
552 c.globRefContextStack = append(c.globRefContextStack, refctx)
553 defer func() {
554 c.globRefContextStack = c.globRefContextStack[:len(c.globRefContextStack)-1]
555 }()
556 c.ensureGlobContext(refctx)
557 }
558 oldFields := refctx.ScopeMap.FieldCountRecursive()
559 oldEdges := refctx.ScopeMap.EdgeCountRecursive()
560 if len(refctx.Key.Edges) == 0 {
561 c.compileField(refctx.ScopeMap, refctx.Key.Key, refctx)
562 } else {
563 c.compileEdges(refctx)
564 }
565 if oldFields != refctx.ScopeMap.FieldCountRecursive() || oldEdges != refctx.ScopeMap.EdgeCountRecursive() {
566 for _, gctx2 := range c.globContexts() {
567
568 old := c.lazyGlobBeingApplied
569 c.lazyGlobBeingApplied = true
570 c.compileKey(gctx2.refctx)
571 c.lazyGlobBeingApplied = old
572 }
573 }
574 }
575
576 func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext) {
577 if refctx.Key.Ampersand {
578 return
579 }
580
581 fa, err := dst.EnsureField(kp, refctx, true, c)
582 if err != nil {
583 c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
584 return
585 }
586
587 for _, f := range fa {
588 c._compileField(f, refctx)
589 }
590 }
591
592 func (c *compiler) ampersandFilter(refctx *RefContext) bool {
593 if !refctx.Key.Ampersand {
594 return true
595 }
596 if len(c.mapRefContextStack) == 0 || !c.mapRefContextStack[len(c.mapRefContextStack)-1].Key.SupportsGlobFilters() {
597 c.errorf(refctx.Key, "glob filters cannot be used outside globs")
598 return false
599 }
600 if len(refctx.Key.Edges) > 0 {
601 return true
602 }
603
604 fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, false, c)
605 if err != nil {
606 c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
607 return false
608 }
609 if len(fa) == 0 {
610 if refctx.Key.Key.Last().ScalarString() != "label" {
611 return false
612 }
613 kp := refctx.Key.Key.Copy()
614 kp.Path = kp.Path[:len(kp.Path)-1]
615 if len(kp.Path) == 0 {
616 n := refctx.ScopeMap.Parent()
617 switch n := n.(type) {
618 case *Field:
619 fa = append(fa, n)
620 case *Edge:
621 if n.Primary_ == nil {
622 if refctx.Key.Value.ScalarBox().Unbox().ScalarString() == "" {
623 return true
624 }
625 return false
626 }
627 if n.Primary_.Value.ScalarString() != refctx.Key.Value.ScalarBox().Unbox().ScalarString() {
628 return false
629 }
630 }
631 } else {
632 fa, err = refctx.ScopeMap.EnsureField(kp, refctx, false, c)
633 if err != nil {
634 c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
635 return false
636 }
637 }
638 for _, f := range fa {
639 label := f.Name
640 if f.Primary_ != nil {
641 label = f.Primary_.Value.ScalarString()
642 }
643 if label != refctx.Key.Value.ScalarBox().Unbox().ScalarString() {
644 return false
645 }
646 }
647 return true
648 }
649 for _, f := range fa {
650 ok := c._ampersandFilter(f, refctx)
651 if !ok {
652 return false
653 }
654 }
655 return true
656 }
657
658 func (c *compiler) _ampersandFilter(f *Field, refctx *RefContext) bool {
659 if refctx.Key.Value.ScalarBox().Unbox() == nil {
660 c.errorf(refctx.Key, "glob filters cannot be composites")
661 return false
662 }
663
664 if a, ok := f.Composite.(*Array); ok {
665 for _, v := range a.Values {
666 if s, ok := v.(*Scalar); ok {
667 if refctx.Key.Value.ScalarBox().Unbox().ScalarString() == s.Value.ScalarString() {
668 return true
669 }
670 }
671 }
672 }
673
674 if f.Primary_ == nil {
675 return false
676 }
677
678 if refctx.Key.Value.ScalarBox().Unbox().ScalarString() != f.Primary_.Value.ScalarString() {
679 return false
680 }
681
682 return true
683 }
684
685 func (c *compiler) _compileField(f *Field, refctx *RefContext) {
686
687 if refctx.Key.Value.Map != nil && refctx.Key.Value.Map.HasFilter() {
688 if f.Map() == nil {
689 f.Composite = &Map{
690 parent: f,
691 }
692 }
693 c.mapRefContextStack = append(c.mapRefContextStack, refctx)
694 ok := c.ampersandFilterMap(f.Map(), refctx.Key.Value.Map, refctx.ScopeAST)
695 c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
696 if !ok {
697 return
698 }
699 }
700
701 if len(refctx.Key.Edges) == 0 && (refctx.Key.Primary.Null != nil || refctx.Key.Value.Null != nil) {
702
703
704 if !IsVar(ParentMap(f)) {
705 ParentMap(f).DeleteField(f.Name)
706 return
707 }
708 }
709
710 if refctx.Key.Primary.Unbox() != nil {
711 if c.ignoreLazyGlob(f) {
712 return
713 }
714 f.Primary_ = &Scalar{
715 parent: f,
716 Value: refctx.Key.Primary.Unbox(),
717 }
718 }
719
720 if refctx.Key.Value.Array != nil {
721 a := &Array{
722 parent: f,
723 }
724 c.compileArray(a, refctx.Key.Value.Array, refctx.ScopeAST)
725 f.Composite = a
726 } else if refctx.Key.Value.Map != nil {
727 scopeAST := refctx.Key.Value.Map
728 if f.Map() == nil {
729 f.Composite = &Map{
730 parent: f,
731 }
732 switch NodeBoardKind(f) {
733 case BoardScenario:
734 c.overlay(ParentBoard(f).Map(), f)
735 case BoardStep:
736 stepsMap := ParentMap(f)
737 for i := range stepsMap.Fields {
738 if stepsMap.Fields[i] == f {
739 if i == 0 {
740 c.overlay(ParentBoard(f).Map(), f)
741 } else {
742 c.overlay(stepsMap.Fields[i-1].Map(), f)
743 }
744 break
745 }
746 }
747 case BoardLayer:
748 default:
749
750 scopeAST = refctx.ScopeAST
751 }
752 } else {
753 scopeAST = refctx.ScopeAST
754 }
755 c.mapRefContextStack = append(c.mapRefContextStack, refctx)
756 c.compileMap(f.Map(), refctx.Key.Value.Map, scopeAST)
757 c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
758 switch NodeBoardKind(f) {
759 case BoardScenario, BoardStep:
760 c.overlayClasses(f.Map())
761 }
762 } else if refctx.Key.Value.Import != nil {
763 n, ok := c._import(refctx.Key.Value.Import)
764 if !ok {
765 return
766 }
767 switch n := n.(type) {
768 case *Field:
769 if n.Primary_ != nil {
770 f.Primary_ = n.Primary_.Copy(f).(*Scalar)
771 }
772 if n.Composite != nil {
773 f.Composite = n.Composite.Copy(f).(Composite)
774 }
775 case *Map:
776 f.Composite = &Map{
777 parent: f,
778 }
779 switch NodeBoardKind(f) {
780 case BoardScenario:
781 c.overlay(ParentBoard(f).Map(), f)
782 case BoardStep:
783 stepsMap := ParentMap(f)
784 for i := range stepsMap.Fields {
785 if stepsMap.Fields[i] == f {
786 if i == 0 {
787 c.overlay(ParentBoard(f).Map(), f)
788 } else {
789 c.overlay(stepsMap.Fields[i-1].Map(), f)
790 }
791 break
792 }
793 }
794 }
795 OverlayMap(f.Map(), n)
796 c.updateLinks(f.Map())
797 switch NodeBoardKind(f) {
798 case BoardScenario, BoardStep:
799 c.overlayClasses(f.Map())
800 }
801 }
802 } else if refctx.Key.Value.ScalarBox().Unbox() != nil {
803 if c.ignoreLazyGlob(f) {
804 return
805 }
806 f.Primary_ = &Scalar{
807 parent: f,
808 Value: refctx.Key.Value.ScalarBox().Unbox(),
809 }
810
811 if f.Name == "link" {
812 c.compileLink(f, refctx)
813 }
814 }
815 }
816
817
818
819 func (c *compiler) ignoreLazyGlob(n Node) bool {
820 if c.lazyGlobBeingApplied && n.Primary() != nil {
821 lastPrimaryRef := n.LastPrimaryRef()
822 if lastPrimaryRef != nil && !lastPrimaryRef.DueToLazyGlob() {
823 return true
824 }
825 }
826 return false
827 }
828
829 func (c *compiler) updateLinks(m *Map) {
830 for _, f := range m.Fields {
831 if f.Name == "link" {
832 val := f.Primary().Value.ScalarString()
833 link, err := d2parser.ParseKey(val)
834 if err != nil {
835 continue
836 }
837
838 linkIDA := link.IDA()
839 if len(linkIDA) == 0 {
840 continue
841 }
842
843
844 if linkIDA[0] != "root" {
845 continue
846 }
847 bida := BoardIDA(f)
848 aida := IDA(f)
849 if len(bida) != len(aida) {
850 prependIDA := aida[:len(aida)-len(bida)]
851 fullIDA := []string{"root"}
852
853
854
855
856
857
858
859
860
861 OUTER:
862 for i := 1; i < len(prependIDA); i += 2 {
863 for j := 0; i+j < len(prependIDA); j++ {
864 if prependIDA[i+j] != linkIDA[1+j] {
865 break
866 }
867
868 if i+j == len(prependIDA)-1 {
869 break OUTER
870 }
871 }
872 fullIDA = append(fullIDA, prependIDA[i])
873 fullIDA = append(fullIDA, prependIDA[i+1])
874 }
875
876 fullIDA = append(fullIDA, linkIDA[1:]...)
877
878 kp := d2ast.MakeKeyPath(fullIDA)
879 s := d2format.Format(kp)
880 f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(s)).ScalarBox().Unbox()
881 }
882 }
883 if f.Map() != nil {
884 c.updateLinks(f.Map())
885 }
886 }
887 }
888
889 func (c *compiler) compileLink(f *Field, refctx *RefContext) {
890 val := refctx.Key.Value.ScalarBox().Unbox().ScalarString()
891 link, err := d2parser.ParseKey(val)
892 if err != nil {
893 return
894 }
895
896 scopeIDA := IDA(refctx.ScopeMap)
897
898 if len(scopeIDA) == 0 {
899 return
900 }
901
902 linkIDA := link.IDA()
903 if len(linkIDA) == 0 {
904 return
905 }
906
907 if linkIDA[0] == "root" {
908 c.errorf(refctx.Key.Key, "cannot refer to root in link")
909 return
910 }
911
912
913 if !strings.EqualFold(linkIDA[0], "layers") && !strings.EqualFold(linkIDA[0], "scenarios") && !strings.EqualFold(linkIDA[0], "steps") && linkIDA[0] != "_" {
914 return
915 }
916
917
918 for i := len(scopeIDA) - 1; i > 0; i-- {
919 if strings.EqualFold(scopeIDA[i-1], "layers") || strings.EqualFold(scopeIDA[i-1], "scenarios") || strings.EqualFold(scopeIDA[i-1], "steps") {
920 scopeIDA = scopeIDA[:i+1]
921 break
922 }
923 if scopeIDA[i-1] == "root" {
924 scopeIDA = scopeIDA[:i]
925 break
926 }
927 }
928
929
930 for len(linkIDA) > 0 && linkIDA[0] == "_" {
931 if len(scopeIDA) < 2 {
932
933
934 c.errorf(refctx.Key.Key, "invalid underscore usage")
935 return
936 }
937
938 scopeIDA = scopeIDA[:len(scopeIDA)-2]
939 linkIDA = linkIDA[1:]
940 }
941 if len(scopeIDA) == 0 {
942 scopeIDA = []string{"root"}
943 }
944
945
946 scopeIDA = append(scopeIDA, linkIDA...)
947 kp := d2ast.MakeKeyPath(scopeIDA)
948 f.Primary_.Value = d2ast.FlatUnquotedString(d2format.Format(kp))
949 }
950
951 func (c *compiler) compileEdges(refctx *RefContext) {
952 if refctx.Key.Key == nil {
953 c._compileEdges(refctx)
954 return
955 }
956
957 fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, true, c)
958 if err != nil {
959 c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
960 return
961 }
962 for _, f := range fa {
963 if _, ok := f.Composite.(*Array); ok {
964 c.errorf(refctx.Key.Key, "cannot index into array")
965 return
966 }
967 if f.Map() == nil {
968 f.Composite = &Map{
969 parent: f,
970 }
971 }
972 refctx2 := *refctx
973 refctx2.ScopeMap = f.Map()
974 c._compileEdges(&refctx2)
975 }
976 }
977
978 func (c *compiler) _compileEdges(refctx *RefContext) {
979 eida := NewEdgeIDs(refctx.Key)
980 for i, eid := range eida {
981 if refctx.Key != nil && refctx.Key.Value.Null != nil {
982 refctx.ScopeMap.DeleteEdge(eid)
983 continue
984 }
985
986 refctx = refctx.Copy()
987 refctx.Edge = refctx.Key.Edges[i]
988
989 var ea []*Edge
990 if eid.Index != nil || eid.Glob {
991 ea = refctx.ScopeMap.GetEdges(eid, refctx, c)
992 if len(ea) == 0 {
993 if !eid.Glob {
994 c.errorf(refctx.Edge, "indexed edge does not exist")
995 }
996 continue
997 }
998 for _, e := range ea {
999 e.References = append(e.References, &EdgeReference{
1000 Context_: refctx,
1001 DueToGlob_: len(c.globRefContextStack) > 0,
1002 DueToLazyGlob_: c.lazyGlobBeingApplied,
1003 })
1004 refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Src, refctx, c)
1005 refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Dst, refctx, c)
1006 }
1007 } else {
1008 var err error
1009 ea, err = refctx.ScopeMap.CreateEdge(eid, refctx, c)
1010 if err != nil {
1011 c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
1012 continue
1013 }
1014 }
1015
1016 for _, e := range ea {
1017 if refctx.Key.EdgeKey != nil {
1018 if e.Map_ == nil {
1019 e.Map_ = &Map{
1020 parent: e,
1021 }
1022 }
1023 c.compileField(e.Map_, refctx.Key.EdgeKey, refctx)
1024 } else {
1025 if refctx.Key.Primary.Unbox() != nil {
1026 if c.ignoreLazyGlob(e) {
1027 return
1028 }
1029 e.Primary_ = &Scalar{
1030 parent: e,
1031 Value: refctx.Key.Primary.Unbox(),
1032 }
1033 }
1034 if refctx.Key.Value.Array != nil {
1035 c.errorf(refctx.Key.Value.Unbox(), "edges cannot be assigned arrays")
1036 continue
1037 } else if refctx.Key.Value.Map != nil {
1038 if e.Map_ == nil {
1039 e.Map_ = &Map{
1040 parent: e,
1041 }
1042 }
1043 c.mapRefContextStack = append(c.mapRefContextStack, refctx)
1044 c.compileMap(e.Map_, refctx.Key.Value.Map, refctx.ScopeAST)
1045 c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
1046 } else if refctx.Key.Value.ScalarBox().Unbox() != nil {
1047 if c.ignoreLazyGlob(e) {
1048 return
1049 }
1050 e.Primary_ = &Scalar{
1051 parent: e,
1052 Value: refctx.Key.Value.ScalarBox().Unbox(),
1053 }
1054 }
1055 }
1056 }
1057 }
1058 }
1059
1060 func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map) {
1061 for _, an := range a.Nodes {
1062 var irv Value
1063 switch v := an.Unbox().(type) {
1064 case *d2ast.Array:
1065 ira := &Array{
1066 parent: dst,
1067 }
1068 c.compileArray(ira, v, scopeAST)
1069 irv = ira
1070 case *d2ast.Map:
1071 irm := &Map{
1072 parent: dst,
1073 }
1074 c.compileMap(irm, v, scopeAST)
1075 irv = irm
1076 case d2ast.Scalar:
1077 irv = &Scalar{
1078 parent: dst,
1079 Value: v,
1080 }
1081 case *d2ast.Import:
1082 n, ok := c._import(v)
1083 if !ok {
1084 continue
1085 }
1086 switch n := n.(type) {
1087 case *Field:
1088 if v.Spread {
1089 a, ok := n.Composite.(*Array)
1090 if !ok {
1091 c.errorf(v, "can only spread import array into array")
1092 continue
1093 }
1094 dst.Values = append(dst.Values, a.Values...)
1095 continue
1096 }
1097 if n.Composite != nil {
1098 irv = n.Composite
1099 } else {
1100 irv = n.Primary_
1101 }
1102 case *Map:
1103 if v.Spread {
1104 c.errorf(v, "can only spread import array into array")
1105 continue
1106 }
1107 irv = n
1108 }
1109 case *d2ast.Substitution:
1110 irv = &Scalar{
1111 parent: dst,
1112 Value: &d2ast.UnquotedString{
1113 Value: []d2ast.InterpolationBox{{Substitution: an.Substitution}},
1114 },
1115 }
1116 }
1117
1118 dst.Values = append(dst.Values, irv)
1119 }
1120 }
1121
View as plain text