Skip to main content

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}