Skip to content

Commit 49bb43f

Browse files
committedFeb 27, 2025
basic !null working
1 parent 341c042 commit 49bb43f

File tree

13 files changed

+529
-843
lines changed

13 files changed

+529
-843
lines changed
 

‎d2compiler/compile_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,20 @@ ok: {
23392339
tassert.Equal(t, "null", g.Objects[0].IDVal)
23402340
},
23412341
},
2342+
{
2343+
name: "no-lazy-null",
2344+
2345+
text: `a
2346+
a -> b
2347+
c.d
2348+
**: null
2349+
2350+
g
2351+
`,
2352+
assertions: func(t *testing.T, g *d2graph.Graph) {
2353+
tassert.Equal(t, 1, len(g.Objects))
2354+
},
2355+
},
23422356
{
23432357
name: "sql-regression",
23442358

‎d2ir/compile.go

+69-6
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ type compiler struct {
4343
// Used to prevent field globs causing infinite loops.
4444
globRefContextStack []*RefContext
4545
// Used to check whether ampersands are allowed in the current map.
46-
mapRefContextStack []*RefContext
47-
lazyGlobBeingApplied bool
46+
mapRefContextStack []*RefContext
47+
lazyGlobBeingApplied bool
48+
markedFieldsForDeletion map[*Map]map[string]struct{}
49+
markedEdgesForDeletion map[*Map][]*EdgeID
4850
}
4951

5052
type CompileOptions struct {
@@ -65,8 +67,10 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, []string, error) {
6567
err: &d2parser.ParseError{},
6668
fs: opts.FS,
6769

68-
seenImports: make(map[string]struct{}),
69-
utf16Pos: opts.UTF16Pos,
70+
seenImports: make(map[string]struct{}),
71+
utf16Pos: opts.UTF16Pos,
72+
markedFieldsForDeletion: make(map[*Map]map[string]struct{}),
73+
markedEdgesForDeletion: make(map[*Map][]*EdgeID),
7074
}
7175
m := &Map{}
7276
m.initRoot()
@@ -81,6 +85,8 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, []string, error) {
8185
c.compileMap(m, ast, ast)
8286
c.compileSubstitutions(m, nil)
8387
c.overlayClasses(m)
88+
c.processMarkedDeletions(m)
89+
8490
if !c.err.Empty() {
8591
return nil, nil, c.err
8692
}
@@ -862,12 +868,17 @@ func (c *compiler) _compileField(f *Field, refctx *RefContext) {
862868
// For vars, if we delete the field, it may just resolve to an outer scope var of the same name
863869
// Instead we keep it around, so that resolveSubstitutions can find it
864870
if !IsVar(ParentMap(f)) {
865-
ParentMap(f).DeleteField(f.Name.ScalarString())
871+
c.markFieldForDeletion(ParentMap(f), f.Name.ScalarString())
866872
}
867873
}
868874
return
869875
}
870876

877+
if len(refctx.Key.Edges) == 0 && (refctx.Key.Primary.NotNull != nil || refctx.Key.Value.NotNull != nil) {
878+
c.unmarkFieldForDeletion(ParentMap(f), f.Name.ScalarString())
879+
return
880+
}
881+
871882
if refctx.Key.Primary.Unbox() != nil {
872883
if c.ignoreLazyGlob(f) {
873884
return
@@ -1146,10 +1157,14 @@ func (c *compiler) _compileEdges(refctx *RefContext) {
11461157
for i, eid := range eida {
11471158
if !eid.Glob && (refctx.Key.Primary.Null != nil || refctx.Key.Value.Null != nil) {
11481159
if !c.lazyGlobBeingApplied {
1149-
refctx.ScopeMap.DeleteEdge(eid)
1160+
c.markEdgeForDeletion(refctx.ScopeMap, eid)
11501161
}
11511162
continue
11521163
}
1164+
if !eid.Glob && (refctx.Key.Primary.NotNull != nil || refctx.Key.Value.NotNull != nil) {
1165+
c.unmarkEdgeForDeletion(refctx.ScopeMap, eid)
1166+
continue
1167+
}
11531168

11541169
refctx = refctx.Copy()
11551170
refctx.Edge = refctx.Key.Edges[i]
@@ -1295,3 +1310,51 @@ func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map)
12951310
dst.Values = append(dst.Values, irv)
12961311
}
12971312
}
1313+
1314+
func (c *compiler) markFieldForDeletion(parent *Map, key string) {
1315+
if c.markedFieldsForDeletion[parent] == nil {
1316+
c.markedFieldsForDeletion[parent] = make(map[string]struct{})
1317+
}
1318+
c.markedFieldsForDeletion[parent][key] = struct{}{}
1319+
}
1320+
1321+
func (c *compiler) unmarkFieldForDeletion(parent *Map, key string) {
1322+
if c.markedFieldsForDeletion[parent] != nil {
1323+
delete(c.markedFieldsForDeletion[parent], key)
1324+
}
1325+
}
1326+
1327+
func (c *compiler) markEdgeForDeletion(parent *Map, eid *EdgeID) {
1328+
c.markedEdgesForDeletion[parent] = append(c.markedEdgesForDeletion[parent], eid.Copy())
1329+
}
1330+
1331+
func (c *compiler) unmarkEdgeForDeletion(parent *Map, eid *EdgeID) {
1332+
edges := c.markedEdgesForDeletion[parent]
1333+
for i, e := range edges {
1334+
if e.Match(eid) {
1335+
// Remove this edge from the slice
1336+
c.markedEdgesForDeletion[parent] = append(edges[:i], edges[i+1:]...)
1337+
break
1338+
}
1339+
}
1340+
}
1341+
1342+
func (c *compiler) processMarkedDeletions(m *Map) {
1343+
// Process field deletions
1344+
for parent, keys := range c.markedFieldsForDeletion {
1345+
for key := range keys {
1346+
parent.DeleteField(key)
1347+
}
1348+
}
1349+
1350+
// Process edge deletions
1351+
for parent, edges := range c.markedEdgesForDeletion {
1352+
for _, eid := range edges {
1353+
parent.DeleteEdge(eid)
1354+
}
1355+
}
1356+
1357+
// Clear the tracking maps
1358+
c.markedFieldsForDeletion = make(map[*Map]map[string]struct{})
1359+
c.markedEdgesForDeletion = make(map[*Map][]*EdgeID)
1360+
}

0 commit comments

Comments
 (0)
Failed to load comments.