Skip to main content

ast_lang/
node.rs

1//! The trait every AST node implements.
2
3use arena_lang::Id;
4use span_lang::Span;
5
6/// The contract a language's syntax-tree node satisfies so the generic traversals
7/// in this crate can walk and rebuild it.
8///
9/// ast-lang owns no grammar: a language defines its own node type — almost always
10/// a single `enum` with a variant per syntactic form — stores its nodes in an
11/// [`Arena`](arena_lang::Arena), and wires the tree with [`Id`] handles. Implementing
12/// `Node` for that type is what lets [`walk`](crate::walk) and
13/// [`transform`](crate::transform) operate on it without knowing what the variants
14/// are.
15///
16/// The contract is three operations, and nothing more:
17///
18/// - [`span`](Node::span) — the byte range of source this node covers.
19/// - [`each_child`](Node::each_child) — report the node's direct child handles, in
20///   source order. Read traversal is built on this.
21/// - [`map_children`](Node::map_children) — rebuild the node with each child handle
22///   replaced by another, preserving everything else. Rewriting is built on this.
23///
24/// The two traversal operations are kept separate so a leaf node — one with no
25/// children — is an empty `each_child` and a plain clone in `map_children`.
26///
27/// # Examples
28///
29/// A minimal expression tree. The node carries its span and its child handles; the
30/// three methods just thread those handles through.
31///
32/// ```
33/// use ast_lang::{Id, Node, Span};
34///
35/// enum Expr {
36///     Lit(i64, Span),
37///     Neg(Id<Expr>, Span),
38///     Add(Id<Expr>, Id<Expr>, Span),
39/// }
40///
41/// impl Node for Expr {
42///     fn span(&self) -> Span {
43///         match self {
44///             Expr::Lit(_, s) | Expr::Neg(_, s) | Expr::Add(_, _, s) => *s,
45///         }
46///     }
47///
48///     fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
49///         match self {
50///             Expr::Lit(..) => {}
51///             Expr::Neg(a, _) => f(*a),
52///             Expr::Add(a, b, _) => {
53///                 f(*a);
54///                 f(*b);
55///             }
56///         }
57///     }
58///
59///     fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
60///         match self {
61///             Expr::Lit(v, s) => Expr::Lit(*v, *s),
62///             Expr::Neg(a, s) => Expr::Neg(f(*a), *s),
63///             Expr::Add(a, b, s) => Expr::Add(f(*a), f(*b), *s),
64///         }
65///     }
66/// }
67/// ```
68pub trait Node: Sized {
69    /// Returns the byte range of source this node covers.
70    ///
71    /// A synthetic node with no source of its own returns an empty span (for
72    /// example [`Span::empty`](span_lang::Span::empty) at the insertion point);
73    /// every node has a span so a later pass can always point a diagnostic at it.
74    fn span(&self) -> Span;
75
76    /// Calls `f` once with each direct child handle, in source order.
77    ///
78    /// "Direct" means one level down — a child reports its own children when it is
79    /// visited in turn. A leaf node calls `f` zero times. The order `f` is called
80    /// in is the order the children appear in the source, which is the order
81    /// [`walk`](crate::walk) visits them.
82    fn each_child(&self, f: &mut dyn FnMut(Id<Self>));
83
84    /// Rebuilds this node with each child handle `c` replaced by `f(c)`,
85    /// preserving the span and every non-child field.
86    ///
87    /// This is the one language-specific step of a [`transform`](crate::transform):
88    /// the machinery supplies an `f` that maps each old child handle to its rebuilt
89    /// counterpart, and this method threads those new handles back into a fresh
90    /// node. A leaf node ignores `f` and returns a copy of itself.
91    fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self;
92}