diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go
index 446a8975b..464faf196 100644
--- a/d2ast/d2ast.go
+++ b/d2ast/d2ast.go
@@ -1065,9 +1065,17 @@ func MakeKeyPath(a []string) *KeyPath {
return kp
}
-func (kp *KeyPath) IDA() (ida []string) {
+func MakeKeyPathString(a []String) *KeyPath {
+ kp := &KeyPath{}
+ for _, el := range a {
+ kp.Path = append(kp.Path, MakeValueBox(RawString(el.ScalarString(), true)).StringBox())
+ }
+ return kp
+}
+
+func (kp *KeyPath) IDA() (ida []String) {
for _, el := range kp.Path {
- ida = append(ida, el.Unbox().ScalarString())
+ ida = append(ida, el.Unbox())
}
return ida
}
@@ -1578,9 +1586,9 @@ func (s *Substitution) IDA() (ida []string) {
return ida
}
-func (i *Import) IDA() (ida []string) {
+func (i *Import) IDA() (ida []String) {
for _, el := range i.Path[1:] {
- ida = append(ida, el.Unbox().ScalarString())
+ ida = append(ida, el.Unbox())
}
return ida
}
diff --git a/d2compiler/compile.go b/d2compiler/compile.go
index c7573e21e..221bb9d67 100644
--- a/d2compiler/compile.go
+++ b/d2compiler/compile.go
@@ -110,7 +110,7 @@ func (c *compiler) compileBoard(g *d2graph.Graph, ir *d2ir.Map) *d2graph.Graph {
}
func (c *compiler) compileBoardsField(g *d2graph.Graph, ir *d2ir.Map, fieldName string) {
- boards := ir.GetField(fieldName)
+ boards := ir.GetField(d2ast.FlatUnquotedString(fieldName))
if boards.Map() == nil {
return
}
@@ -224,7 +224,7 @@ func (c *compiler) errorf(n d2ast.Node, f string, v ...interface{}) {
}
func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) {
- class := m.GetField("class")
+ class := m.GetField(d2ast.FlatUnquotedString("class"))
if class != nil {
var classNames []string
if class.Primary() != nil {
@@ -265,7 +265,7 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) {
}
}
}
- shape := m.GetField("shape")
+ shape := m.GetField(d2ast.FlatUnquotedString("shape"))
if shape != nil {
if shape.Composite != nil {
c.errorf(shape.LastPrimaryKey(), "reserved field shape does not accept composite")
@@ -388,7 +388,7 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
IsVar: d2ir.IsVar(fr.Context_.ScopeMap),
}
if fr.Context_.ScopeMap != nil && !d2ir.IsVar(fr.Context_.ScopeMap) {
- scopeObjIDA := d2graphIDA(d2ir.BoardIDA(fr.Context_.ScopeMap))
+ scopeObjIDA := d2ir.BoardIDA(fr.Context_.ScopeMap)
r.ScopeObj = obj.Graph.Root.EnsureChild(scopeObjIDA)
}
obj.References = append(obj.References, r)
@@ -766,7 +766,7 @@ func compileStyleFieldInit(attrs *d2graph.Attributes, f *d2ir.Field) {
}
func (c *compiler) compileEdge(obj *d2graph.Object, e *d2ir.Edge) {
- edge, err := obj.Connect(d2graphIDA(e.ID.SrcPath), d2graphIDA(e.ID.DstPath), e.ID.SrcArrow, e.ID.DstArrow, "")
+ edge, err := obj.Connect(e.ID.SrcPath, e.ID.DstPath, e.ID.SrcArrow, e.ID.DstArrow, "")
if err != nil {
c.errorf(e.References[0].AST(), err.Error())
return
@@ -790,7 +790,7 @@ func (c *compiler) compileEdge(obj *d2graph.Object, e *d2ir.Edge) {
ScopeObj: obj,
}
if er.Context_.ScopeMap != nil && !d2ir.IsVar(er.Context_.ScopeMap) {
- scopeObjIDA := d2graphIDA(d2ir.BoardIDA(er.Context_.ScopeMap))
+ scopeObjIDA := d2ir.BoardIDA(er.Context_.ScopeMap)
r.ScopeObj = edge.Src.Graph.Root.EnsureChild(scopeObjIDA)
}
edge.References = append(edge.References, r)
@@ -798,7 +798,7 @@ func (c *compiler) compileEdge(obj *d2graph.Object, e *d2ir.Edge) {
}
func (c *compiler) compileEdgeMap(edge *d2graph.Edge, m *d2ir.Map) {
- class := m.GetField("class")
+ class := m.GetField(d2ast.FlatUnquotedString("class"))
if class != nil {
var classNames []string
if class.Primary() != nil {
@@ -1230,7 +1230,11 @@ func (c *compiler) validateBoardLinks(g *d2graph.Graph) {
continue
}
- if slices.Equal(linkKey.IDA(), obj.Graph.IDA()) {
+ formattedIDA := []string{}
+ for _, id := range linkKey.IDA() {
+ formattedIDA = append(formattedIDA, id.ScalarString())
+ }
+ if slices.Equal(formattedIDA, obj.Graph.IDA()) {
obj.Link = nil
continue
}
@@ -1246,34 +1250,34 @@ func (c *compiler) validateBoardLinks(g *d2graph.Graph) {
}
}
-func hasBoard(root *d2graph.Graph, ida []string) bool {
+func hasBoard(root *d2graph.Graph, ida []d2ast.String) bool {
if len(ida) == 0 {
return true
}
- if ida[0] == "root" {
+ if ida[0].ScalarString() == "root" && ida[0].IsUnquoted() {
return hasBoard(root, ida[1:])
}
id := ida[0]
if len(ida) == 1 {
- return root.Name == id
+ return root.Name == id.ScalarString()
}
nextID := ida[1]
- switch id {
+ switch id.ScalarString() {
case "layers":
for _, b := range root.Layers {
- if b.Name == nextID {
+ if b.Name == nextID.ScalarString() {
return hasBoard(b, ida[2:])
}
}
case "scenarios":
for _, b := range root.Scenarios {
- if b.Name == nextID {
+ if b.Name == nextID.ScalarString() {
return hasBoard(b, ida[2:])
}
}
case "steps":
for _, b := range root.Steps {
- if b.Name == nextID {
+ if b.Name == nextID.ScalarString() {
return hasBoard(b, ida[2:])
}
}
@@ -1288,13 +1292,6 @@ func init() {
}
}
-func d2graphIDA(irIDA []string) (ida []d2ast.String) {
- for _, el := range irIDA {
- ida = append(ida, d2ast.RawString(el, true))
- }
- return ida
-}
-
// Unused for now until shape: edge_group
func (c *compiler) preprocessSeqDiagrams(m *d2ir.Map) {
for _, f := range m.Fields {
@@ -1350,8 +1347,8 @@ func (c *compiler) preprocessEdgeGroup(seqDiagram, m *d2ir.Map) {
f := srcParent.GetField(el)
if !isEdgeGroup(f) {
for j := 0; j < i+1; j++ {
- e.ID.SrcPath = append([]string{"_"}, e.ID.SrcPath...)
- e.ID.DstPath = append([]string{"_"}, e.ID.DstPath...)
+ e.ID.SrcPath = append([]d2ast.String{d2ast.FlatUnquotedString("_")}, e.ID.SrcPath...)
+ e.ID.DstPath = append([]d2ast.String{d2ast.FlatUnquotedString("_")}, e.ID.DstPath...)
}
break
}
@@ -1361,7 +1358,7 @@ func (c *compiler) preprocessEdgeGroup(seqDiagram, m *d2ir.Map) {
}
func hoistActor(seqDiagram *d2ir.Map, f *d2ir.Field) {
- f2 := seqDiagram.GetField(f.Name.ScalarString())
+ f2 := seqDiagram.GetField(f.Name)
if f2 == nil {
seqDiagram.Fields = append(seqDiagram.Fields, f.Copy(seqDiagram).(*d2ir.Field))
} else {
@@ -1420,7 +1417,7 @@ func parentSeqDiagram(n d2ir.Node) *d2ir.Map {
}
func compileConfig(ir *d2ir.Map) (*d2target.Config, error) {
- f := ir.GetField("vars", "d2-config")
+ f := ir.GetField(d2ast.FlatUnquotedString("vars"), d2ast.FlatUnquotedString("d2-config"))
if f == nil || f.Map() == nil {
return nil, nil
}
@@ -1429,36 +1426,36 @@ func compileConfig(ir *d2ir.Map) (*d2target.Config, error) {
config := &d2target.Config{}
- f = configMap.GetField("sketch")
+ f = configMap.GetField(d2ast.FlatUnquotedString("sketch"))
if f != nil {
val, _ := strconv.ParseBool(f.Primary().Value.ScalarString())
config.Sketch = &val
}
- f = configMap.GetField("theme-id")
+ f = configMap.GetField(d2ast.FlatUnquotedString("theme-id"))
if f != nil {
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
config.ThemeID = go2.Pointer(int64(val))
}
- f = configMap.GetField("dark-theme-id")
+ f = configMap.GetField(d2ast.FlatUnquotedString("dark-theme-id"))
if f != nil {
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
config.DarkThemeID = go2.Pointer(int64(val))
}
- f = configMap.GetField("pad")
+ f = configMap.GetField(d2ast.FlatUnquotedString("pad"))
if f != nil {
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
config.Pad = go2.Pointer(int64(val))
}
- f = configMap.GetField("layout-engine")
+ f = configMap.GetField(d2ast.FlatUnquotedString("layout-engine"))
if f != nil {
config.LayoutEngine = go2.Pointer(f.Primary().Value.ScalarString())
}
- f = configMap.GetField("theme-overrides")
+ f = configMap.GetField(d2ast.FlatUnquotedString("theme-overrides"))
if f != nil {
overrides, err := compileThemeOverrides(f.Map())
if err != nil {
@@ -1466,7 +1463,7 @@ func compileConfig(ir *d2ir.Map) (*d2target.Config, error) {
}
config.ThemeOverrides = overrides
}
- f = configMap.GetField("dark-theme-overrides")
+ f = configMap.GetField(d2ast.FlatUnquotedString("dark-theme-overrides"))
if f != nil {
overrides, err := compileThemeOverrides(f.Map())
if err != nil {
@@ -1474,7 +1471,7 @@ func compileConfig(ir *d2ir.Map) (*d2target.Config, error) {
}
config.DarkThemeOverrides = overrides
}
- f = configMap.GetField("data")
+ f = configMap.GetField(d2ast.FlatUnquotedString("data"))
if f != nil && f.Map() != nil {
config.Data = make(map[string]interface{})
for _, f := range f.Map().Fields {
diff --git a/d2ir/compile.go b/d2ir/compile.go
index 07e882a15..a112a99e3 100644
--- a/d2ir/compile.go
+++ b/d2ir/compile.go
@@ -88,12 +88,12 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, []string, error) {
}
func (c *compiler) overlayClasses(m *Map) {
- classes := m.GetField("classes")
+ classes := m.GetField(d2ast.FlatUnquotedString("classes"))
if classes == nil || classes.Map() == nil {
return
}
- layersField := m.GetField("layers")
+ layersField := m.GetField(d2ast.FlatUnquotedString("layers"))
if layersField == nil {
return
}
@@ -108,7 +108,7 @@ func (c *compiler) overlayClasses(m *Map) {
continue
}
l := lf.Map()
- lClasses := l.GetField("classes")
+ lClasses := l.GetField(d2ast.FlatUnquotedString("classes"))
if lClasses == nil {
lClasses = classes.Copy(l).(*Field)
@@ -153,7 +153,7 @@ func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
} else if f.Map() != nil {
if f.Name != nil && f.Name.ScalarString() == "vars" && f.Name.IsUnquoted() {
c.compileSubstitutions(f.Map(), varsStack)
- c.validateConfigs(f.Map().GetField("d2-config"))
+ c.validateConfigs(f.Map().GetField(d2ast.FlatUnquotedString("d2-config")))
} else {
c.compileSubstitutions(f.Map(), varsStack)
}
@@ -381,7 +381,7 @@ func (c *compiler) resolveSubstitution(vars *Map, node Node, substitution *d2ast
parent := ParentField(node)
for i, p := range substitution.Path {
- f := vars.GetField(p.Unbox().ScalarString())
+ f := vars.GetField(p.Unbox())
if f == nil {
return nil
}
@@ -445,7 +445,7 @@ func (g *globContext) copyApplied(from *globContext) {
func (g *globContext) prefixed(dst *Map) *globContext {
g2 := g.copy()
- prefix := d2ast.MakeKeyPath(RelIDA(g2.refctx.ScopeMap, dst))
+ prefix := d2ast.MakeKeyPathString(RelIDA(g2.refctx.ScopeMap, dst))
g2.refctx.Key = g2.refctx.Key.Copy()
if g2.refctx.Key.Key != nil {
prefix.Path = append(prefix.Path, g2.refctx.Key.Key.Path...)
@@ -483,9 +483,9 @@ func (c *compiler) ampersandFilterMap(dst *Map, ast, scopeAST *d2ast.Map) bool {
}
var ks string
if gctx.refctx.Key.HasMultiGlob() {
- ks = d2format.Format(d2ast.MakeKeyPath(IDA(dst)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(IDA(dst)))
} else {
- ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(dst)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(BoardIDA(dst)))
}
delete(gctx.appliedFields, ks)
delete(gctx.appliedEdges, ks)
@@ -992,11 +992,11 @@ func (c *compiler) extendLinks(m *Map, importF *Field, importDir string) {
}
for _, id := range linkIDA[1:] {
- if id == "_" {
+ if id.ScalarString() == "_" && id.IsUnquoted() {
if len(linkIDA) < 2 || len(importIDA) < 2 {
break
}
- linkIDA = append([]string{linkIDA[0]}, linkIDA[2:]...)
+ linkIDA = append([]d2ast.String{linkIDA[0]}, linkIDA[2:]...)
importIDA = importIDA[:len(importIDA)-2]
} else {
break
@@ -1004,7 +1004,7 @@ func (c *compiler) extendLinks(m *Map, importF *Field, importDir string) {
}
extendedIDA := append(importIDA, linkIDA[1:]...)
- kp := d2ast.MakeKeyPath(extendedIDA)
+ kp := d2ast.MakeKeyPathString(extendedIDA)
s := d2format.Format(kp)
f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(s)).ScalarBox().Unbox()
}
@@ -1046,30 +1046,34 @@ func (c *compiler) compileLink(f *Field, refctx *RefContext) {
return
}
- if linkIDA[0] == "root" {
+ if linkIDA[0].ScalarString() == "root" && linkIDA[0].IsUnquoted() {
c.errorf(refctx.Key.Key, "cannot refer to root in link")
return
}
+ if !linkIDA[0].IsUnquoted() {
+ return
+ }
+
// 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] != "_" {
+ if !strings.EqualFold(linkIDA[0].ScalarString(), "layers") && !strings.EqualFold(linkIDA[0].ScalarString(), "scenarios") && !strings.EqualFold(linkIDA[0].ScalarString(), "steps") && linkIDA[0].ScalarString() != "_" {
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") {
+ if scopeIDA[i-1].IsUnquoted() && (strings.EqualFold(scopeIDA[i-1].ScalarString(), "layers") || strings.EqualFold(scopeIDA[i-1].ScalarString(), "scenarios") || strings.EqualFold(scopeIDA[i-1].ScalarString(), "steps")) {
scopeIDA = scopeIDA[:i+1]
break
}
- if scopeIDA[i-1] == "root" {
+ if scopeIDA[i-1].ScalarString() == "root" && scopeIDA[i-1].IsUnquoted() {
scopeIDA = scopeIDA[:i]
break
}
}
// Resolve underscores
- for len(linkIDA) > 0 && linkIDA[0] == "_" {
+ for len(linkIDA) > 0 && linkIDA[0].ScalarString() == "_" && linkIDA[0].IsUnquoted() {
if len(scopeIDA) < 2 {
// Leave the underscore. It will fail in compiler as a standalone board,
// but if imported, will get further resolved in extendLinks
@@ -1080,12 +1084,12 @@ func (c *compiler) compileLink(f *Field, refctx *RefContext) {
linkIDA = linkIDA[1:]
}
if len(scopeIDA) == 0 {
- scopeIDA = []string{"root"}
+ scopeIDA = []d2ast.String{d2ast.FlatUnquotedString("root")}
}
// Create the absolute path by appending scope path with value specified
scopeIDA = append(scopeIDA, linkIDA...)
- kp := d2ast.MakeKeyPath(scopeIDA)
+ kp := d2ast.MakeKeyPathString(scopeIDA)
f.Primary_.Value = d2ast.FlatUnquotedString(d2format.Format(kp))
}
diff --git a/d2ir/d2ir.go b/d2ir/d2ir.go
index 690b5acb7..9400761ee 100644
--- a/d2ir/d2ir.go
+++ b/d2ir/d2ir.go
@@ -377,11 +377,11 @@ func (f *Field) LastRef() Reference {
}
type EdgeID struct {
- SrcPath []string `json:"src_path"`
- SrcArrow bool `json:"src_arrow"`
+ SrcPath []d2ast.String `json:"src_path"`
+ SrcArrow bool `json:"src_arrow"`
- DstPath []string `json:"dst_path"`
- DstArrow bool `json:"dst_arrow"`
+ DstPath []d2ast.String `json:"dst_path"`
+ DstArrow bool `json:"dst_arrow"`
// If nil, then any EdgeID with equal src/dst/arrows matches.
Index *int `json:"index"`
@@ -409,8 +409,8 @@ func (eid *EdgeID) Copy() *EdgeID {
tmp := *eid
eid = &tmp
- eid.SrcPath = append([]string(nil), eid.SrcPath...)
- eid.DstPath = append([]string(nil), eid.DstPath...)
+ eid.SrcPath = append([]d2ast.String(nil), eid.SrcPath...)
+ eid.DstPath = append([]d2ast.String(nil), eid.DstPath...)
return eid
}
@@ -428,7 +428,7 @@ func (eid *EdgeID) Match(eid2 *EdgeID) bool {
return false
}
for i, s := range eid.SrcPath {
- if !strings.EqualFold(s, eid2.SrcPath[i]) {
+ if !strings.EqualFold(s.ScalarString(), eid2.SrcPath[i].ScalarString()) {
return false
}
}
@@ -440,7 +440,7 @@ func (eid *EdgeID) Match(eid2 *EdgeID) bool {
return false
}
for i, s := range eid.DstPath {
- if !strings.EqualFold(s, eid2.DstPath[i]) {
+ if !strings.EqualFold(s.ScalarString(), eid2.DstPath[i].ScalarString()) {
return false
}
}
@@ -450,21 +450,21 @@ func (eid *EdgeID) Match(eid2 *EdgeID) bool {
// 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) {
+func (eid *EdgeID) resolve(m *Map) (_ *EdgeID, _ *Map, common []d2ast.String, _ error) {
eid = eid.Copy()
maxUnderscores := go2.Max(countUnderscores(eid.SrcPath), countUnderscores(eid.DstPath))
for i := 0; i < maxUnderscores; i++ {
- if eid.SrcPath[0] == "_" {
+ if eid.SrcPath[0].ScalarString() == "_" && eid.SrcPath[0].IsUnquoted() {
eid.SrcPath = eid.SrcPath[1:]
} else {
mf := ParentField(m)
- eid.SrcPath = append([]string{mf.Name.ScalarString()}, eid.SrcPath...)
+ eid.SrcPath = append([]d2ast.String{mf.Name}, eid.SrcPath...)
}
- if eid.DstPath[0] == "_" {
+ if eid.DstPath[0].ScalarString() == "_" && eid.DstPath[0].IsUnquoted() {
eid.DstPath = eid.DstPath[1:]
} else {
mf := ParentField(m)
- eid.DstPath = append([]string{mf.Name.ScalarString()}, eid.DstPath...)
+ eid.DstPath = append([]d2ast.String{mf.Name}, eid.DstPath...)
}
m = ParentMap(m)
if m == nil {
@@ -473,7 +473,7 @@ func (eid *EdgeID) resolve(m *Map) (_ *EdgeID, _ *Map, common []string, _ error)
}
for len(eid.SrcPath) > 1 && len(eid.DstPath) > 1 {
- if !strings.EqualFold(eid.SrcPath[0], eid.DstPath[0]) || strings.Contains(eid.SrcPath[0], "*") {
+ if !strings.EqualFold(eid.SrcPath[0].ScalarString(), eid.DstPath[0].ScalarString()) || strings.Contains(eid.SrcPath[0].ScalarString(), "*") {
return eid, m, common, nil
}
common = append(common, eid.SrcPath[0])
@@ -702,9 +702,9 @@ func (m *Map) EdgeCountRecursive() int {
func (m *Map) GetClassMap(name string) *Map {
root := RootMap(m)
- classes := root.Map().GetField("classes")
+ classes := root.Map().GetField(d2ast.FlatUnquotedString("classes"))
if classes != nil && classes.Map() != nil {
- class := classes.Map().GetField(name)
+ class := classes.Map().GetField(d2ast.FlatUnquotedString(name))
if class != nil && class.Map() != nil {
return class.Map()
}
@@ -712,8 +712,8 @@ func (m *Map) GetClassMap(name string) *Map {
return nil
}
-func (m *Map) GetField(ida ...string) *Field {
- for len(ida) > 0 && ida[0] == "_" {
+func (m *Map) GetField(ida ...d2ast.String) *Field {
+ for len(ida) > 0 && ida[0].ScalarString() == "_" && ida[0].IsUnquoted() {
m = ParentMap(m)
if m == nil {
return nil
@@ -722,7 +722,7 @@ func (m *Map) GetField(ida ...string) *Field {
return m.getField(ida)
}
-func (m *Map) getField(ida []string) *Field {
+func (m *Map) getField(ida []d2ast.String) *Field {
if len(ida) == 0 {
return nil
}
@@ -730,7 +730,7 @@ func (m *Map) getField(ida []string) *Field {
s := ida[0]
rest := ida[1:]
- if s == "_" {
+ if s.ScalarString() == "_" && s.IsUnquoted() {
return nil
}
@@ -738,7 +738,10 @@ func (m *Map) getField(ida []string) *Field {
if f.Name == nil {
continue
}
- if !strings.EqualFold(f.Name.ScalarString(), s) {
+ if !strings.EqualFold(f.Name.ScalarString(), s.ScalarString()) {
+ continue
+ }
+ if f.Name.IsUnquoted() != s.IsUnquoted() {
continue
}
if len(rest) == 0 {
@@ -788,9 +791,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
if gctx != nil {
var ks string
if refctx.Key.HasMultiGlob() {
- ks = d2format.Format(d2ast.MakeKeyPath(IDA(f)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(IDA(f)))
} else {
- ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(BoardIDA(f)))
}
if !kp.HasGlob() {
if !passthrough {
@@ -887,7 +890,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
return d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", headString)
}
- if findBoardKeyword(headString) != -1 && head.IsUnquoted() && NodeBoardKind(m) == "" {
+ if findBoardKeyword(head) != -1 && head.IsUnquoted() && NodeBoardKind(m) == "" {
return d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", headString)
}
@@ -945,9 +948,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
for _, grefctx := range c.globRefContextStack {
var ks string
if grefctx.Key.HasMultiGlob() {
- ks = d2format.Format(d2ast.MakeKeyPath(IDA(f)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(IDA(f)))
} else {
- ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(BoardIDA(f)))
}
gctx2 := c.getGlobContext(grefctx)
gctx2.appliedFields[ks] = struct{}{}
@@ -1090,7 +1093,7 @@ func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, gctx *globContext, ea *[
}
if len(common) > 0 {
- commonKP := d2ast.MakeKeyPath(common)
+ commonKP := d2ast.MakeKeyPathString(common)
lastMatch := 0
for i, el := range commonKP.Path {
for j := lastMatch; j < len(refctx.Edge.Src.Path); j++ {
@@ -1142,9 +1145,9 @@ func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, gctx *globContext, ea *[
if gctx != nil {
var ks string
if refctx.Key.HasMultiGlob() {
- ks = d2format.Format(d2ast.MakeKeyPath(IDA(e)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(IDA(e)))
} else {
- ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(e)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(BoardIDA(e)))
}
if _, ok := gctx.appliedEdges[ks]; ok {
continue
@@ -1186,7 +1189,7 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, gctx *globContext, c *
return d2parser.Errorf(refctx.Edge, err.Error())
}
if len(common) > 0 {
- commonKP := d2ast.MakeKeyPath(common)
+ commonKP := d2ast.MakeKeyPathString(common)
lastMatch := 0
for i, el := range commonKP.Path {
for j := lastMatch; j < len(refctx.Edge.Src.Path); j++ {
@@ -1303,7 +1306,7 @@ func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, gctx *globContext, c
return nil, d2parser.Errorf(refctx.Edge, err.Error())
}
if len(common) > 0 {
- commonKP := d2ast.MakeKeyPath(common)
+ commonKP := d2ast.MakeKeyPathString(common)
lastMatch := 0
for i, el := range commonKP.Path {
for j := lastMatch; j < len(refctx.Edge.Src.Path); j++ {
@@ -1360,9 +1363,9 @@ func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, gctx *globContext, c
e2.ID = e2.ID.Copy()
e2.ID.Index = nil
if refctx.Key.HasMultiGlob() {
- ks = d2format.Format(d2ast.MakeKeyPath(IDA(e2)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(IDA(e2)))
} else {
- ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(e2)))
+ ks = d2format.Format(d2ast.MakeKeyPathString(BoardIDA(e2)))
}
if _, ok := gctx.appliedEdges[ks]; ok {
return nil, nil
@@ -1401,11 +1404,11 @@ func (f *Field) AST() d2ast.Node {
func (e *Edge) AST() d2ast.Node {
astEdge := &d2ast.Edge{}
- astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath)
+ astEdge.Src = d2ast.MakeKeyPathString(e.ID.SrcPath)
if e.ID.SrcArrow {
astEdge.SrcArrow = "<"
}
- astEdge.Dst = d2ast.MakeKeyPath(e.ID.DstPath)
+ astEdge.Dst = d2ast.MakeKeyPathString(e.ID.DstPath)
if e.ID.DstArrow {
astEdge.DstArrow = ">"
}
@@ -1424,7 +1427,7 @@ func (e *Edge) AST() d2ast.Node {
return k
}
-func (e *Edge) IDString() string {
+func (e *Edge) IDString() d2ast.String {
ast := e.AST().(*d2ast.Key)
if e.ID.Index != nil {
ast.EdgeIndex = &d2ast.EdgeIndex{
@@ -1433,7 +1436,8 @@ func (e *Edge) IDString() string {
}
ast.Primary = d2ast.ScalarBox{}
ast.Value = d2ast.ValueBox{}
- return d2format.Format(ast)
+ formatted := d2format.Format(ast)
+ return d2ast.FlatUnquotedString(formatted)
}
func (a *Array) AST() d2ast.Node {
@@ -1465,7 +1469,7 @@ func (m *Map) AST() d2ast.Node {
func (m *Map) appendFieldReferences(i int, kp *d2ast.KeyPath, refctx *RefContext, c *compiler) {
sb := kp.Path[i]
- f := m.GetField(sb.Unbox().ScalarString())
+ f := m.GetField(sb.Unbox())
if f == nil {
return
}
@@ -1563,7 +1567,7 @@ func ParentShape(n Node) string {
f, ok := n.(*Field)
if ok {
if f.Map() != nil {
- shapef := f.Map().GetField("shape")
+ shapef := f.Map().GetField(d2ast.FlatUnquotedString("shape"))
if shapef != nil && shapef.Primary() != nil {
return shapef.Primary().Value.ScalarString()
}
@@ -1576,30 +1580,30 @@ func ParentShape(n Node) string {
}
}
-func countUnderscores(p []string) int {
+func countUnderscores(p []d2ast.String) int {
for i, el := range p {
- if el != "_" {
+ if el.ScalarString() != "_" || el.IsUnquoted() {
return i
}
}
return 0
}
-func findBoardKeyword(ida ...string) int {
+func findBoardKeyword(ida ...d2ast.String) int {
for i := range ida {
- if _, ok := d2ast.BoardKeywords[ida[i]]; ok {
+ if _, ok := d2ast.BoardKeywords[ida[i].ScalarString()]; ok && ida[i].IsUnquoted() {
return i
}
}
return -1
}
-func findProhibitedEdgeKeyword(ida ...string) int {
+func findProhibitedEdgeKeyword(ida ...d2ast.String) int {
for i := range ida {
- if _, ok := d2ast.SimpleReservedKeywords[ida[i]]; ok {
+ if _, ok := d2ast.SimpleReservedKeywords[ida[i].ScalarString()]; ok && ida[i].IsUnquoted() {
return i
}
- if _, ok := d2ast.ReservedKeywordHolders[ida[i]]; ok {
+ if _, ok := d2ast.ReservedKeywordHolders[ida[i].ScalarString()]; ok && ida[i].IsUnquoted() {
return i
}
}
@@ -1643,7 +1647,7 @@ func parentPrimaryKey(n Node) *d2ast.Key {
}
// BoardIDA returns the absolute path to n from the nearest board root.
-func BoardIDA(n Node) (ida []string) {
+func BoardIDA(n Node) (ida []d2ast.String) {
for {
switch n := n.(type) {
case *Field:
@@ -1651,7 +1655,7 @@ func BoardIDA(n Node) (ida []string) {
reverseIDA(ida)
return ida
}
- ida = append(ida, n.Name.ScalarString())
+ ida = append(ida, n.Name)
case *Edge:
ida = append(ida, n.IDString())
}
@@ -1664,11 +1668,11 @@ func BoardIDA(n Node) (ida []string) {
}
// IDA returns the absolute path to n.
-func IDA(n Node) (ida []string) {
+func IDA(n Node) (ida []d2ast.String) {
for {
switch n := n.(type) {
case *Field:
- ida = append(ida, n.Name.ScalarString())
+ ida = append(ida, n.Name)
if n.Root() {
reverseIDA(ida)
return ida
@@ -1685,17 +1689,17 @@ func IDA(n Node) (ida []string) {
}
// RelIDA returns the path to n relative to p.
-func RelIDA(p, n Node) (ida []string) {
+func RelIDA(p, n Node) (ida []d2ast.String) {
for {
switch n := n.(type) {
case *Field:
- ida = append(ida, n.Name.ScalarString())
+ ida = append(ida, n.Name)
if n.Root() {
reverseIDA(ida)
return ida
}
case *Edge:
- ida = append(ida, n.String())
+ ida = append(ida, d2ast.FlatUnquotedString(n.String()))
}
n = n.Parent()
f, fok := n.(*Field)
@@ -1707,11 +1711,11 @@ func RelIDA(p, n Node) (ida []string) {
}
}
-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
+func reverseIDA[T any](slice []T) {
+ for i := 0; i < len(slice)/2; i++ {
+ tmp := slice[i]
+ slice[i] = slice[len(slice)-i-1]
+ slice[len(slice)-i-1] = tmp
}
}
@@ -1786,7 +1790,7 @@ func (m *Map) Equal(n2 Node) bool {
}
func (m *Map) InClass(key *d2ast.Key) bool {
- classes := m.Map().GetField("classes")
+ classes := m.Map().GetField(d2ast.FlatUnquotedString("classes"))
if classes == nil || classes.Map() == nil {
return false
}
@@ -1814,7 +1818,7 @@ func (m *Map) IsClass() bool {
if parentBoard.Map() == nil {
return false
}
- classes := parentBoard.Map().GetField("classes")
+ classes := parentBoard.Map().GetField(d2ast.FlatUnquotedString("classes"))
if classes == nil || classes.Map() == nil {
return false
}
@@ -1835,9 +1839,9 @@ func (m *Map) FindBoardRoot(path []string) *Map {
return m
}
- layersf := m.GetField("layers")
- scenariosf := m.GetField("scenarios")
- stepsf := m.GetField("steps")
+ layersf := m.GetField(d2ast.FlatUnquotedString("layers"))
+ scenariosf := m.GetField(d2ast.FlatUnquotedString("scenarios"))
+ stepsf := m.GetField(d2ast.FlatUnquotedString("steps"))
if layersf != nil && layersf.Map() != nil {
for _, f := range layersf.Map().Fields {
diff --git a/d2ir/merge.go b/d2ir/merge.go
index dd2e77be0..3b22b27df 100644
--- a/d2ir/merge.go
+++ b/d2ir/merge.go
@@ -2,7 +2,7 @@ package d2ir
func OverlayMap(base, overlay *Map) {
for _, of := range overlay.Fields {
- bf := base.GetField(of.Name.ScalarString())
+ bf := base.GetField(of.Name)
if bf == nil {
base.Fields = append(base.Fields, of.Copy(base).(*Field))
continue
@@ -31,7 +31,7 @@ func ExpandSubstitution(m, resolved *Map, placeholder *Field) {
}
for _, of := range resolved.Fields {
- bf := m.GetField(of.Name.ScalarString())
+ bf := m.GetField(of.Name)
if bf == nil {
m.Fields = append(m.Fields[:fi], append([]*Field{of.Copy(m).(*Field)}, m.Fields[fi:]...)...)
fi++
diff --git a/e2etests/testdata/txtar/sql-table-reserved/dagre/board.exp.json b/e2etests/testdata/txtar/sql-table-reserved/dagre/board.exp.json
new file mode 100644
index 000000000..05f561842
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-table-reserved/dagre/board.exp.json
@@ -0,0 +1,305 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "my_table",
+ "type": "sql_table",
+ "pos": {
+ "x": 0,
+ "y": 166
+ },
+ "width": 200,
+ "height": 200,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "stroke": "N7",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "static.wikia.nocookie.net",
+ "Path": "/tomandjerry/images/4/46/JerryJumbo3-1-.jpg",
+ "RawPath": "",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "OUTSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "shape",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 51,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "string",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "icon",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 35,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "string",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "width",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "height",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 53,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ }
+ ],
+ "label": "my_table",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 99,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 74,
+ "y": 0
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(x -> my_table)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "my_table",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 100,
+ "y": 66
+ },
+ {
+ "x": 100,
+ "y": 106
+ },
+ {
+ "x": 100,
+ "y": 126
+ },
+ {
+ "x": 100,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sql-table-reserved/dagre/sketch.exp.svg b/e2etests/testdata/txtar/sql-table-reserved/dagre/sketch.exp.svg
new file mode 100644
index 000000000..47427da18
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-table-reserved/dagre/sketch.exp.svg
@@ -0,0 +1,102 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/sql-table-reserved/elk/board.exp.json b/e2etests/testdata/txtar/sql-table-reserved/elk/board.exp.json
new file mode 100644
index 000000000..c53ead7f7
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-table-reserved/elk/board.exp.json
@@ -0,0 +1,300 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "my_table",
+ "type": "sql_table",
+ "pos": {
+ "x": 78,
+ "y": 217
+ },
+ "width": 200,
+ "height": 200,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "stroke": "N7",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "static.wikia.nocookie.net",
+ "Path": "/tomandjerry/images/4/46/JerryJumbo3-1-.jpg",
+ "RawPath": "",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "OUTSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "shape",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 51,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "string",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "icon",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 35,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "string",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "width",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "height",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 53,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": null,
+ "reference": ""
+ }
+ ],
+ "label": "my_table",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 99,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 12
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(x -> my_table)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "my_table",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 38.5,
+ "y": 77.6989974975586
+ },
+ {
+ "x": 38.5,
+ "y": 228.6999969482422
+ },
+ {
+ "x": 78.5,
+ "y": 228.6999969482422
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sql-table-reserved/elk/sketch.exp.svg b/e2etests/testdata/txtar/sql-table-reserved/elk/sketch.exp.svg
new file mode 100644
index 000000000..1f6898cdd
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-table-reserved/elk/sketch.exp.svg
@@ -0,0 +1,102 @@
+my_tableshapestringiconstringwidthintheightintx
+
+
+
\ No newline at end of file
diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt
index a9d4bffa8..38b0ec213 100644
--- a/e2etests/txtar.txt
+++ b/e2etests/txtar.txt
@@ -643,3 +643,17 @@ financial.style.fill: "#e8f4f8"
monitoring.style.fill: "#f8e8e8"
projects.style.fill: "#e8f8e8"
team.style.fill: "#f8f0e8"
+
+-- sql-table-reserved --
+my_table: {
+ shape: sql_table
+ icon: https://static.wikia.nocookie.net/tomandjerry/images/4/46/JerryJumbo3-1-.jpg
+ width: 200
+ height: 200
+ "shape": string
+ "icon": string
+ "width": int
+ "height": int
+}
+
+x -> my_table."shape"