label works

This commit is contained in:
Alexander Wang 2023-07-10 19:24:21 -07:00
parent ce93835777
commit c9793b9a87
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
7 changed files with 365 additions and 0 deletions

View file

@ -423,6 +423,7 @@ func (s *BlockString) value() {}
func (a *Array) value() {}
func (m *Map) value() {}
func (i *Import) value() {}
func (i *Substitution) value() {}
func (n *Null) scalar() {}
func (b *Boolean) scalar() {}
@ -901,6 +902,7 @@ type ValueBox struct {
Array *Array `json:"array,omitempty"`
Map *Map `json:"map,omitempty"`
Import *Import `json:"import,omitempty"`
Substitution *Substitution `json:"substitution,omitempty"`
}
func (vb ValueBox) Unbox() Value {
@ -925,6 +927,8 @@ func (vb ValueBox) Unbox() Value {
return vb.Map
case vb.Import != nil:
return vb.Import
case vb.Substitution != nil:
return vb.Substitution
default:
return nil
}
@ -953,6 +957,8 @@ func MakeValueBox(v Value) ValueBox {
vb.Map = v
case *Import:
vb.Import = v
case *Substitution:
vb.Substitution = v
}
return vb
}

View file

@ -1,6 +1,7 @@
package d2compiler
import (
"encoding/json"
"encoding/xml"
"fmt"
"io"
@ -64,6 +65,8 @@ func compileIR(ast *d2ast.Map, m *d2ir.Map) (*d2graph.Graph, error) {
g := d2graph.NewGraph()
g.AST = ast
c.compileBoard(g, m)
b, _ := json.MarshalIndent(m, "", " ")
println("\033[1;31m--- DEBUG:", string(b), "\033[m")
if len(c.err.Errors) > 0 {
return nil, c.err
}
@ -277,6 +280,26 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
}
}
return
} else if f.Name == "vars" {
if f.Map() != nil {
if len(f.Map().Edges) > 0 {
c.errorf(f.Map().Edges[0].LastRef().AST(), "vars cannot contain an edge")
}
// for _, varField := range f.Map().Fields {
// if varField.Map() != nil {
// c.errorf(varField.LastRef().AST(), "vars must be simple")
// }
// for _, cf := range classesField.Map().Fields {
// if _, ok := d2graph.ReservedKeywords[cf.Name]; !ok {
// c.errorf(cf.LastRef().AST(), "%s is an invalid class field, must be reserved keyword", cf.Name)
// }
// if cf.Name == "class" {
// c.errorf(cf.LastRef().AST(), `"class" cannot appear within "classes"`)
// }
// }
// }
}
return
} else if isReserved {
c.compileReserved(&obj.Attributes, f)
return

View file

@ -2744,6 +2744,7 @@ func TestCompile2(t *testing.T) {
t.Run("boards", testBoards)
t.Run("seqdiagrams", testSeqDiagrams)
t.Run("nulls", testNulls)
t.Run("vars", testVars)
}
func testBoards(t *testing.T) {
@ -3168,6 +3169,78 @@ scenarios: {
})
}
func testVars(t *testing.T) {
t.Parallel()
t.Run("basic", func(t *testing.T) {
t.Parallel()
tca := []struct {
name string
skip bool
run func(t *testing.T)
}{
{
name: "label",
run: func(t *testing.T) {
g := assertCompile(t, `
vars: {
x: im a var
}
hi: ${x}
`, "")
assert.Equal(t, 1, len(g.Objects))
assert.Equal(t, "im a var", g.Objects[0].Label.Value)
},
},
}
for _, tc := range tca {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if tc.skip {
t.SkipNow()
}
tc.run(t)
})
}
})
t.Run("errors", func(t *testing.T) {
t.Parallel()
tca := []struct {
name string
skip bool
run func(t *testing.T)
}{
{
name: "missing",
run: func(t *testing.T) {
assertCompile(t, `
vars: {
x: hey
}
hi: ${z}
`, "d2/testdata/d2compiler/TestCompile2/vars/errors/missing.d2:5:1: could not resolve variable z")
},
},
}
for _, tc := range tca {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if tc.skip {
t.SkipNow()
}
tc.run(t)
})
}
})
}
func assertCompile(t *testing.T, text string, expErr string) *d2graph.Graph {
d2Path := fmt.Sprintf("d2/testdata/d2compiler/%v.d2", t.Name())
g, err := d2compiler.Compile(d2Path, strings.NewReader(text), nil)

View file

@ -53,6 +53,8 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, error) {
c.compileMap(m, ast, ast)
c.compileClasses(m)
c.compileVars(m)
c.compileSubstitutions(m)
if !c.err.Empty() {
return nil, c.err
}
@ -96,6 +98,81 @@ func (c *compiler) compileClasses(m *Map) {
}
}
func (c *compiler) compileSubstitutions(m *Map) {
vars := m.GetField("vars")
for _, f := range m.Fields {
// No substitutions within vars itself
if f.Name == "vars" {
continue
}
for _, ref := range f.References {
if ref.Context.Key != nil && ref.Context.Key.Value.Substitution != nil {
var resolved *Field
m := vars
for _, p := range ref.Context.Key.Value.Substitution.Path {
r := m.Map().GetField(p.Unbox().ScalarString())
if r == nil {
resolved = nil
break
}
m = r
resolved = r
}
if resolved == nil {
c.errorf(ref.Context.Key, "could not resolve variable %s", strings.Join(ref.Context.Key.Value.Substitution.IDA(), "."))
} else {
// TODO do i need this
// ref.Context.Key.Value = d2ast.MakeValueBox(resolved.Primary().Value)
// TODO maps
f.Primary_ = &Scalar{
parent: f,
Value: resolved.Primary().Value,
}
}
ref.Context.Key.Value.Substitution = nil
}
}
}
}
func (c *compiler) compileVars(m *Map) {
vars := m.GetField("vars")
if vars == nil || vars.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()
lVars := l.GetField("vars")
if lVars == nil {
lVars = vars.Copy(l).(*Field)
l.Fields = append(l.Fields, lVars)
} else {
base := vars.Copy(l).(*Field)
OverlayMap(base.Map(), lVars.Map())
l.DeleteField("vars")
l.Fields = append(l.Fields, base)
}
c.compileVars(l)
}
}
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))
@ -244,6 +321,10 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext)
c.compileClasses(f.Map())
}
}
} else if refctx.Key.Value.Substitution != nil {
// b, _ := json.MarshalIndent(refctx.Key.Value.Substitution.IDA(), "", " ")
// println("\033[1;31m--- DEBUG:", string(b), "\033[m")
// println("\033[1;31m--- DEBUG:", "=======what===============", "\033[m")
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
// If the link is a board, we need to transform it into an absolute path.
if f.Name == "link" {

View file

@ -1596,6 +1596,9 @@ func (p *parser) parseValue() d2ast.ValueBox {
case '@':
box.Import = p.parseImport(false)
return box
case '$':
box.Substitution = p.parseSubstitution(false)
return box
}
p.replay(r)

View file

@ -0,0 +1,168 @@
{
"graph": {
"name": "",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,0:0:0-5:0:34",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,1:0:1-3:1:24",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,1:0:1-1:4:5",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,1:0:1-1:4:5",
"value": [
{
"string": "vars",
"raw_string": "vars"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,1:6:7-3:1:24",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,2:2:11-2:13:22",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,2:2:11-2:3:12",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,2:2:11-2:3:12",
"value": [
{
"string": "x",
"raw_string": "x"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,2:5:14-2:13:22",
"value": [
{
"string": "im a var",
"raw_string": "im a var"
}
]
}
}
}
}
]
}
}
}
},
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,4:0:25-4:8:33",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,4:0:25-4:2:27",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,4:0:25-4:2:27",
"value": [
{
"string": "hi",
"raw_string": "hi"
}
]
}
}
]
},
"primary": {},
"value": {}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": null,
"objects": [
{
"id": "hi",
"id_val": "hi",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,4:0:25-4:2:27",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/vars/basic/label.d2,4:0:25-4:2:27",
"value": [
{
"string": "hi",
"raw_string": "hi"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "im a var"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
]
},
"err": null
}

View file

@ -0,0 +1,11 @@
{
"graph": null,
"err": {
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile2/vars/errors/missing.d2,4:0:20-4:8:28",
"errmsg": "d2/testdata/d2compiler/TestCompile2/vars/errors/missing.d2:5:1: could not resolve variable z"
}
]
}
}