d2ir: Fix infinite loop in triple globs
This commit is contained in:
parent
4090e780f8
commit
a24716d1f1
6 changed files with 2258 additions and 1283 deletions
|
|
@ -492,11 +492,15 @@ func (c *compiler) ensureGlobContext(refctx *RefContext) *globContext {
|
|||
|
||||
func (c *compiler) compileKey(refctx *RefContext) {
|
||||
if refctx.Key.HasGlob() {
|
||||
// These three printlns are for debugging infinite loops.
|
||||
// println("og", refctx.Edge, refctx.Key, refctx.Scope, refctx.ScopeMap, refctx.ScopeAST)
|
||||
for _, refctx2 := range c.globRefContextStack {
|
||||
// println("st", refctx2.Edge, refctx2.Key, refctx2.Scope, refctx2.ScopeMap, refctx2.ScopeAST)
|
||||
if refctx.Equal(refctx2) {
|
||||
// Break the infinite loop.
|
||||
return
|
||||
}
|
||||
// println("keys", d2format.Format(refctx2.Key), d2format.Format(refctx.Key))
|
||||
}
|
||||
c.globRefContextStack = append(c.globRefContextStack, refctx)
|
||||
defer func() {
|
||||
|
|
|
|||
48
d2ir/d2ir.go
48
d2ir/d2ir.go
|
|
@ -575,7 +575,8 @@ func (rc *RefContext) EdgeIndex() int {
|
|||
|
||||
func (rc *RefContext) Equal(rc2 *RefContext) bool {
|
||||
// We intentionally ignore edges here because the same glob can produce multiple RefContexts that should be treated the same with only the edge as the difference.
|
||||
return rc.Key.Equals(rc2.Key) && rc.Scope == rc2.Scope && rc.ScopeMap == rc2.ScopeMap && rc.ScopeAST == rc2.ScopeAST
|
||||
// Same with ScopeMap.
|
||||
return rc.Key.Equals(rc2.Key) && rc.Scope == rc2.Scope && rc.ScopeAST == rc2.ScopeAST
|
||||
}
|
||||
|
||||
func (m *Map) FieldCountRecursive() int {
|
||||
|
|
@ -708,19 +709,30 @@ func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool, c
|
|||
}
|
||||
|
||||
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create bool, gctx *globContext, c *compiler, fa *[]*Field) error {
|
||||
faAppend := func(fa2 ...*Field) {
|
||||
for _, f := range fa2 {
|
||||
if gctx != nil {
|
||||
// Always match all commons, sources and destinations for edge globs.
|
||||
if len(refctx.Key.Edges) == 0 || kp == refctx.Key.EdgeKey {
|
||||
ks := d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
|
||||
if _, ok := gctx.appliedFields[ks]; ok {
|
||||
continue
|
||||
}
|
||||
filter := func(f *Field, passthrough bool) bool {
|
||||
if gctx != nil {
|
||||
// For globs with edges, we only ignore duplicate fields if the glob is not at the terminal of the keypath, the glob is on the common key or the glob is on the edge key.
|
||||
if !kp.HasGlob() {
|
||||
return true
|
||||
}
|
||||
lastEl := kp.Path[len(kp.Path)-1]
|
||||
if len(refctx.Key.Edges) == 0 || lastEl.UnquotedString == nil || len(lastEl.UnquotedString.Pattern) == 0 || kp == refctx.Key.Key || kp == refctx.Key.EdgeKey {
|
||||
ks := d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
|
||||
if _, ok := gctx.appliedFields[ks]; ok {
|
||||
return false
|
||||
}
|
||||
if !passthrough {
|
||||
gctx.appliedFields[ks] = struct{}{}
|
||||
}
|
||||
}
|
||||
*fa = append(*fa, f)
|
||||
}
|
||||
return true
|
||||
}
|
||||
faAppend := func(fa2 ...*Field) {
|
||||
for _, f := range fa2 {
|
||||
if filter(f, false) {
|
||||
*fa = append(*fa, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -732,6 +744,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
|
|||
faAppend(fa2...)
|
||||
} else {
|
||||
for _, f := range fa2 {
|
||||
if !filter(f, true) {
|
||||
return nil
|
||||
}
|
||||
if f.Map() == nil {
|
||||
f.Composite = &Map{
|
||||
parent: f,
|
||||
|
|
@ -750,6 +765,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
|
|||
if i == len(kp.Path)-1 {
|
||||
faAppend(f)
|
||||
} else {
|
||||
if !filter(f, true) {
|
||||
return nil
|
||||
}
|
||||
if f.Map() == nil {
|
||||
f.Composite = &Map{
|
||||
parent: f,
|
||||
|
|
@ -804,6 +822,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
|
|||
faAppend(f)
|
||||
return nil
|
||||
}
|
||||
if !filter(f, true) {
|
||||
return nil
|
||||
}
|
||||
if _, ok := f.Composite.(*Array); ok {
|
||||
return d2parser.Errorf(kp.Path[i].Unbox(), "cannot index into array")
|
||||
}
|
||||
|
|
@ -832,7 +853,10 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
|
|||
}
|
||||
m.Fields = append(m.Fields, f)
|
||||
if i+1 == len(kp.Path) {
|
||||
*fa = append(*fa, f)
|
||||
faAppend(f)
|
||||
return nil
|
||||
}
|
||||
if !filter(f, true) {
|
||||
return nil
|
||||
}
|
||||
if f.Composite == nil {
|
||||
|
|
|
|||
|
|
@ -432,6 +432,7 @@ layers: {
|
|||
}
|
||||
`)
|
||||
assert.Success(t, err)
|
||||
t.Log(m)
|
||||
assertQuery(t, m, 14, 0, nil, "")
|
||||
},
|
||||
},
|
||||
|
|
@ -450,6 +451,24 @@ c
|
|||
assertQuery(t, m, 4, 3, nil, "")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alixander-review/3",
|
||||
run: func(t testing.TB) {
|
||||
m, err := compile(t, `
|
||||
a
|
||||
|
||||
***.b -> c
|
||||
|
||||
layers: {
|
||||
z: {
|
||||
d
|
||||
}
|
||||
}
|
||||
`)
|
||||
assert.Success(t, err)
|
||||
assertQuery(t, m, 8, 2, nil, "")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runa(t, tca)
|
||||
|
|
|
|||
993
testdata/d2ir/TestCompile/patterns/alixander-review/1.exp.json
generated
vendored
993
testdata/d2ir/TestCompile/patterns/alixander-review/1.exp.json
generated
vendored
File diff suppressed because it is too large
Load diff
2199
testdata/d2ir/TestCompile/patterns/alixander-review/3.exp.json
generated
vendored
Normal file
2199
testdata/d2ir/TestCompile/patterns/alixander-review/3.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
278
testdata/d2ir/TestCompile/patterns/double-glob/defaults.exp.json
generated
vendored
278
testdata/d2ir/TestCompile/patterns/double-glob/defaults.exp.json
generated
vendored
|
|
@ -1880,284 +1880,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_path": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "scenarios"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
|
||||
"value": [
|
||||
{
|
||||
"string": "**",
|
||||
"raw_string": "**"
|
||||
}
|
||||
],
|
||||
"pattern": [
|
||||
"*",
|
||||
"",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"edge": null,
|
||||
"key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-2:1:20",
|
||||
"key": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "scenarios"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
|
||||
"value": [
|
||||
{
|
||||
"string": "**",
|
||||
"raw_string": "**"
|
||||
}
|
||||
],
|
||||
"pattern": [
|
||||
"*",
|
||||
"",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"map": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:4:4-2:1:20",
|
||||
"nodes": [
|
||||
{
|
||||
"map_key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:12:18",
|
||||
"key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
|
||||
"value": [
|
||||
{
|
||||
"string": "shape",
|
||||
"raw_string": "shape"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:8:14-1:12:18",
|
||||
"value": [
|
||||
{
|
||||
"string": "page",
|
||||
"raw_string": "page"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_path": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "scenarios"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
|
||||
"value": [
|
||||
{
|
||||
"string": "**",
|
||||
"raw_string": "**"
|
||||
}
|
||||
],
|
||||
"pattern": [
|
||||
"*",
|
||||
"",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"edge": null,
|
||||
"key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-2:1:20",
|
||||
"key": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "scenarios"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": ",0:0:0-0:0:0",
|
||||
"value": [
|
||||
{
|
||||
"string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
|
||||
"value": [
|
||||
{
|
||||
"string": "**",
|
||||
"raw_string": "**"
|
||||
}
|
||||
],
|
||||
"pattern": [
|
||||
"*",
|
||||
"",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"map": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,0:4:4-2:1:20",
|
||||
"nodes": [
|
||||
{
|
||||
"map_key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:12:18",
|
||||
"key": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
|
||||
"value": [
|
||||
{
|
||||
"string": "shape",
|
||||
"raw_string": "shape"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"unquoted_string": {
|
||||
"range": "TestCompile/patterns/double-glob/defaults.d2,1:8:14-1:12:18",
|
||||
"value": [
|
||||
{
|
||||
"string": "page",
|
||||
"raw_string": "page"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue