Skip to main content

ast_lang/
lib.rs

1//! # ast_lang
2//!
3//! The syntax-tree substrate for the `-lang` family: the trait an AST node
4//! implements and the generic machinery that walks and rewrites a tree of those
5//! nodes. It owns no grammar — a language brings its own node type — so the same
6//! traversal code serves every language built on it.
7//!
8//! ## Model
9//!
10//! ast-lang builds on the pattern [`arena_lang`] establishes: a language defines a
11//! single node type `N` (almost always an `enum`), stores its nodes in an
12//! [`Arena<N>`](Arena), and wires the tree with [`Id<N>`](Id) handles rather than
13//! references — so a parent can hold its children without tangling the borrow
14//! checker. A node implements [`Node`] to report its [`span`](Node::span), enumerate
15//! its child handles ([`each_child`](Node::each_child)), and rebuild itself with
16//! remapped children ([`map_children`](Node::map_children)). Everything else is
17//! generic:
18//!
19//! - [`walk`] drives a [`Visitor`] over the tree, depth-first and iteratively, so
20//!   even a very deep tree never overflows the call stack. The visitor steers the
21//!   traversal with a [`Flow`] and accumulates whatever it needs.
22//! - [`transform`] rebuilds a tree into a destination arena, passing each node
23//!   through a closure — the rewrite (fold) operation, also iterative.
24//!
25//! ## Quickstart
26//!
27//! ```
28//! use ast_lang::{transform, walk, Arena, Flow, Id, Node, Span, Visitor};
29//!
30//! // A language defines its node type; ast-lang carries the rest.
31//! #[derive(Clone)]
32//! enum Expr {
33//!     Lit(i64, Span),
34//!     Add(Id<Expr>, Id<Expr>, Span),
35//! }
36//!
37//! impl Node for Expr {
38//!     fn span(&self) -> Span {
39//!         match self {
40//!             Expr::Lit(_, s) | Expr::Add(_, _, s) => *s,
41//!         }
42//!     }
43//!     fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
44//!         if let Expr::Add(a, b, _) = self {
45//!             f(*a);
46//!             f(*b);
47//!         }
48//!     }
49//!     fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
50//!         match self {
51//!             Expr::Lit(v, s) => Expr::Lit(*v, *s),
52//!             Expr::Add(a, b, s) => Expr::Add(f(*a), f(*b), *s),
53//!         }
54//!     }
55//! }
56//!
57//! let mut arena = Arena::new();
58//! let one = arena.alloc(Expr::Lit(1, Span::new(0, 1)));
59//! let two = arena.alloc(Expr::Lit(2, Span::new(4, 5)));
60//! let add = arena.alloc(Expr::Add(one, two, Span::new(0, 5)));
61//!
62//! // Walk: sum the literals.
63//! struct Sum(i64);
64//! impl Visitor<Expr> for Sum {
65//!     fn enter(&mut self, _: &Arena<Expr>, _: Id<Expr>, node: &Expr) -> Flow {
66//!         if let Expr::Lit(v, _) = node {
67//!             self.0 += *v;
68//!         }
69//!         Flow::Continue
70//!     }
71//! }
72//! let mut sum = Sum(0);
73//! walk(&arena, add, &mut sum);
74//! assert_eq!(sum.0, 3);
75//!
76//! // Transform: deep-copy into a fresh arena, doubling each literal.
77//! let mut doubled = Arena::new();
78//! let _ = transform(&arena, add, &mut doubled, |node| match node {
79//!     Expr::Lit(v, s) => Expr::Lit(v * 2, s),
80//!     other => other,
81//! });
82//! ```
83//!
84//! ## Features
85//!
86//! - `std` (default) — the standard library; without it the crate is `no_std`
87//!   (it always needs `alloc`). Forwards to `span-lang/std` and `arena-lang/std`.
88//!
89//! ## Stability
90//!
91//! The public surface is being designed across the 0.x series and freezes at
92//! `1.0.0`, after which it follows Semantic Versioning: no breaking changes before
93//! `2.0`, additions arrive in minor releases, and the MSRV (Rust 1.85) only rises
94//! in a minor. The frozen surface is catalogued in
95//! [`docs/API.md`](https://github.com/jamesgober/ast-lang/blob/main/docs/API.md).
96
97#![cfg_attr(not(feature = "std"), no_std)]
98#![cfg_attr(docsrs, feature(doc_cfg))]
99#![deny(missing_docs)]
100#![forbid(unsafe_code)]
101
102extern crate alloc;
103
104mod node;
105mod transform;
106mod visit;
107
108pub use node::Node;
109pub use transform::transform;
110pub use visit::{Flow, Visitor, walk};
111
112// Re-exported so a downstream defining and traversing an AST can name the storage
113// and position types this crate's API is built on without also having to depend on
114// `arena-lang` and `span-lang` directly.
115pub use arena_lang::{Arena, Id};
116pub use span_lang::Span;