lexer-lang 1.0.0

Scanner/tokenizer core for hand-written and generated lexers.
Documentation

Installation

[dependencies]
lexer-lang = "1"

Usage

A lexer is a loop over a Cursor: peek at the next character, consume its run, and emit a Token<K> of the language's own kind.

use intern_lang::Interner;
use token_lang::{Symbol, TokenKind};
use lexer_lang::Cursor;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Kind {
    Ident(Symbol),
    Number,
    Whitespace,
    Eof,
}
impl TokenKind for Kind {
    fn is_trivia(&self) -> bool { matches!(self, Kind::Whitespace) }
    fn is_eof(&self) -> bool { matches!(self, Kind::Eof) }
    fn symbol(&self) -> Option<Symbol> {
        match self { Kind::Ident(s) => Some(*s), _ => None }
    }
}

let mut interner = Interner::new();
let mut cursor = Cursor::new("x42 7");
while let Some(c) = cursor.first() {
    let kind = if c.is_whitespace() {
        cursor.eat_while(char::is_whitespace);
        Kind::Whitespace
    } else if c.is_ascii_digit() {
        cursor.eat_while(|c| c.is_ascii_digit());
        Kind::Number
    } else {
        cursor.eat_while(|c| c.is_ascii_alphanumeric());
        Kind::Ident(cursor.intern_lexeme(&mut interner))
    };
    let _token = cursor.emit(kind);
}

See docs/API.md for the full surface.

Status

Status: stable (1.0). v1.0.0 freezes the Cursor scanner under Semantic Versioning: no breaking changes before 2.0. See the ROADMAP and docs/API.md.

Contributing

See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.