ast_lang/visit.rs
1//! Read-only traversal: the [`Visitor`] trait and the [`walk`] driver.
2
3use alloc::vec::Vec;
4
5use arena_lang::{Arena, Id};
6
7use crate::Node;
8
9/// What a [`Visitor`] tells [`walk`] to do after entering a node.
10///
11/// Returned from [`Visitor::enter`] to steer the traversal:
12///
13/// - [`Continue`](Flow::Continue) — descend into this node's children, then leave it.
14/// - [`SkipChildren`](Flow::SkipChildren) — do not descend, but still leave this node.
15/// - [`Stop`](Flow::Stop) — abandon the whole walk now; no further node is entered
16/// or left.
17///
18/// # Examples
19///
20/// ```
21/// use ast_lang::Flow;
22///
23/// // The default for a visitor that wants the full tree.
24/// assert_eq!(Flow::default(), Flow::Continue);
25/// ```
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
27pub enum Flow {
28 /// Descend into the node's children, then call [`Visitor::leave`] for it.
29 #[default]
30 Continue,
31 /// Skip the node's children but still call [`Visitor::leave`] for it.
32 SkipChildren,
33 /// Stop the entire walk immediately; no further `enter` or `leave` runs.
34 Stop,
35}
36
37/// A read-only traversal over a tree of [`Node`]s.
38///
39/// Implement `Visitor` to observe a tree without changing it — collecting spans,
40/// counting forms, building a symbol table, accumulating diagnostics. The visitor
41/// holds whatever state it accumulates (`&mut self` is threaded through every
42/// callback), and [`walk`] drives it over the tree.
43///
44/// Both methods have defaults — [`enter`](Visitor::enter) continues, [`leave`](Visitor::leave)
45/// does nothing — so a visitor overrides only the one it needs. `enter` runs on the
46/// way down and returns a [`Flow`] to steer the walk; `leave` runs on the way back
47/// up, after the node's children have been fully visited.
48///
49/// # Examples
50///
51/// A visitor that sums the integer literals in an expression tree.
52///
53/// ```
54/// use ast_lang::{walk, Arena, Flow, Id, Node, Span, Visitor};
55///
56/// enum Expr {
57/// Lit(i64, Span),
58/// Add(Id<Expr>, Id<Expr>, Span),
59/// }
60///
61/// impl Node for Expr {
62/// fn span(&self) -> Span {
63/// match self {
64/// Expr::Lit(_, s) | Expr::Add(_, _, s) => *s,
65/// }
66/// }
67/// fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
68/// if let Expr::Add(a, b, _) = self {
69/// f(*a);
70/// f(*b);
71/// }
72/// }
73/// fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
74/// match self {
75/// Expr::Lit(v, s) => Expr::Lit(*v, *s),
76/// Expr::Add(a, b, s) => Expr::Add(f(*a), f(*b), *s),
77/// }
78/// }
79/// }
80///
81/// #[derive(Default)]
82/// struct Sum(i64);
83/// impl Visitor<Expr> for Sum {
84/// fn enter(&mut self, _arena: &Arena<Expr>, _id: Id<Expr>, node: &Expr) -> Flow {
85/// if let Expr::Lit(v, _) = node {
86/// self.0 += *v;
87/// }
88/// Flow::Continue
89/// }
90/// }
91///
92/// let mut arena = Arena::new();
93/// let one = arena.alloc(Expr::Lit(1, Span::new(0, 1)));
94/// let two = arena.alloc(Expr::Lit(2, Span::new(4, 5)));
95/// let add = arena.alloc(Expr::Add(one, two, Span::new(0, 5)));
96///
97/// let mut sum = Sum::default();
98/// walk(&arena, add, &mut sum);
99/// assert_eq!(sum.0, 3);
100/// ```
101pub trait Visitor<N: Node> {
102 /// Called when the walk reaches a node, before its children.
103 ///
104 /// Return a [`Flow`] to steer the traversal: [`Continue`](Flow::Continue) to
105 /// descend, [`SkipChildren`](Flow::SkipChildren) to prune the subtree,
106 /// [`Stop`](Flow::Stop) to end the walk. The default descends.
107 fn enter(&mut self, arena: &Arena<N>, id: Id<N>, node: &N) -> Flow {
108 let _ = (arena, id, node);
109 Flow::Continue
110 }
111
112 /// Called after a node's children have been fully visited, on the way back up.
113 ///
114 /// Runs for every node that was entered and not stopped at, including one whose
115 /// children were skipped. The default does nothing.
116 fn leave(&mut self, arena: &Arena<N>, id: Id<N>, node: &N) {
117 let _ = (arena, id, node);
118 }
119}
120
121/// One pending unit of work for the iterative walk: a node still to enter, or a
122/// node whose children are done and which is ready to leave.
123enum Step<N> {
124 Enter(Id<N>),
125 Leave(Id<N>),
126}
127
128/// Walks the tree rooted at `root`, driving `visitor` over every node.
129///
130/// The traversal is depth-first and **iterative** — it keeps its own work stack on
131/// the heap rather than recursing — so a tree of any depth is walked without
132/// risking a call-stack overflow. Each node is entered before its children
133/// ([`Visitor::enter`]) and left after them ([`Visitor::leave`]); children are
134/// visited in the order [`Node::each_child`] yields them. The visitor can prune or
135/// stop the walk through the [`Flow`] it returns from `enter`.
136///
137/// A handle that names no live node in `arena` — including a `root` that is out of
138/// range — is silently skipped, so a stale handle is a no-op, never a panic.
139///
140/// # Examples
141///
142/// ```
143/// use ast_lang::{walk, Arena, Flow, Id, Node, Span, Visitor};
144///
145/// # enum Expr { Lit(i64, Span), Neg(Id<Expr>, Span) }
146/// # impl Node for Expr {
147/// # fn span(&self) -> Span { match self { Expr::Lit(_, s) | Expr::Neg(_, s) => *s } }
148/// # fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) { if let Expr::Neg(a, _) = self { f(*a) } }
149/// # fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
150/// # match self { Expr::Lit(v, s) => Expr::Lit(*v, *s), Expr::Neg(a, s) => Expr::Neg(f(*a), *s) }
151/// # }
152/// # }
153/// // Count how many nodes the walk visits.
154/// struct Count(usize);
155/// impl Visitor<Expr> for Count {
156/// fn enter(&mut self, _: &Arena<Expr>, _: Id<Expr>, _: &Expr) -> Flow {
157/// self.0 += 1;
158/// Flow::Continue
159/// }
160/// }
161///
162/// let mut arena = Arena::new();
163/// let lit = arena.alloc(Expr::Lit(7, Span::new(1, 2)));
164/// let neg = arena.alloc(Expr::Neg(lit, Span::new(0, 2)));
165///
166/// let mut count = Count(0);
167/// walk(&arena, neg, &mut count);
168/// assert_eq!(count.0, 2); // Neg and its Lit child
169/// ```
170pub fn walk<N, V>(arena: &Arena<N>, root: Id<N>, visitor: &mut V)
171where
172 N: Node,
173 V: Visitor<N> + ?Sized,
174{
175 let mut stack: Vec<Step<N>> = Vec::new();
176 stack.push(Step::Enter(root));
177 // One reusable buffer for a node's children, refilled per node instead of
178 // allocating a fresh vector each time.
179 let mut scratch: Vec<Id<N>> = Vec::new();
180
181 while let Some(step) = stack.pop() {
182 match step {
183 Step::Enter(id) => {
184 let Some(node) = arena.get(id) else {
185 continue;
186 };
187 match visitor.enter(arena, id, node) {
188 Flow::Stop => return,
189 Flow::SkipChildren => visitor.leave(arena, id, node),
190 Flow::Continue => {
191 stack.push(Step::Leave(id));
192 scratch.clear();
193 node.each_child(&mut |child| scratch.push(child));
194 // Pop the children back off in reverse so the main stack
195 // yields them in source order.
196 while let Some(child) = scratch.pop() {
197 stack.push(Step::Enter(child));
198 }
199 }
200 }
201 }
202 Step::Leave(id) => {
203 if let Some(node) = arena.get(id) {
204 visitor.leave(arena, id, node);
205 }
206 }
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use crate::Span;
214
215 use super::*;
216
217 /// A leaf or a pair, enough to test ordering and pruning.
218 enum E {
219 Leaf(u32, Span),
220 Pair(Id<E>, Id<E>, Span),
221 }
222
223 impl Node for E {
224 fn span(&self) -> Span {
225 match self {
226 E::Leaf(_, s) | E::Pair(_, _, s) => *s,
227 }
228 }
229 fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
230 if let E::Pair(a, b, _) = self {
231 f(*a);
232 f(*b);
233 }
234 }
235 fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
236 match self {
237 E::Leaf(v, s) => E::Leaf(*v, *s),
238 E::Pair(a, b, s) => E::Pair(f(*a), f(*b), *s),
239 }
240 }
241 }
242
243 #[derive(Default)]
244 struct Tags(Vec<u32>);
245 impl Visitor<E> for Tags {
246 fn enter(&mut self, _: &Arena<E>, _: Id<E>, node: &E) -> Flow {
247 if let E::Leaf(v, _) = node {
248 self.0.push(*v);
249 }
250 Flow::Continue
251 }
252 }
253
254 fn pair_tree() -> (Arena<E>, Id<E>) {
255 let mut arena = Arena::new();
256 let a = arena.alloc(E::Leaf(1, Span::new(0, 1)));
257 let b = arena.alloc(E::Leaf(2, Span::new(1, 2)));
258 let root = arena.alloc(E::Pair(a, b, Span::new(0, 2)));
259 (arena, root)
260 }
261
262 #[test]
263 fn test_flow_default_is_continue() {
264 assert_eq!(Flow::default(), Flow::Continue);
265 }
266
267 #[test]
268 fn test_walk_enters_leaves_left_to_right() {
269 let (arena, root) = pair_tree();
270 let mut tags = Tags::default();
271 walk(&arena, root, &mut tags);
272 assert_eq!(tags.0, [1, 2]);
273 }
274
275 #[test]
276 fn test_walk_on_missing_root_is_a_noop() {
277 let arena: Arena<E> = Arena::new();
278 let (other, root) = pair_tree();
279 let _ = &other;
280 let mut tags = Tags::default();
281 walk(&arena, root, &mut tags);
282 assert!(tags.0.is_empty());
283 }
284
285 #[test]
286 fn test_skip_children_prunes_descendants() {
287 struct SkipRoot {
288 leaves: u32,
289 }
290 impl Visitor<E> for SkipRoot {
291 fn enter(&mut self, _: &Arena<E>, _: Id<E>, node: &E) -> Flow {
292 match node {
293 E::Pair(..) => Flow::SkipChildren,
294 E::Leaf(..) => {
295 self.leaves += 1;
296 Flow::Continue
297 }
298 }
299 }
300 }
301 let (arena, root) = pair_tree();
302 let mut v = SkipRoot { leaves: 0 };
303 walk(&arena, root, &mut v);
304 assert_eq!(v.leaves, 0); // the pair's leaves were pruned
305 }
306}