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

Native lexer

lex is the first native-parser layer. It accepts any byte iterator and returns the same source-spanned TokenStream used by quoting and emission. The raw token categories are Group, Ident, Literal, and Punct; comments are retained as trivia attached to those atoms or to the surrounding stream. The lexer does not synthesize typed nodes, adjacency, EOC, or EOF. Physical layout remains recoverable from source gaps. After greedy token recognition, structural delimiter pairs become recursive Group trees that retain exact opening and closing spans.

Generated Token![..] values are checked typed refinements of these raw atoms, not another TokenTree variant. Tokenize performs the later framing step and represents adjacency explicitly as CellTokenTree::Adj; converting the cell back to lexical tokens removes it. Thus f(x) and f (x) have the same framed operator, while return 1 has none. The lexer applies context-free maximal munch across generated operators, delimiters, and comment openers. Consequently |--1 begins with the |- operator rather than a comment, while a token beginning with -- is a line comment. Likewise, (*) remains the single greedy operator rather than a parenthesized group. Quotation marks are part of a single literal token, not delimiter groups.

$ remains part of an identifier. Package$name$12 is one IdentToken; its typed Symbol merely adds optional subspans for Package and 12. Only one package qualifier and one decimal index can be marked. Exact Core$word spellings use a separate hardcoded renaming hook, so Core$not behaves as not, but Core$Core$not is an ordinary symbol.

Precision precedes exponent in a decimal real literal. 1p53, .5p20, and 1.25p53e3 are single FloatLiteral nodes. In 1e3p20, the completed exponent ends the literal and p20 is the adjacent symbol.

The lexer owns one validated source buffer and gives every atom a span into the same allocation. Variable spellings are therefore sliced from source instead of copied into token nodes. The byte-iterator entry point first materializes its finite input; lex_str starts directly from source text.

#![allow(unused)]
fn main() {
use m2_syn::{TokenTree, lex_str};

let tokens = lex_str("***1")?;
let spellings = tokens
    .into_iter()
    .filter_map(|token| match token {
        TokenTree::Literal(token) => Some(token.text().to_owned()),
        token => token.spelling().map(str::to_owned),
    })
    .collect::<Vec<_>>();

assert_eq!(spellings, ["**", "*", "1"]);
Ok::<(), m2_syn::LexError>(())
}