d2/d2ir/compile.go

677 lines
16 KiB
Go
Raw Normal View History

2023-01-16 11:52:37 +00:00
package d2ir
import (
"io/fs"
"strings"
2023-01-16 11:52:37 +00:00
"oss.terrastruct.com/d2/d2ast"
2023-03-03 01:56:32 +00:00
"oss.terrastruct.com/d2/d2format"
2023-01-16 11:52:37 +00:00
"oss.terrastruct.com/d2/d2parser"
2023-07-11 20:32:07 +00:00
"oss.terrastruct.com/util-go/go2"
2023-01-16 11:52:37 +00:00
)
type compiler struct {
err *d2parser.ParseError
fs fs.FS
// importStack is used to detect cyclic imports.
importStack []string
// importCache enables reuse of files imported multiple times.
importCache map[string]*Map
utf16 bool
}
type CompileOptions struct {
UTF16 bool
// Pass nil to disable imports.
FS fs.FS
2023-01-16 11:52:37 +00:00
}
func (c *compiler) errorf(n d2ast.Node, f string, v ...interface{}) {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, d2parser.Errorf(n, f, v...).(d2ast.Error))
2023-01-16 11:52:37 +00:00
}
func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, error) {
if opts == nil {
opts = &CompileOptions{}
}
c := &compiler{
err: &d2parser.ParseError{},
fs: opts.FS,
importCache: make(map[string]*Map),
utf16: opts.UTF16,
}
2023-01-24 06:45:21 +00:00
m := &Map{}
m.initRoot()
m.parent.(*Field).References[0].Context.Scope = ast
2023-06-20 03:06:26 +00:00
m.parent.(*Field).References[0].Context.ScopeAST = ast
c.pushImportStack(&d2ast.Import{
Path: []*d2ast.StringBox{d2ast.RawStringBox(ast.GetRange().Path, true)},
})
defer c.popImportStack()
2023-06-20 03:06:26 +00:00
c.compileMap(m, ast, ast)
2023-07-12 00:19:34 +00:00
c.compileSubstitutions(m, nil)
2023-07-11 04:18:18 +00:00
c.overlayClasses(m)
2023-01-16 11:52:37 +00:00
if !c.err.Empty() {
2023-01-18 01:39:31 +00:00
return nil, c.err
2023-01-16 11:52:37 +00:00
}
2023-01-18 11:51:16 +00:00
return m, nil
2023-01-18 01:39:31 +00:00
}
2023-07-11 04:18:18 +00:00
func (c *compiler) overlayClasses(m *Map) {
2023-02-06 21:32:08 +00:00
classes := m.GetField("classes")
if classes == nil || classes.Map() == nil {
return
}
layersField := m.GetField("layers")
if layersField == nil {
return
}
layers := layersField.Map()
if layers == nil {
return
}
for _, lf := range layers.Fields {
if lf.Map() == nil || lf.Primary() != nil {
c.errorf(lf.References[0].Context.Key, "invalid layer")
continue
}
l := lf.Map()
lClasses := l.GetField("classes")
if lClasses == nil {
lClasses = classes.Copy(l).(*Field)
l.Fields = append(l.Fields, lClasses)
} else {
base := classes.Copy(l).(*Field)
OverlayMap(base.Map(), lClasses.Map())
l.DeleteField("classes")
l.Fields = append(l.Fields, base)
}
2023-07-11 04:18:18 +00:00
c.overlayClasses(l)
2023-02-06 21:32:08 +00:00
}
}
2023-07-12 00:19:34 +00:00
func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
for _, f := range m.Fields {
if f.Name == "vars" && f.Map() != nil {
varsStack = append([]*Map{f.Map()}, varsStack...)
}
if f.Primary() != nil {
2023-07-12 18:55:58 +00:00
c.resolveSubstitutions(varsStack, f)
2023-07-12 00:19:34 +00:00
}
2023-07-13 05:20:55 +00:00
if arr, ok := f.Composite.(*Array); ok {
for _, val := range arr.Values {
if scalar, ok := val.(*Scalar); ok {
c.resolveSubstitutions(varsStack, scalar)
}
}
2023-07-13 17:11:08 +00:00
} else if f.Map() != nil {
2023-07-12 18:55:58 +00:00
// don't resolve substitutions in vars with the current scope of vars
2023-07-12 06:26:06 +00:00
if f.Name == "vars" {
c.compileSubstitutions(f.Map(), varsStack[1:])
} else {
c.compileSubstitutions(f.Map(), varsStack)
}
2023-07-12 00:19:34 +00:00
}
2023-07-11 20:32:07 +00:00
}
2023-07-12 00:19:34 +00:00
for _, e := range m.Edges {
if e.Primary() != nil {
2023-07-12 18:55:58 +00:00
c.resolveSubstitutions(varsStack, e)
2023-07-12 00:19:34 +00:00
}
2023-07-12 00:33:01 +00:00
if e.Map() != nil {
c.compileSubstitutions(e.Map(), varsStack)
}
2023-07-11 20:32:07 +00:00
}
2023-07-12 00:19:34 +00:00
}
2023-07-11 20:32:07 +00:00
2023-07-12 18:55:58 +00:00
func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) {
2023-07-12 00:33:01 +00:00
var subbed bool
var resolvedField *Field
2023-07-12 18:55:58 +00:00
switch s := node.Primary().Value.(type) {
2023-07-12 00:19:34 +00:00
case *d2ast.UnquotedString:
for i, box := range s.Value {
2023-07-11 20:32:07 +00:00
if box.Substitution != nil {
2023-07-12 00:33:01 +00:00
for _, vars := range varsStack {
resolvedField = c.resolveSubstitution(vars, box.Substitution)
if resolvedField != nil {
2023-07-13 15:35:16 +00:00
if resolvedField.Primary() != nil {
if _, ok := resolvedField.Primary().Value.(*d2ast.Null); ok {
resolvedField = nil
}
}
2023-07-12 00:33:01 +00:00
break
}
}
2023-07-12 06:26:06 +00:00
if resolvedField == nil {
2023-07-12 18:55:58 +00:00
c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
2023-07-12 00:33:01 +00:00
return
2023-07-11 20:32:07 +00:00
}
2023-07-12 21:53:28 +00:00
if box.Substitution.Spread {
if resolvedField.Composite == nil {
2023-07-13 15:37:22 +00:00
c.errorf(box.Substitution, "cannot spread non-composite")
2023-07-12 21:53:28 +00:00
continue
}
2023-07-13 05:20:55 +00:00
switch n := node.(type) {
case *Scalar: // Array value
resolvedArr, ok := resolvedField.Composite.(*Array)
if !ok {
c.errorf(box.Substitution, "cannot spread non-array into array")
continue
}
arr := n.parent.(*Array)
for i, s := range arr.Values {
if s == n {
arr.Values = append(append(arr.Values[:i], resolvedArr.Values...), arr.Values[i+1:]...)
break
}
}
case *Field:
if resolvedField.Map() != nil {
OverlayMap(ParentMap(n), resolvedField.Map())
}
// Remove the placeholder field
m := n.parent.(*Map)
for i, f2 := range m.Fields {
if n == f2 {
m.Fields = append(m.Fields[:i], m.Fields[i+1:]...)
break
}
2023-07-12 21:53:28 +00:00
}
}
}
2023-07-12 18:55:58 +00:00
if resolvedField.Primary() == nil {
if len(s.Value) > 1 {
c.errorf(node.LastRef().AST(), `cannot substitute map variable "%s" as part of a string`, strings.Join(box.Substitution.IDA(), "."))
return
}
2023-07-13 05:20:55 +00:00
switch n := node.(type) {
case *Field:
n.Primary_ = nil
case *Edge:
n.Primary_ = nil
}
2023-07-12 06:26:06 +00:00
} else {
2023-07-13 17:13:26 +00:00
s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
subbed = true
2023-07-12 18:55:58 +00:00
}
if resolvedField.Composite != nil {
switch n := node.(type) {
case *Field:
n.Composite = resolvedField.Composite
case *Edge:
if resolvedField.Composite.Map() == nil {
c.errorf(node.LastRef().AST(), `cannot substitute array variable "%s" to an edge`, strings.Join(box.Substitution.IDA(), "."))
return
}
n.Map_ = resolvedField.Composite.Map()
}
2023-07-12 06:26:06 +00:00
}
2023-07-11 20:32:07 +00:00
}
}
2023-07-11 20:52:29 +00:00
if subbed {
2023-07-12 00:19:34 +00:00
s.Coalesce()
2023-07-11 20:52:29 +00:00
}
2023-07-12 00:19:34 +00:00
case *d2ast.DoubleQuotedString:
for i, box := range s.Value {
2023-07-11 20:32:07 +00:00
if box.Substitution != nil {
2023-07-12 00:33:01 +00:00
for _, vars := range varsStack {
resolvedField = c.resolveSubstitution(vars, box.Substitution)
if resolvedField != nil {
break
}
}
2023-07-12 06:26:06 +00:00
if resolvedField == nil {
2023-07-12 18:55:58 +00:00
c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
2023-07-12 00:33:01 +00:00
return
2023-07-11 20:32:07 +00:00
}
2023-07-13 17:11:08 +00:00
if resolvedField.Primary() == nil && resolvedField.Composite != nil {
2023-07-12 18:55:58 +00:00
c.errorf(node.LastRef().AST(), `cannot substitute map variable "%s" in quotes`, strings.Join(box.Substitution.IDA(), "."))
2023-07-12 06:26:06 +00:00
return
}
2023-07-13 17:13:26 +00:00
s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
2023-07-12 06:26:06 +00:00
subbed = true
2023-07-11 20:32:07 +00:00
}
}
2023-07-11 20:52:29 +00:00
if subbed {
2023-07-12 00:19:34 +00:00
s.Coalesce()
2023-07-11 20:52:29 +00:00
}
2023-07-11 20:32:07 +00:00
}
}
2023-07-12 00:33:01 +00:00
func (c *compiler) resolveSubstitution(vars *Map, substitution *d2ast.Substitution) *Field {
2023-07-12 06:26:06 +00:00
if vars == nil {
return nil
}
for i, p := range substitution.Path {
f := vars.GetField(p.Unbox().ScalarString())
if f == nil {
return nil
2023-07-11 02:24:21 +00:00
}
2023-07-13 15:35:16 +00:00
2023-07-12 06:26:06 +00:00
if i == len(substitution.Path)-1 {
return f
2023-07-11 04:18:18 +00:00
}
2023-07-12 06:26:06 +00:00
vars = f.Map()
2023-07-11 04:18:18 +00:00
}
2023-07-12 06:26:06 +00:00
return nil
2023-07-11 04:18:18 +00:00
}
2023-03-25 19:06:06 +00:00
func (c *compiler) overlay(base *Map, f *Field) {
if f.Map() == nil || f.Primary() != nil {
c.errorf(f.References[0].Context.Key, "invalid %s", NodeBoardKind(f))
2023-01-18 15:15:16 +00:00
return
}
2023-03-25 19:06:06 +00:00
base = base.CopyBase(f)
OverlayMap(base, f.Map())
f.Composite = base
2023-01-16 11:52:37 +00:00
}
2023-06-20 03:06:26 +00:00
func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
2023-01-16 11:52:37 +00:00
for _, n := range ast.Nodes {
switch {
case n.MapKey != nil:
2023-01-24 05:48:43 +00:00
c.compileKey(&RefContext{
Key: n.MapKey,
Scope: ast,
ScopeMap: dst,
2023-06-20 03:06:26 +00:00
ScopeAST: scopeAST,
2023-01-18 05:28:33 +00:00
})
2023-07-12 21:53:28 +00:00
case n.Substitution != nil:
// placeholder field to be resolved at the end
f := &Field{
parent: dst,
Primary_: &Scalar{
Value: &d2ast.UnquotedString{
Value: []d2ast.InterpolationBox{{Substitution: n.Substitution}},
},
},
}
dst.Fields = append(dst.Fields, f)
case n.Import != nil:
impn, ok := c._import(n.Import)
if !ok {
continue
}
if impn.Map() == nil {
c.errorf(n.Import, "cannot spread import non map into map")
continue
}
OverlayMap(dst, impn.Map())
if impnf, ok := impn.(*Field); ok {
if impnf.Primary_ != nil {
dstf := ParentField(dst)
if dstf != nil {
dstf.Primary_ = impnf.Primary_
}
}
}
2023-01-16 11:52:37 +00:00
}
}
}
2023-01-24 05:48:43 +00:00
func (c *compiler) compileKey(refctx *RefContext) {
2023-01-18 05:28:33 +00:00
if len(refctx.Key.Edges) == 0 {
2023-01-24 05:48:43 +00:00
c.compileField(refctx.ScopeMap, refctx.Key.Key, refctx)
2023-01-16 11:52:37 +00:00
} else {
2023-01-24 05:48:43 +00:00
c.compileEdges(refctx)
2023-01-16 11:52:37 +00:00
}
}
2023-01-18 10:06:44 +00:00
func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext) {
2023-06-26 18:57:18 +00:00
if refctx.Key != nil && len(refctx.Key.Edges) == 0 && refctx.Key.Value.Null != nil {
2023-07-13 15:35:16 +00:00
// For vars, if we delete the field, it may just resolve to an outer scope var of the same name
// Instead we keep it around, so that resolveSubstitutions can find it
2023-07-13 17:22:37 +00:00
if !IsVar(dst) {
2023-07-13 15:35:16 +00:00
dst.DeleteField(kp.IDA()...)
return
}
2023-06-26 18:57:18 +00:00
}
2023-01-18 10:06:44 +00:00
f, err := dst.EnsureField(kp, refctx)
2023-01-16 11:52:37 +00:00
if err != nil {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
2023-01-16 11:52:37 +00:00
return
}
2023-01-18 10:06:44 +00:00
if refctx.Key.Primary.Unbox() != nil {
2023-01-18 15:15:16 +00:00
f.Primary_ = &Scalar{
2023-01-16 11:52:37 +00:00
parent: f,
2023-01-18 10:06:44 +00:00
Value: refctx.Key.Primary.Unbox(),
2023-01-16 11:52:37 +00:00
}
}
2023-01-18 10:06:44 +00:00
if refctx.Key.Value.Array != nil {
2023-01-16 11:52:37 +00:00
a := &Array{
parent: f,
}
2023-06-20 03:06:26 +00:00
c.compileArray(a, refctx.Key.Value.Array, refctx.ScopeAST)
2023-01-16 11:52:37 +00:00
f.Composite = a
2023-01-18 10:06:44 +00:00
} else if refctx.Key.Value.Map != nil {
2023-01-18 15:15:16 +00:00
if f.Map() == nil {
f.Composite = &Map{
2023-01-16 11:52:37 +00:00
parent: f,
}
}
2023-06-20 03:06:26 +00:00
scopeAST := refctx.Key.Value.Map
2023-03-25 19:06:06 +00:00
switch NodeBoardKind(f) {
case BoardScenario:
2023-04-07 04:02:12 +00:00
c.overlay(ParentBoard(f).Map(), f)
2023-03-25 19:06:06 +00:00
case BoardStep:
2023-04-07 04:02:12 +00:00
stepsMap := ParentMap(f)
2023-03-25 19:06:06 +00:00
for i := range stepsMap.Fields {
if stepsMap.Fields[i] == f {
if i == 0 {
2023-04-07 04:02:12 +00:00
c.overlay(ParentBoard(f).Map(), f)
2023-03-25 19:06:06 +00:00
} else {
c.overlay(stepsMap.Fields[i-1].Map(), f)
}
break
}
}
2023-06-20 03:06:26 +00:00
case BoardLayer:
default:
// If new board type, use that as the new scope AST, otherwise, carry on
scopeAST = refctx.ScopeAST
2023-03-25 19:06:06 +00:00
}
2023-06-20 03:06:26 +00:00
c.compileMap(f.Map(), refctx.Key.Value.Map, scopeAST)
2023-02-06 21:32:08 +00:00
switch NodeBoardKind(f) {
case BoardScenario, BoardStep:
2023-07-11 04:18:18 +00:00
c.overlayClasses(f.Map())
2023-02-06 21:32:08 +00:00
}
} else if refctx.Key.Value.Import != nil {
n, ok := c._import(refctx.Key.Value.Import)
if !ok {
return
}
switch n := n.(type) {
case *Field:
if n.Primary_ != nil {
2023-06-07 05:13:49 +00:00
f.Primary_ = n.Primary_.Copy(f).(*Scalar)
}
if n.Composite != nil {
f.Composite = n.Composite.Copy(f).(Composite)
}
case *Map:
f.Composite = &Map{
parent: f,
}
switch NodeBoardKind(f) {
case BoardScenario:
c.overlay(ParentBoard(f).Map(), f)
case BoardStep:
stepsMap := ParentMap(f)
for i := range stepsMap.Fields {
if stepsMap.Fields[i] == f {
if i == 0 {
c.overlay(ParentBoard(f).Map(), f)
} else {
c.overlay(stepsMap.Fields[i-1].Map(), f)
}
break
}
}
}
OverlayMap(f.Map(), n)
2023-06-07 05:13:49 +00:00
c.updateLinks(f.Map())
switch NodeBoardKind(f) {
case BoardScenario, BoardStep:
2023-07-11 04:18:18 +00:00
c.overlayClasses(f.Map())
}
}
2023-01-18 10:06:44 +00:00
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
2023-03-03 01:56:32 +00:00
// If the link is a board, we need to transform it into an absolute path.
2023-03-01 22:53:58 +00:00
if f.Name == "link" {
2023-03-01 23:38:02 +00:00
c.compileLink(refctx)
2023-03-01 22:53:58 +00:00
}
2023-01-18 15:15:16 +00:00
f.Primary_ = &Scalar{
2023-01-16 11:52:37 +00:00
parent: f,
2023-01-18 10:06:44 +00:00
Value: refctx.Key.Value.ScalarBox().Unbox(),
2023-01-16 11:52:37 +00:00
}
}
}
2023-06-07 05:13:49 +00:00
func (c *compiler) updateLinks(m *Map) {
for _, f := range m.Fields {
if f.Name == "link" {
bida := BoardIDA(f)
aida := IDA(f)
if len(bida) != len(aida) {
prependIDA := aida[:len(aida)-len(bida)]
kp := d2ast.MakeKeyPath(prependIDA)
s := d2format.Format(kp) + strings.TrimPrefix(f.Primary_.Value.ScalarString(), "root")
f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(s)).ScalarBox().Unbox()
2023-06-07 05:13:49 +00:00
}
}
if f.Map() != nil {
c.updateLinks(f.Map())
}
}
}
2023-03-01 23:38:02 +00:00
func (c *compiler) compileLink(refctx *RefContext) {
val := refctx.Key.Value.ScalarBox().Unbox().ScalarString()
link, err := d2parser.ParseKey(val)
if err != nil {
return
}
2023-03-03 01:56:32 +00:00
scopeIDA := IDA(refctx.ScopeMap)
2023-03-01 23:38:02 +00:00
if len(scopeIDA) == 0 {
return
}
linkIDA := link.IDA()
if len(linkIDA) == 0 {
return
}
2023-03-03 02:01:51 +00:00
if linkIDA[0] == "root" {
c.errorf(refctx.Key.Key, "cannot refer to root in link")
return
}
2023-03-03 01:56:32 +00:00
// If it doesn't start with one of these reserved words, the link is definitely not a board link.
if !strings.EqualFold(linkIDA[0], "layers") && !strings.EqualFold(linkIDA[0], "scenarios") && !strings.EqualFold(linkIDA[0], "steps") && linkIDA[0] != "_" {
2023-03-01 23:38:02 +00:00
return
}
// Chop off the non-board portion of the scope, like if this is being defined on a nested object (e.g. `x.y.z`)
for i := len(scopeIDA) - 1; i > 0; i-- {
if strings.EqualFold(scopeIDA[i-1], "layers") || strings.EqualFold(scopeIDA[i-1], "scenarios") || strings.EqualFold(scopeIDA[i-1], "steps") {
2023-03-01 23:38:02 +00:00
scopeIDA = scopeIDA[:i+1]
break
}
if scopeIDA[i-1] == "root" {
scopeIDA = scopeIDA[:i]
break
}
}
// Resolve underscores
for len(linkIDA) > 0 && linkIDA[0] == "_" {
if len(scopeIDA) < 2 {
2023-03-01 23:49:47 +00:00
// IR compiler only validates bad underscore usage
// The compiler will validate if the target board actually exists
2023-03-03 01:56:32 +00:00
c.errorf(refctx.Key.Key, "invalid underscore usage")
2023-03-01 23:38:02 +00:00
return
}
// pop 2 off path per one underscore
scopeIDA = scopeIDA[:len(scopeIDA)-2]
linkIDA = linkIDA[1:]
}
if len(scopeIDA) == 0 {
scopeIDA = []string{"root"}
}
// Create the absolute path by appending scope path with value specified
scopeIDA = append(scopeIDA, linkIDA...)
2023-03-03 02:25:14 +00:00
kp := d2ast.MakeKeyPath(scopeIDA)
refctx.Key.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(d2format.Format(kp)))
2023-03-01 23:38:02 +00:00
}
2023-01-24 05:48:43 +00:00
func (c *compiler) compileEdges(refctx *RefContext) {
2023-01-18 10:06:44 +00:00
if refctx.Key.Key != nil {
2023-01-24 05:48:43 +00:00
f, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx)
2023-01-16 11:52:37 +00:00
if err != nil {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
2023-01-16 11:52:37 +00:00
return
}
2023-01-18 05:28:33 +00:00
if _, ok := f.Composite.(*Array); ok {
c.errorf(refctx.Key.Key, "cannot index into array")
return
}
2023-01-18 15:15:16 +00:00
if f.Map() == nil {
f.Composite = &Map{
2023-01-16 11:52:37 +00:00
parent: f,
}
}
2023-01-24 05:48:43 +00:00
refctx.ScopeMap = f.Map()
2023-01-16 11:52:37 +00:00
}
2023-01-18 05:28:33 +00:00
eida := NewEdgeIDs(refctx.Key)
2023-01-16 11:52:37 +00:00
for i, eid := range eida {
2023-06-26 18:57:18 +00:00
if refctx.Key != nil && refctx.Key.Value.Null != nil {
refctx.ScopeMap.DeleteEdge(eid)
continue
}
2023-01-18 10:06:44 +00:00
refctx = refctx.Copy()
refctx.Edge = refctx.Key.Edges[i]
2023-01-16 11:52:37 +00:00
var e *Edge
if eid.Index != nil {
2023-01-24 05:48:43 +00:00
ea := refctx.ScopeMap.GetEdges(eid)
2023-01-16 11:52:37 +00:00
if len(ea) == 0 {
2023-01-18 10:06:44 +00:00
c.errorf(refctx.Edge, "indexed edge does not exist")
2023-01-16 11:52:37 +00:00
continue
}
e = ea[0]
2023-01-22 08:59:02 +00:00
e.References = append(e.References, &EdgeReference{
2023-01-18 10:06:44 +00:00
Context: refctx,
})
2023-01-24 05:48:43 +00:00
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Src, refctx)
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Dst, refctx)
2023-01-16 11:52:37 +00:00
} else {
2023-01-24 05:48:43 +00:00
_, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, refctx)
2023-01-17 12:44:14 +00:00
if err != nil {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
2023-01-17 12:44:14 +00:00
continue
}
2023-01-24 05:48:43 +00:00
_, err = refctx.ScopeMap.EnsureField(refctx.Edge.Dst, refctx)
2023-01-17 12:44:14 +00:00
if err != nil {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
2023-01-17 12:44:14 +00:00
continue
}
2023-01-24 05:48:43 +00:00
e, err = refctx.ScopeMap.CreateEdge(eid, refctx)
2023-01-16 11:52:37 +00:00
if err != nil {
2023-01-18 10:06:44 +00:00
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
2023-01-16 11:52:37 +00:00
continue
}
}
2023-01-18 05:28:33 +00:00
if refctx.Key.EdgeKey != nil {
2023-01-18 15:15:16 +00:00
if e.Map_ == nil {
e.Map_ = &Map{
2023-01-16 11:52:37 +00:00
parent: e,
}
}
2023-01-18 15:15:16 +00:00
c.compileField(e.Map_, refctx.Key.EdgeKey, refctx)
2023-01-16 11:52:37 +00:00
} else {
2023-01-18 05:28:33 +00:00
if refctx.Key.Primary.Unbox() != nil {
2023-01-18 15:15:16 +00:00
e.Primary_ = &Scalar{
2023-01-16 11:52:37 +00:00
parent: e,
2023-01-18 05:28:33 +00:00
Value: refctx.Key.Primary.Unbox(),
2023-01-16 11:52:37 +00:00
}
2023-01-22 09:43:25 +00:00
}
if refctx.Key.Value.Array != nil {
c.errorf(refctx.Key.Value.Unbox(), "edges cannot be assigned arrays")
continue
2023-01-18 05:28:33 +00:00
} else if refctx.Key.Value.Map != nil {
2023-01-18 15:15:16 +00:00
if e.Map_ == nil {
e.Map_ = &Map{
2023-01-16 11:52:37 +00:00
parent: e,
}
}
2023-06-20 03:06:26 +00:00
c.compileMap(e.Map_, refctx.Key.Value.Map, refctx.ScopeAST)
2023-01-22 09:43:25 +00:00
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
e.Primary_ = &Scalar{
parent: e,
Value: refctx.Key.Value.ScalarBox().Unbox(),
}
2023-01-16 11:52:37 +00:00
}
}
}
}
2023-06-20 03:06:26 +00:00
func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map) {
2023-01-16 12:48:45 +00:00
for _, an := range a.Nodes {
var irv Value
switch v := an.Unbox().(type) {
case *d2ast.Array:
ira := &Array{
parent: dst,
}
2023-06-20 03:06:26 +00:00
c.compileArray(ira, v, scopeAST)
2023-01-16 12:48:45 +00:00
irv = ira
case *d2ast.Map:
irm := &Map{
parent: dst,
}
2023-06-20 03:06:26 +00:00
c.compileMap(irm, v, scopeAST)
2023-01-16 12:48:45 +00:00
irv = irm
case d2ast.Scalar:
irv = &Scalar{
parent: dst,
Value: v,
}
case *d2ast.Import:
n, ok := c._import(v)
if !ok {
continue
}
switch n := n.(type) {
case *Field:
if v.Spread {
a, ok := n.Composite.(*Array)
if !ok {
c.errorf(v, "can only spread import array into array")
continue
}
dst.Values = append(dst.Values, a.Values...)
continue
}
if n.Composite != nil {
irv = n.Composite
} else {
irv = n.Primary_
}
case *Map:
if v.Spread {
c.errorf(v, "can only spread import array into array")
continue
}
irv = n
}
2023-01-16 15:45:13 +00:00
case *d2ast.Substitution:
2023-07-13 05:20:55 +00:00
irv = &Scalar{
parent: dst,
Value: &d2ast.UnquotedString{
Value: []d2ast.InterpolationBox{{Substitution: an.Substitution}},
},
}
2023-01-16 12:48:45 +00:00
}
dst.Values = append(dst.Values, irv)
}
2023-01-16 11:52:37 +00:00
}