d2ir: Implement lazy globs and triple glob

This finishes up the globs implementation!

See tests for what I mean by lazy globs and what the triple glob does.
This commit is contained in:
Anmol Sethi 2023-07-30 13:52:42 -07:00
parent 24a1e006d0
commit bb6b176dee
No known key found for this signature in database
GPG key ID: 8CEF1878FF10ADEB
10 changed files with 25522 additions and 741 deletions

View file

@ -723,7 +723,43 @@ func (mk *Key) SetScalar(scalar ScalarBox) {
}
}
func (mk *Key) HasQueryGlob() bool {
func (mk *Key) HasGlob() bool {
if mk.Key.HasGlob() {
return true
}
for _, e := range mk.Edges {
if e.Src.HasGlob() || e.Dst.HasGlob() {
return true
}
}
if mk.EdgeIndex != nil && mk.EdgeIndex.Glob {
return true
}
if mk.EdgeKey.HasGlob() {
return true
}
return false
}
func (mk *Key) HasTripleGlob() bool {
if mk.Key.HasTripleGlob() {
return true
}
for _, e := range mk.Edges {
if e.Src.HasTripleGlob() || e.Dst.HasTripleGlob() {
return true
}
}
if mk.EdgeIndex != nil && mk.EdgeIndex.Glob {
return true
}
if mk.EdgeKey.HasTripleGlob() {
return true
}
return false
}
func (mk *Key) SupportsGlobFilters() bool {
if mk.Key.HasGlob() && len(mk.Edges) == 0 {
return true
}
@ -736,6 +772,11 @@ func (mk *Key) HasQueryGlob() bool {
return false
}
func (mk *Key) Copy() *Key {
mk2 := *mk
return &mk2
}
type KeyPath struct {
Range Range `json:"range"`
Path []*StringBox `json:"path"`
@ -763,16 +804,12 @@ func (kp *KeyPath) Copy() *KeyPath {
return &kp2
}
func (kp *KeyPath) HasDoubleGlob() bool {
if kp == nil {
return false
}
for _, el := range kp.Path {
if el.UnquotedString != nil && el.ScalarString() == "**" {
return true
}
}
return false
func IsDoubleGlob(pattern []string) bool {
return len(pattern) == 3 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*"
}
func IsTripleGlob(pattern []string) bool {
return len(pattern) == 5 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*" && pattern[3] == "" && pattern[4] == "*"
}
func (kp *KeyPath) HasGlob() bool {
@ -787,6 +824,30 @@ func (kp *KeyPath) HasGlob() bool {
return false
}
func (kp *KeyPath) HasTripleGlob() bool {
if kp == nil {
return false
}
for _, el := range kp.Path {
if el.UnquotedString != nil && IsTripleGlob(el.UnquotedString.Pattern) {
return true
}
}
return false
}
func (kp *KeyPath) HasMultiGlob() bool {
if kp == nil {
return false
}
for _, el := range kp.Path {
if el.UnquotedString != nil && (IsDoubleGlob(el.UnquotedString.Pattern) || IsTripleGlob(el.UnquotedString.Pattern)) {
return true
}
}
return false
}
type Edge struct {
Range Range `json:"range"`

View file

@ -5,14 +5,23 @@ import (
"strconv"
"strings"
"oss.terrastruct.com/util-go/go2"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2themes"
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
"oss.terrastruct.com/util-go/go2"
)
type globContext struct {
refctx *RefContext
// Set of BoardIDA that this glob has already applied to.
appliedFields map[string]struct{}
// Set of Edge IDs that this glob has already applied to.
appliedEdges map[string]struct{}
}
type compiler struct {
err *d2parser.ParseError
@ -23,7 +32,12 @@ type compiler struct {
importCache map[string]*Map
utf16Pos bool
globStack []bool
// Stack of globs that must be recomputed at each new object in and below the current scope.
globContextStack [][]*globContext
// Used to prevent field globs causing infinite loops.
globRefContextStack []*RefContext
// Used to check whether ampersands are allowed in the current map.
mapRefContextStack []*RefContext
}
type CompileOptions struct {
@ -346,6 +360,52 @@ func (c *compiler) overlay(base *Map, f *Field) {
}
func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
var globs []*globContext
if len(c.globContextStack) > 0 {
previousGlobs := c.globContextStack[len(c.globContextStack)-1]
if NodeBoardKind(dst) == BoardLayer {
for _, g := range previousGlobs {
if g.refctx.Key.HasTripleGlob() {
// Same as below but reset applied too.
g2 := *g
g2.refctx = g.refctx.Copy()
g2.appliedFields = make(map[string]struct{})
g2.appliedEdges = make(map[string]struct{})
prefix := d2ast.MakeKeyPath(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...)
}
if len(prefix.Path) > 0 {
g2.refctx.Key.Key = prefix
}
globs = append(globs, &g2)
}
}
} else if NodeBoardKind(dst) != "" {
// Make all globs relative to the scenario or step.
for _, g := range previousGlobs {
g2 := *g
g2.refctx = g.refctx.Copy()
prefix := d2ast.MakeKeyPath(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...)
}
if len(prefix.Path) > 0 {
g2.refctx.Key.Key = prefix
}
globs = append(globs, &g2)
}
} else {
globs = append(globs, previousGlobs...)
}
}
c.globContextStack = append(c.globContextStack, globs)
defer func() {
c.globContextStack = c.globContextStack[:len(c.globContextStack)-1]
}()
for _, n := range ast.Nodes {
switch {
case n.MapKey != nil:
@ -403,7 +463,47 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
}
}
func (c *compiler) globContexts() []*globContext {
return c.globContextStack[len(c.globContextStack)-1]
}
func (c *compiler) getGlobContext(refctx *RefContext) *globContext {
for _, gctx := range c.globContexts() {
if gctx.refctx.Equal(refctx) {
return gctx
}
}
return nil
}
func (c *compiler) ensureGlobContext(refctx *RefContext) *globContext {
gctx := c.getGlobContext(refctx)
if gctx != nil {
return gctx
}
gctx = &globContext{
refctx: refctx,
appliedFields: make(map[string]struct{}),
appliedEdges: make(map[string]struct{}),
}
c.globContextStack[len(c.globContextStack)-1] = append(c.globContexts(), gctx)
return gctx
}
func (c *compiler) compileKey(refctx *RefContext) {
if refctx.Key.HasGlob() {
for _, refctx2 := range c.globRefContextStack {
if refctx.Equal(refctx2) {
// Break the infinite loop.
return
}
}
c.globRefContextStack = append(c.globRefContextStack, refctx)
defer func() {
c.globRefContextStack = c.globRefContextStack[:len(c.globRefContextStack)-1]
}()
c.ensureGlobContext(refctx)
}
if len(refctx.Key.Edges) == 0 {
c.compileField(refctx.ScopeMap, refctx.Key.Key, refctx)
} else {
@ -416,7 +516,7 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext)
return
}
fa, err := dst.EnsureField(kp, refctx, true)
fa, err := dst.EnsureField(kp, refctx, true, c)
if err != nil {
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
return
@ -431,7 +531,7 @@ func (c *compiler) ampersandFilter(refctx *RefContext) bool {
if !refctx.Key.Ampersand {
return true
}
if len(c.globStack) == 0 || !c.globStack[len(c.globStack)-1] {
if len(c.mapRefContextStack) == 0 || !c.mapRefContextStack[len(c.mapRefContextStack)-1].Key.SupportsGlobFilters() {
c.errorf(refctx.Key, "glob filters cannot be used outside globs")
return false
}
@ -439,7 +539,7 @@ func (c *compiler) ampersandFilter(refctx *RefContext) bool {
return true
}
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, false)
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, false, c)
if err != nil {
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
return false
@ -532,9 +632,9 @@ func (c *compiler) _compileField(f *Field, refctx *RefContext) {
// If new board type, use that as the new scope AST, otherwise, carry on
scopeAST = refctx.ScopeAST
}
c.globStack = append(c.globStack, refctx.Key.HasQueryGlob())
c.mapRefContextStack = append(c.mapRefContextStack, refctx)
c.compileMap(f.Map(), refctx.Key.Value.Map, scopeAST)
c.globStack = c.globStack[:len(c.globStack)-1]
c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
switch NodeBoardKind(f) {
case BoardScenario, BoardStep:
c.overlayClasses(f.Map())
@ -692,7 +792,7 @@ func (c *compiler) compileEdges(refctx *RefContext) {
return
}
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, true)
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, true, c)
if err != nil {
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
return
@ -728,7 +828,9 @@ func (c *compiler) _compileEdges(refctx *RefContext) {
if eid.Index != nil || eid.Glob {
ea = refctx.ScopeMap.GetEdges(eid, refctx)
if len(ea) == 0 {
c.errorf(refctx.Edge, "indexed edge does not exist")
if !eid.Glob {
c.errorf(refctx.Edge, "indexed edge does not exist")
}
continue
}
for _, e := range ea {
@ -740,7 +842,7 @@ func (c *compiler) _compileEdges(refctx *RefContext) {
}
} else {
var err error
ea, err = refctx.ScopeMap.CreateEdge(eid, refctx)
ea, err = refctx.ScopeMap.CreateEdge(eid, refctx, c)
if err != nil {
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
continue
@ -771,9 +873,9 @@ func (c *compiler) _compileEdges(refctx *RefContext) {
parent: e,
}
}
c.globStack = append(c.globStack, refctx.Key.HasQueryGlob())
c.mapRefContextStack = append(c.mapRefContextStack, refctx)
c.compileMap(e.Map_, refctx.Key.Value.Map, refctx.ScopeAST)
c.globStack = c.globStack[:len(c.globStack)-1]
c.mapRefContextStack = c.mapRefContextStack[:len(c.mapRefContextStack)-1]
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
e.Primary_ = &Scalar{
parent: e,

View file

@ -245,7 +245,11 @@ func NodeBoardKind(n Node) BoardKind {
}
f = ParentField(n)
case *Map:
f = ParentField(n)
var ok bool
f, ok = n.parent.(*Field)
if !ok {
return ""
}
if f.Root() {
return BoardLayer
}
@ -569,6 +573,10 @@ func (rc *RefContext) EdgeIndex() int {
return -1
}
func (rc *RefContext) Equal(rc2 *RefContext) bool {
return rc.Edge == rc2.Edge && rc.Key.Equals(rc2.Key) && rc.Scope == rc2.Scope && rc.ScopeMap == rc2.ScopeMap && rc.ScopeAST == rc2.ScopeAST
}
func (m *Map) FieldCountRecursive() int {
if m == nil {
return 0
@ -667,7 +675,7 @@ func (m *Map) getField(ida []string) *Field {
}
// EnsureField is a bit of a misnomer. It's more of a Query/Ensure combination function at this point.
func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool) ([]*Field, error) {
func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool, c *compiler) ([]*Field, error) {
i := 0
for kp.Path[i].Unbox().ScalarString() == "_" {
m = ParentMap(m)
@ -680,18 +688,47 @@ func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool) ([
i++
}
var gctx *globContext
if refctx != nil && refctx.Key.HasGlob() && c != nil {
gctx = c.getGlobContext(refctx)
}
var fa []*Field
err := m.ensureField(i, kp, refctx, create, &fa)
return fa, err
err := m.ensureField(i, kp, refctx, create, gctx, c, &fa)
if err != nil {
return fa, err
}
if len(fa) > 0 && create && c != nil {
for _, gctx2 := range c.globContexts() {
c.compileKey(gctx2.refctx)
}
}
return fa, nil
}
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create bool, fa *[]*Field) error {
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
}
gctx.appliedFields[ks] = struct{}{}
}
}
*fa = append(*fa, f)
}
}
us, ok := kp.Path[i].Unbox().(*d2ast.UnquotedString)
if ok && us.Pattern != nil {
fa2, ok := m.doubleGlob(us.Pattern)
fa2, ok := m.multiGlob(us.Pattern)
if ok {
if i == len(kp.Path)-1 {
*fa = append(*fa, fa2...)
faAppend(fa2...)
} else {
for _, f := range fa2 {
if f.Map() == nil {
@ -699,7 +736,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
parent: f,
}
}
err := f.Map().ensureField(i+1, kp, refctx, create, fa)
err := f.Map().ensureField(i+1, kp, refctx, create, gctx, c, fa)
if err != nil {
return err
}
@ -710,14 +747,14 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
for _, f := range m.Fields {
if matchPattern(f.Name, us.Pattern) {
if i == len(kp.Path)-1 {
*fa = append(*fa, f)
faAppend(f)
} else {
if f.Map() == nil {
f.Composite = &Map{
parent: f,
}
}
err := f.Map().ensureField(i+1, kp, refctx, create, fa)
err := f.Map().ensureField(i+1, kp, refctx, create, gctx, c, fa)
if err != nil {
return err
}
@ -763,7 +800,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
}
if i+1 == len(kp.Path) {
*fa = append(*fa, f)
faAppend(f)
return nil
}
if _, ok := f.Composite.(*Array); ok {
@ -774,7 +811,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
parent: f,
}
}
return f.Map().ensureField(i+1, kp, refctx, create, fa)
return f.Map().ensureField(i+1, kp, refctx, create, gctx, c, fa)
}
if !create {
@ -797,10 +834,12 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
*fa = append(*fa, f)
return nil
}
f.Composite = &Map{
parent: f,
if f.Composite == nil {
f.Composite = &Map{
parent: f,
}
}
return f.Map().ensureField(i+1, kp, refctx, create, fa)
return f.Map().ensureField(i+1, kp, refctx, create, gctx, c, fa)
}
func (m *Map) DeleteEdge(eid *EdgeID) *Edge {
@ -914,7 +953,7 @@ func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
}
}
}
fa, err := m.EnsureField(commonKP, nil, false)
fa, err := m.EnsureField(commonKP, nil, false, nil)
if err != nil {
return nil
}
@ -935,11 +974,11 @@ func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
return nil
}
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, nil, false)
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, nil, false, nil)
if err != nil {
return err
}
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, nil, false)
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, nil, false, nil)
if err != nil {
return err
}
@ -957,12 +996,25 @@ func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
return nil
}
func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) ([]*Edge, error) {
func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext, c *compiler) ([]*Edge, error) {
var ea []*Edge
return ea, m.createEdge(eid, refctx, &ea)
var gctx *globContext
if refctx != nil && refctx.Key.HasGlob() && c != nil {
gctx = c.getGlobContext(refctx)
}
err := m.createEdge(eid, refctx, gctx, c, &ea)
if err != nil {
return ea, err
}
if len(ea) > 0 && c != nil {
for _, gctx2 := range c.globContexts() {
c.compileKey(gctx2.refctx)
}
}
return ea, nil
}
func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, gctx *globContext, c *compiler, ea *[]*Edge) error {
if ParentEdge(m) != nil {
return d2parser.Errorf(refctx.Edge, "cannot create edge inside edge")
}
@ -983,7 +1035,7 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
}
}
}
fa, err := m.EnsureField(commonKP, nil, true)
fa, err := m.EnsureField(commonKP, nil, true, c)
if err != nil {
return err
}
@ -996,7 +1048,7 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
parent: f,
}
}
err = f.Map().createEdge(eid, refctx, ea)
err = f.Map().createEdge(eid, refctx, gctx, c, ea)
if err != nil {
return err
}
@ -1022,11 +1074,11 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
return d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
}
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, refctx, true)
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, refctx, true, c)
if err != nil {
return err
}
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, refctx, true)
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, refctx, true, c)
if err != nil {
return err
}
@ -1038,21 +1090,21 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
continue
}
if refctx.Edge.Src.HasDoubleGlob() {
if refctx.Edge.Src.HasMultiGlob() {
// If src has a double glob we only select leafs, those without children.
if src.Map().IsContainer() {
continue
}
if ParentBoard(src) != ParentBoard(dst) {
if NodeBoardKind(src) != "" || ParentBoard(src) != ParentBoard(dst) {
continue
}
}
if refctx.Edge.Dst.HasDoubleGlob() {
if refctx.Edge.Dst.HasMultiGlob() {
// If dst has a double glob we only select leafs, those without children.
if dst.Map().IsContainer() {
continue
}
if ParentBoard(src) != ParentBoard(dst) {
if NodeBoardKind(dst) != "" || ParentBoard(src) != ParentBoard(dst) {
continue
}
}
@ -1060,17 +1112,20 @@ func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
eid2 := eid.Copy()
eid2.SrcPath = RelIDA(m, src)
eid2.DstPath = RelIDA(m, dst)
e, err := m.createEdge2(eid2, refctx, src, dst)
e, err := m.createEdge2(eid2, refctx, gctx, src, dst)
if err != nil {
return err
}
*ea = append(*ea, e)
if e != nil {
*ea = append(*ea, e)
}
}
}
return nil
}
func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, src, dst *Field) (*Edge, error) {
func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, gctx *globContext, src, dst *Field) (*Edge, error) {
if NodeBoardKind(src) != "" {
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot create edges between boards")
}
@ -1094,6 +1149,15 @@ func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, src, dst *Field) (*Ed
Context: refctx,
}},
}
if gctx != nil {
ks := e.String()
if _, ok := gctx.appliedEdges[ks]; ok {
return nil, nil
}
gctx.appliedEdges[ks] = struct{}{}
}
m.Edges = append(m.Edges, e)
return e, nil

