Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 an AozoraNode.
  • BlockLeaf — a standalone block construct (page break, section break, heading) carrying an AozoraNode.
  • BlockOpen / BlockClose — the two ends of a paired container ([#ここから…] / [#ここで…終わり]), each carrying a ContainerKind.

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