Skip to content

Commit

Permalink
d2ir: Ensure filters pass before setting primary
Browse files Browse the repository at this point in the history
  • Loading branch information
nhooyr committed Aug 30, 2023
1 parent 2f1732e commit d9cea18
Show file tree
Hide file tree
Showing 8 changed files with 2,774 additions and 26 deletions.
9 changes: 9 additions & 0 deletions d2ast/d2ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,15 @@ func (m *Map) IsFileMap() bool {
return m.Range.Start.Line == 0 && m.Range.Start.Column == 0
}

func (m *Map) HasFilter() bool {
for _, n := range m.Nodes {
if n.MapKey != nil && (n.MapKey.Ampersand || n.MapKey.NotAmpersand) {
return true
}
}
return false
}

// TODO: require @ on import values for readability
type Key struct {
Range Range `json:"range"`
Expand Down
76 changes: 50 additions & 26 deletions d2ir/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,36 @@ func (g *globContext) prefixed(dst *Map) *globContext {
return &g2
}

func (c *compiler) ampersandFilterMap(dst *Map, ast, scopeAST *d2ast.Map) bool {
for _, n := range ast.Nodes {
switch {
case n.MapKey != nil:
ok := c.ampersandFilter(&RefContext{
Key: n.MapKey,
Scope: ast,
ScopeMap: dst,
ScopeAST: scopeAST,
})
if !ok {
// Unapply glob if appropriate.
gctx := c.getGlobContext(c.mapRefContextStack[len(c.mapRefContextStack)-1])
if gctx == nil {
return false
}
var ks string
if gctx.refctx.Key.HasTripleGlob() {
ks = d2format.Format(d2ast.MakeKeyPath(IDA(dst)))
} else {
ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(dst)))
}
delete(gctx.appliedFields, ks)
return false
}
}
}
return true
}

func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
var globs []*globContext
if len(c.globContextStack) > 0 {
Expand All @@ -400,31 +430,9 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
c.globContextStack = c.globContextStack[:len(c.globContextStack)-1]
}()

for _, n := range ast.Nodes {
switch {
case n.MapKey != nil:
ok := c.ampersandFilter(&RefContext{
Key: n.MapKey,
Scope: ast,
ScopeMap: dst,
ScopeAST: scopeAST,
})
if !ok {
// Unapply glob if appropriate.
gctx := c.getGlobContext(c.mapRefContextStack[len(c.mapRefContextStack)-1])
if gctx == nil {
return
}
var ks string
if gctx.refctx.Key.HasTripleGlob() {
ks = d2format.Format(d2ast.MakeKeyPath(IDA(dst)))
} else {
ks = d2format.Format(d2ast.MakeKeyPath(BoardIDA(dst)))
}
delete(gctx.appliedFields, ks)
return
}
}
ok := c.ampersandFilterMap(dst, ast, scopeAST)
if !ok {
return
}

for _, n := range ast.Nodes {
Expand Down Expand Up @@ -644,7 +652,22 @@ func (c *compiler) _ampersandFilter(f *Field, refctx *RefContext) bool {
}

func (c *compiler) _compileField(f *Field, refctx *RefContext) {
if len(refctx.Key.Edges) == 0 && refctx.Key.Value.Null != nil {
// In case of filters, we need to pass filters before continuing
if refctx.Key.Value.Map != nil && refctx.Key.Value.Map.HasFilter() {
if f.Map() == nil {
f.Composite = &Map{
parent: f,
}
}
c.mapRefContextStack = append(c.mapRefContextStack, refctx)
ok := c.ampersandFilterMap(f.Map(), refctx.Key.Value.Map, refctx.ScopeAST)
c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
if !ok {
return
}
}

if len(refctx.Key.Edges) == 0 && (refctx.Key.Primary.Null != nil || refctx.Key.Value.Null != nil) {
// 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
if !IsVar(ParentMap(f)) {
Expand All @@ -662,6 +685,7 @@ func (c *compiler) _compileField(f *Field, refctx *RefContext) {
Value: refctx.Key.Primary.Unbox(),
}
}

if refctx.Key.Value.Array != nil {
a := &Array{
parent: f,
Expand Down
40 changes: 40 additions & 0 deletions d2ir/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,46 @@ b.label: a
assertQuery(t, m, 0, 0, "yellow", "b.style.fill")
},
},
{
name: "primary-filter",
run: func(t testing.TB) {
m, err := compile(t, `
parent: {
a -> b1
a -> b2
a -> b3
b1 -> c1
b1 -> c2
c1: {
c1-child.class: hidden
}
c2: {
C2-child.class: hidden
}
c2.class: hidden
b2.class: hidden
}
classes: {
hidden: {
style: {
fill: red
}
}
}
# Error
**: null {
&class: hidden
}
`)
assert.Success(t, err)
assertQuery(t, m, 9, 3, nil, "")
},
},
}

runa(t, tca)
Expand Down
128 changes: 128 additions & 0 deletions testdata/d2ir/TestCompile/filters/array.exp.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions testdata/d2ir/TestCompile/filters/base.exp.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d9cea18

Please sign in to comment.