View file

@ -3,19 +3,41 @@ package d2ir
import (
"strings"
"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2graph"
)
func (m *Map) doubleGlob(pattern []string) ([]*Field, bool) {
if !(len(pattern) == 3 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*") {
return nil, false
}
func (m *Map) multiGlob(pattern []string) ([]*Field, bool) {
var fa []*Field
m._doubleGlob(&fa)
return fa, true
if d2ast.IsDoubleGlob(pattern) {
m._doubleGlob(&fa)
return fa, true
}
if d2ast.IsTripleGlob(pattern) {
m._tripleGlob(&fa)
return fa, true
}
return nil, false
}
func (m *Map) _doubleGlob(fa *[]*Field) {
for _, f := range m.Fields {
if _, ok := d2graph.ReservedKeywords[f.Name]; ok {
if f.Name == "layers" {
continue
}
if _, ok := d2graph.BoardKeywords[f.Name]; !ok {
continue
}
}
*fa = append(*fa, f)
if f.Map() != nil {
f.Map()._doubleGlob(fa)
}
}
}
func (m *Map) _tripleGlob(fa *[]*Field) {
for _, f := range m.Fields {
if _, ok := d2graph.ReservedKeywords[f.Name]; ok {
if _, ok := d2graph.BoardKeywords[f.Name]; !ok {
@ -24,7 +46,7 @@ func (m *Map) _doubleGlob(fa *[]*Field) {
}
*fa = append(*fa, f)
if f.Map() != nil {
f.Map()._doubleGlob(fa)
f.Map()._tripleGlob(fa)
}
}
}

View file

@ -285,6 +285,31 @@ d
assertQuery(t, m, 0, 0, nil, "(* -> *)[*]")
},
},
{
name: "single-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `wrapper.*: {
shape: page
}
wrapper.a
wrapper.b
wrapper.c
wrapper.d
scenarios.x: { wrapper.p }
layers.x: { wrapper.p }
`)
assert.Success(t, err)
assertQuery(t, m, 26, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "wrapper.a.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.b.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.c.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.wrapper.p.shape")
assertQuery(t, m, 0, 0, nil, "layers.x.wrapper.p")
},
},
{
name: "double-glob/edge/1",
run: func(t testing.TB) {
@ -314,21 +339,81 @@ task.** -> fast
assertQuery(t, m, 3, 1, nil, "")
},
},
{
name: "double-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `**: {
shape: page
}
a
b
c
d
scenarios.x: { p }
layers.x: { p }
`)
assert.Success(t, err)
assertQuery(t, m, 25, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "a.shape")
assertQuery(t, m, 0, 0, "page", "b.shape")
assertQuery(t, m, 0, 0, "page", "c.shape")
assertQuery(t, m, 0, 0, "page", "d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
assertQuery(t, m, 0, 0, nil, "layers.x.p")
},
},
{
name: "triple-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `***: {
shape: page
}
a
b
c
d
scenarios.x: { p }
layers.x: { p }
`)
assert.Success(t, err)
assertQuery(t, m, 27, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "a.shape")
assertQuery(t, m, 0, 0, "page", "b.shape")
assertQuery(t, m, 0, 0, "page", "c.shape")
assertQuery(t, m, 0, 0, "page", "d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
assertQuery(t, m, 0, 0, "page", "layers.x.p.shape")
},
},
{
name: "triple-glob/edge-defaults",
run: func(t testing.TB) {
m, err := compile(t, `(*** -> ***)[*]: {
target-arrowhead.shape: diamond
}
a -> b
c -> d
scenarios.x: { p -> q }
layers.x: { j -> f }
`)
assert.Success(t, err)
assertQuery(t, m, 28, 6, nil, "")
assertQuery(t, m, 0, 0, "diamond", "(a -> b)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "(c -> d)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(a -> b)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(c -> d)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(p -> q)[0].target-arrowhead.shape")
assertQuery(t, m, 4, 1, nil, "layers.x")
assertQuery(t, m, 0, 0, "diamond", "layers.x.(j -> f)[0].target-arrowhead.shape")
},
},
}
runa(t, tca)
t.Run("errors", func(t *testing.T) {
tca := []testCase{
{
name: "glob-edge-glob-index",
run: func(t testing.TB) {
_, err := compile(t, `(* -> b)[*].style.fill: red
`)
assert.ErrorString(t, err, `TestCompile/patterns/errors/glob-edge-glob-index.d2:1:2: indexed edge does not exist`)
},
},
}
runa(t, tca)
})
}

File diff suppressed because it is too large Load diff

View file

@ -1,667 +1,4 @@
{
"fields": [
{
"name": "b",
"references": [
{
"string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
},
"key_path": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"context": {
"edge": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
},
"key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:0:0-0:24:24",
"edges": [
{
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
}
],
"edge_key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:21:21-0:24:24",
"value": [
{
"string": "red",
"raw_string": "red"
}
]
}
}
}
}
}
]
}
],
"edges": [
{
"edge_id": {
"src_path": [
"b"
],
"src_arrow": false,
"dst_path": [
"b"
],
"dst_arrow": true,
"index": 0,
"glob": false
},
"map": {
"fields": [
{
"name": "style",
"composite": {
"fields": [
{
"name": "fill",
"primary": {
"value": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:21:21-0:24:24",
"value": [
{
"string": "red",
"raw_string": "red"
}
]
}
},
"references": [
{
"string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
},
"key_path": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"context": {
"edge": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
},
"key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:0:0-0:24:24",
"edges": [
{
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
}
],
"edge_key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:21:21-0:24:24",
"value": [
{
"string": "red",
"raw_string": "red"
}
]
}
}
}
}
}
]
}
],
"edges": null
},
"references": [
{
"string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
},
"key_path": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"context": {
"edge": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
},
"key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:0:0-0:24:24",
"edges": [
{
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
}
],
"edge_key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:21:21-0:24:24",
"value": [
{
"string": "red",
"raw_string": "red"
}
]
}
}
}
}
}
]
}
],
"edges": null
},
"references": [
{
"context": {
"edge": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
},
"key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:0:0-0:24:24",
"edges": [
{
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:7:7",
"src": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:1:1-0:2:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:6:6-0:7:7",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"dst_arrow": ">"
}
],
"edge_key": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:19:19",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:9:9-0:14:14",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:15:15-0:19:19",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/errors/glob-edge-glob-index.d2,0:21:21-0:24:24",
"value": [
{
"string": "red",
"raw_string": "red"
}
]
}
}
}
}
}
]
}
]
"fields": null,
"edges": null
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff