Skip to content

Commit 30f7d28

Browse files
committed
cue/ast: implement Visitor walking in terms of func walking
Roger correctly points out that a before func returning a new visitor can be implemented in terms of a before func returning a boolean, we just need to keep state in the receiver in the form of a stack. This is a step towards removing the very recently added WalkVisitor API, given the above, and how the tests still pass with this change. The following commits will incrementally remove the new API. While here, remove the godoc on walk, as it was a remnant from Go's walking API, and not correct given our implementation. We already have a godoc on the exported Walk API which is correct. Signed-off-by: Daniel Martí <[email protected]> Change-Id: If1b4fbaf974a1a23ebfb211676c5f6ac5832c2d5 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194082 Reviewed-by: Roger Peppe <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent caa1e98 commit 30f7d28

File tree

1 file changed

+72
-55
lines changed

1 file changed

+72
-55
lines changed

cue/ast/walk.go

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,34 @@ import (
2525
// each of the non-nil children of node, followed by a call of after. Both
2626
// functions may be nil. If before is nil, it is assumed to always return true.
2727
func Walk(node Node, before func(Node) bool, after func(Node)) {
28-
walk(&inspector{before: before, after: after}, node)
28+
v := &inspector{before: before, after: after}
29+
walk(node, v.Before, v.After)
2930
}
3031

3132
// WalkVisitor traverses an AST in depth-first order with a [Visitor].
3233
func WalkVisitor(node Node, visitor Visitor) {
33-
walk(visitor, node)
34+
v := &stackVisitor{stack: []Visitor{visitor}}
35+
walk(node, v.Before, v.After)
36+
}
37+
38+
// stackVisitor helps implement Visitor support on top of Walk.
39+
type stackVisitor struct {
40+
stack []Visitor
41+
}
42+
43+
func (v *stackVisitor) Before(node Node) bool {
44+
current := v.stack[len(v.stack)-1]
45+
next := current.Before(node)
46+
if next == nil {
47+
return false
48+
}
49+
v.stack = append(v.stack, next)
50+
return true
51+
}
52+
53+
func (v *stackVisitor) After(node Node) {
54+
v.stack[len(v.stack)-1] = nil // set visitor to nil so it can be garbage collected
55+
v.stack = v.stack[:len(v.stack)-1]
3456
}
3557

3658
// A Visitor's before method is invoked for each node encountered by Walk.
@@ -41,25 +63,20 @@ type Visitor interface {
4163
After(node Node)
4264
}
4365

44-
func walkList[N Node](v Visitor, list []N) {
66+
func walkList[N Node](list []N, before func(Node) bool, after func(Node)) {
4567
for _, node := range list {
46-
walk(v, node)
68+
walk(node, before, after)
4769
}
4870
}
4971

50-
// walk traverses an AST in depth-first order: It starts by calling
51-
// v.Visit(node); node must not be nil. If the visitor w returned by
52-
// v.Visit(node) is not nil, walk is invoked recursively with visitor
53-
// w for each of the non-nil children of node, followed by a call of
54-
// w.Visit(nil).
55-
func walk(v Visitor, node Node) {
56-
if v = v.Before(node); v == nil {
72+
func walk(node Node, before func(Node) bool, after func(Node)) {
73+
if !before(node) {
5774
return
5875
}
5976

6077
// TODO: record the comment groups and interleave with the values like for
6178
// parsing and printing?
62-
walkList(v, Comments(node))
79+
walkList(Comments(node), before, after)
6380

6481
// walk children
6582
// (the order of the cases matches the order
@@ -70,121 +87,121 @@ func walk(v Visitor, node Node) {
7087
// nothing to do
7188

7289
case *CommentGroup:
73-
walkList(v, n.List)
90+
walkList(n.List, before, after)
7491

7592
case *Attribute:
7693
// nothing to do
7794

7895
case *Field:
79-
walk(v, n.Label)
96+
walk(n.Label, before, after)
8097
if n.Value != nil {
81-
walk(v, n.Value)
98+
walk(n.Value, before, after)
8299
}
83-
walkList(v, n.Attrs)
100+
walkList(n.Attrs, before, after)
84101

85102
case *Func:
86-
walkList(v, n.Args)
87-
walk(v, n.Ret)
103+
walkList(n.Args, before, after)
104+
walk(n.Ret, before, after)
88105

89106
case *StructLit:
90-
walkList(v, n.Elts)
107+
walkList(n.Elts, before, after)
91108

92109
// Expressions
93110
case *BottomLit, *BadExpr, *Ident, *BasicLit:
94111
// nothing to do
95112

96113
case *Interpolation:
97-
walkList(v, n.Elts)
114+
walkList(n.Elts, before, after)
98115

99116
case *ListLit:
100-
walkList(v, n.Elts)
117+
walkList(n.Elts, before, after)
101118

102119
case *Ellipsis:
103120
if n.Type != nil {
104-
walk(v, n.Type)
121+
walk(n.Type, before, after)
105122
}
106123

107124
case *ParenExpr:
108-
walk(v, n.X)
125+
walk(n.X, before, after)
109126

110127
case *SelectorExpr:
111-
walk(v, n.X)
112-
walk(v, n.Sel)
128+
walk(n.X, before, after)
129+
walk(n.Sel, before, after)
113130

114131
case *IndexExpr:
115-
walk(v, n.X)
116-
walk(v, n.Index)
132+
walk(n.X, before, after)
133+
walk(n.Index, before, after)
117134

118135
case *SliceExpr:
119-
walk(v, n.X)
136+
walk(n.X, before, after)
120137
if n.Low != nil {
121-
walk(v, n.Low)
138+
walk(n.Low, before, after)
122139
}
123140
if n.High != nil {
124-
walk(v, n.High)
141+
walk(n.High, before, after)
125142
}
126143

127144
case *CallExpr:
128-
walk(v, n.Fun)
129-
walkList(v, n.Args)
145+
walk(n.Fun, before, after)
146+
walkList(n.Args, before, after)
130147

131148
case *UnaryExpr:
132-
walk(v, n.X)
149+
walk(n.X, before, after)
133150

134151
case *BinaryExpr:
135-
walk(v, n.X)
136-
walk(v, n.Y)
152+
walk(n.X, before, after)
153+
walk(n.Y, before, after)
137154

138155
// Declarations
139156
case *ImportSpec:
140157
if n.Name != nil {
141-
walk(v, n.Name)
158+
walk(n.Name, before, after)
142159
}
143-
walk(v, n.Path)
160+
walk(n.Path, before, after)
144161

145162
case *BadDecl:
146163
// nothing to do
147164

148165
case *ImportDecl:
149-
walkList(v, n.Specs)
166+
walkList(n.Specs, before, after)
150167

151168
case *EmbedDecl:
152-
walk(v, n.Expr)
169+
walk(n.Expr, before, after)
153170

154171
case *LetClause:
155-
walk(v, n.Ident)
156-
walk(v, n.Expr)
172+
walk(n.Ident, before, after)
173+
walk(n.Expr, before, after)
157174

158175
case *Alias:
159-
walk(v, n.Ident)
160-
walk(v, n.Expr)
176+
walk(n.Ident, before, after)
177+
walk(n.Expr, before, after)
161178

162179
case *Comprehension:
163-
walkList(v, n.Clauses)
164-
walk(v, n.Value)
180+
walkList(n.Clauses, before, after)
181+
walk(n.Value, before, after)
165182

166183
// Files and packages
167184
case *File:
168-
walkList(v, n.Decls)
185+
walkList(n.Decls, before, after)
169186

170187
case *Package:
171-
walk(v, n.Name)
188+
walk(n.Name, before, after)
172189

173190
case *ForClause:
174191
if n.Key != nil {
175-
walk(v, n.Key)
192+
walk(n.Key, before, after)
176193
}
177-
walk(v, n.Value)
178-
walk(v, n.Source)
194+
walk(n.Value, before, after)
195+
walk(n.Source, before, after)
179196

180197
case *IfClause:
181-
walk(v, n.Condition)
198+
walk(n.Condition, before, after)
182199

183200
default:
184201
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
185202
}
186203

187-
v.After(node)
204+
after(node)
188205
}
189206

190207
type inspector struct {
@@ -200,14 +217,14 @@ type commentFrame struct {
200217
pos int8
201218
}
202219

203-
func (f *inspector) Before(node Node) Visitor {
220+
func (f *inspector) Before(node Node) bool {
204221
if f.before == nil || f.before(node) {
205222
f.commentStack = append(f.commentStack, f.current)
206223
f.current = commentFrame{cg: Comments(node)}
207224
f.visitComments(f.current.pos)
208-
return f
225+
return true
209226
}
210-
return nil
227+
return false
211228
}
212229

213230
func (f *inspector) After(node Node) {

0 commit comments

Comments
 (0)