Walk the AST
Problem. You have parsed a document and want to visit every classified Aozora construct in source order — to count node kinds, build an index, or drive a custom renderer.
Solution
AozoraTree::source_nodes returns a slice of SourceNode, one per
classified construct, sorted by source position. Each carries a
source_span (byte offsets into the source) and a node, which is a
NodeRef tagging the sentinel kind that fired.
use aozora::{Document, NodeRef};
fn main() {
let source = "|青梅《おうめ》の[#ここから2字下げ]街道《かいどう》[#ここで字下げ終わり]";
let doc = Document::new(source);
let tree = doc.parse();
for sn in tree.source_nodes() {
let span = sn.source_span;
match sn.node {
NodeRef::Inline(node) | NodeRef::BlockLeaf(node) => {
// `node` is an AozoraNode; `.kind()` is the cross-cutting tag.
println!("{:>3}..{:<3} {:?}", span.start, span.end, node.kind());
}
NodeRef::BlockOpen(kind) => {
println!("{:>3}..{:<3} open {kind:?}", span.start, span.end);
}
NodeRef::BlockClose(kind) => {
println!("{:>3}..{:<3} close {kind:?}", span.start, span.end);
}
}
}
}
Expected output
0..21 Ruby
24..45 open Indent { amount: 2 }
45..72 Ruby
72..105 close Indent { amount: 2 }
(Byte offsets are over the full-width UTF-8 source; the exact numbers depend on your input.)
How the surface is shaped
source_nodes() is the source-coordinate view — the one editor
features and indexers want. The NodeRef variant tells you where the
construct landed:
Inline— an inline construct (ruby, bouten, gaiji, 縦中横, …) carrying anAozoraNode.BlockLeaf— a standalone block construct (page break, section break, heading) carrying anAozoraNode.BlockOpen/BlockClose— the two ends of a paired container ([#ここから…]/[#ここで…終わり]), each carrying aContainerKind.
NodeRef::kind() collapses all four into a single
NodeKind tag when you only need the discriminant;
NodeRef::sentinel_kind() gives the sentinel family.
Matching container open/close pairs
The walk above sees opens and closes as independent events. When you
need them paired — “where does this [#ここから…] close?” —
read AozoraTree::container_pairs instead, which yields one entry per
balanced pair (in normalized coordinates). The inline-delimiter
analogue (ruby 《…》, brackets) is AozoraTree::pairs. See
Indent & align containers for the container
model.
Reaching inside a node
AozoraNode is a borrowed enum; its payload fields hold the
construct’s content. To pull text out of a specific variant — say the
base and reading of a ruby node — match the variant and read its
Content; that is the next recipe,
Extract ruby pairs.
See also
- Runnable example:
just example walk_ast(crates/aozora/examples/walk_ast.rs). - Extract ruby pairs — the same walk, narrowed to one node kind, reading its content.
- Library Quickstart → Walking the AST.
- Node reference — every
NodeKindand what it carries. - Borrowed-arena AST — why nodes borrow from the
Document’s arena and what that means for lifetimes.