hermes_ast/visitor.rs
1//! AST traversal.
2use crate::node::{Node, NodeField};
3use crate::context::GCLock;
4
5/// Read-only visitor. Implementors override `visit_node`; the default recurses.
6/// (Unchanged from phase 1 — used by the GC marker in `context.rs`.)
7pub trait Visitor<'gc> {
8 /// Called once for `node`. The default recurses into its children.
9 fn visit_node(&mut self, node: &'gc Node<'gc>) {
10 node.visit_children(self);
11 }
12}
13
14/// The path to the node currently being visited: its parent and the field of
15/// the parent it occupies. Mirrors juno's `Path`.
16#[derive(Debug, Copy, Clone)]
17pub struct Path<'gc> {
18 /// The node that owns the field being visited.
19 pub parent: &'gc Node<'gc>,
20 /// Which structural child field of `parent` the visited node occupies.
21 pub field: NodeField,
22}
23
24impl<'gc> Path<'gc> {
25 /// Build a path from a parent node and one of its child fields.
26 pub fn new(parent: &'gc Node<'gc>, field: NodeField) -> Path<'gc> {
27 Path { parent, field }
28 }
29}
30
31/// What a [`VisitorMut`] did to an element of the AST.
32#[derive(Debug)]
33pub enum TransformResult<T> {
34 /// No change.
35 Unchanged,
36 /// Remove the element if possible. A required single child that is removed
37 /// is replaced with an `EmptyStatement`; an optional child becomes `None`;
38 /// a list element is dropped.
39 Removed,
40 /// Replace the element with the wrapped one.
41 Changed(T),
42 /// Replace the element with several (only valid inside a `NodeList`).
43 Expanded(Vec<T>),
44}
45
46/// The transforming visitor. `call` returns how `node` should be transformed.
47/// A typical impl matches specific nodes and otherwise recurses+rebuilds via
48/// `node.visit_children_mut(ctx, self)`.
49pub trait VisitorMut<'gc> {
50 /// Visit `node`, reached via `path` (`None` at the root), and return how
51 /// it should be transformed.
52 fn call(
53 &mut self,
54 ctx: &'gc GCLock<'_, '_>,
55 node: &'gc Node<'gc>,
56 path: Option<Path<'gc>>,
57 ) -> TransformResult<&'gc Node<'gc>>;
58}