2023-01-16 11:52:37 +00:00
|
|
|
package d2ir
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"oss.terrastruct.com/d2/d2ast"
|
|
|
|
|
"oss.terrastruct.com/d2/d2parser"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type compiler struct {
|
|
|
|
|
err d2parser.ParseError
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2023-01-18 11:45:34 +00:00
|
|
|
func Compile(ast *d2ast.Map) (*Map, error) {
|
2023-01-18 01:39:31 +00:00
|
|
|
c := &compiler{}
|
2023-01-24 06:45:21 +00:00
|
|
|
m := &Map{}
|
|
|
|
|
m.initRoot()
|
|
|
|
|
m.parent.(*Field).References[0].Context.Scope = ast
|
2023-01-18 15:15:16 +00:00
|
|
|
c.compileMap(m, ast)
|
|
|
|
|
c.compileScenarios(m)
|
|
|
|
|
c.compileSteps(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-01-18 15:15:16 +00:00
|
|
|
func (c *compiler) compileScenarios(m *Map) {
|
|
|
|
|
scenariosf := m.GetField("scenarios")
|
|
|
|
|
if scenariosf == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
scenarios := scenariosf.Map()
|
|
|
|
|
if scenarios == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, sf := range scenarios.Fields {
|
2023-02-22 22:59:05 +00:00
|
|
|
if sf.Map() == nil || sf.Primary() != nil {
|
|
|
|
|
c.errorf(sf.References[0].Context.Key, "invalid scenario")
|
2023-01-22 08:21:21 +00:00
|
|
|
continue
|
2023-01-18 15:15:16 +00:00
|
|
|
}
|
2023-01-22 02:52:33 +00:00
|
|
|
base := m.CopyBase(sf)
|
2023-02-02 18:30:54 +00:00
|
|
|
OverlayMap(base, sf.Map())
|
|
|
|
|
sf.Composite = base
|
2023-01-18 15:47:42 +00:00
|
|
|
c.compileScenarios(sf.Map())
|
|
|
|
|
c.compileSteps(sf.Map())
|
2023-01-18 15:15:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *compiler) compileSteps(m *Map) {
|
|
|
|
|
stepsf := m.GetField("steps")
|
|
|
|
|
if stepsf == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
steps := stepsf.Map()
|
|
|
|
|
if steps == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for i, sf := range steps.Fields {
|
2023-02-22 22:59:05 +00:00
|
|
|
if sf.Map() == nil || sf.Primary() != nil {
|
|
|
|
|
c.errorf(sf.References[0].Context.Key, "invalid step")
|
2023-02-23 00:21:16 +00:00
|
|
|
break
|
2023-01-18 15:15:16 +00:00
|
|
|
}
|
|
|
|
|
var base *Map
|
|
|
|
|
if i == 0 {
|
2023-01-22 02:52:33 +00:00
|
|
|
base = m.CopyBase(sf)
|
2023-01-18 15:15:16 +00:00
|
|
|
} else {
|
2023-01-22 02:52:33 +00:00
|
|
|
base = steps.Fields[i-1].Map().CopyBase(sf)
|
2023-01-18 15:15:16 +00:00
|
|
|
}
|
2023-02-02 18:30:54 +00:00
|
|
|
OverlayMap(base, sf.Map())
|
|
|
|
|
sf.Composite = base
|
2023-01-18 15:47:42 +00:00
|
|
|
c.compileScenarios(sf.Map())
|
|
|
|
|
c.compileSteps(sf.Map())
|
2023-01-18 15:15:16 +00:00
|
|
|
}
|
2023-01-16 11:52:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *compiler) compileMap(dst *Map, ast *d2ast.Map) {
|
|
|
|
|
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-01-18 05:28:33 +00:00
|
|
|
})
|
2023-01-16 11:52:37 +00:00
|
|
|
case n.Substitution != nil:
|
|
|
|
|
panic("TODO")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
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-01-18 10:06:44 +00:00
|
|
|
c.compileArray(a, refctx.Key.Value.Array)
|
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-01-18 15:15:16 +00:00
|
|
|
c.compileMap(f.Map(), refctx.Key.Value.Map)
|
2023-01-18 10:06:44 +00:00
|
|
|
} else if refctx.Key.Value.ScalarBox().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.Value.ScalarBox().Unbox(),
|
2023-01-16 11:52:37 +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-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-01-18 15:15:16 +00:00
|
|
|
c.compileMap(e.Map_, refctx.Key.Value.Map)
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *compiler) compileArray(dst *Array, a *d2ast.Array) {
|
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,
|
|
|
|
|
}
|
|
|
|
|
c.compileArray(ira, v)
|
|
|
|
|
irv = ira
|
|
|
|
|
case *d2ast.Map:
|
|
|
|
|
irm := &Map{
|
|
|
|
|
parent: dst,
|
|
|
|
|
}
|
|
|
|
|
c.compileMap(irm, v)
|
|
|
|
|
irv = irm
|
|
|
|
|
case d2ast.Scalar:
|
|
|
|
|
irv = &Scalar{
|
|
|
|
|
parent: dst,
|
|
|
|
|
Value: v,
|
|
|
|
|
}
|
2023-01-16 15:45:13 +00:00
|
|
|
case *d2ast.Substitution:
|
2023-01-27 20:37:08 +00:00
|
|
|
// panic("TODO")
|
2023-01-16 12:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dst.Values = append(dst.Values, irv)
|
|
|
|
|
}
|
2023-01-16 11:52:37 +00:00
|
|
|
}
|