Skip to main content

Node

Trait Node 

Source
pub trait Node: Sized {
    // Required methods
    fn span(&self) -> Span;
    fn each_child(&self, f: &mut dyn FnMut(Id<Self>));
    fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self;
}
Expand description

The contract a language’s syntax-tree node satisfies so the generic traversals in this crate can walk and rebuild it.

ast-lang owns no grammar: a language defines its own node type — almost always a single enum with a variant per syntactic form — stores its nodes in an Arena, and wires the tree with Id handles. Implementing Node for that type is what lets walk and transform operate on it without knowing what the variants are.

The contract is three operations, and nothing more:

  • span — the byte range of source this node covers.
  • each_child — report the node’s direct child handles, in source order. Read traversal is built on this.
  • map_children — rebuild the node with each child handle replaced by another, preserving everything else. Rewriting is built on this.

The two traversal operations are kept separate so a leaf node — one with no children — is an empty each_child and a plain clone in map_children.

§Examples

A minimal expression tree. The node carries its span and its child handles; the three methods just thread those handles through.

use ast_lang::{Id, Node, Span};

enum Expr {
    Lit(i64, Span),
    Neg(Id<Expr>, Span),
    Add(Id<Expr>, Id<Expr>, Span),
}

impl Node for Expr {
    fn span(&self) -> Span {
        match self {
            Expr::Lit(_, s) | Expr::Neg(_, s) | Expr::Add(_, _, s) => *s,
        }
    }

    fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
        match self {
            Expr::Lit(..) => {}
            Expr::Neg(a, _) => f(*a),
            Expr::Add(a, b, _) => {
                f(*a);
                f(*b);
            }
        }
    }

    fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
        match self {
            Expr::Lit(v, s) => Expr::Lit(*v, *s),
            Expr::Neg(a, s) => Expr::Neg(f(*a), *s),
            Expr::Add(a, b, s) => Expr::Add(f(*a), f(*b), *s),
        }
    }
}

Required Methods§

Source

fn span(&self) -> Span

Returns the byte range of source this node covers.

A synthetic node with no source of its own returns an empty span (for example Span::empty at the insertion point); every node has a span so a later pass can always point a diagnostic at it.

Source

fn each_child(&self, f: &mut dyn FnMut(Id<Self>))

Calls f once with each direct child handle, in source order.

“Direct” means one level down — a child reports its own children when it is visited in turn. A leaf node calls f zero times. The order f is called in is the order the children appear in the source, which is the order walk visits them.

Source

fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self

Rebuilds this node with each child handle c replaced by f(c), preserving the span and every non-child field.

This is the one language-specific step of a transform: the machinery supplies an f that maps each old child handle to its rebuilt counterpart, and this method threads those new handles back into a fresh node. A leaf node ignores f and returns a copy of itself.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§