1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! # lexer_lang
//!
//! The scanner that turns source text into a token stream.
//!
//! lexer-lang provides the [`Cursor`] — a zero-copy scanner over a `&str` — the
//! primitive every lexer is built on, hand-written or generated. A lexer drives the
//! cursor with peek/advance steps and turns each scanned run into a
//! [`Token<K>`](Token), where `K` is the language's own token kind. The cursor owns
//! scanning and nothing else: it does not decide a language's keywords (that is `K`),
//! collect diagnostics (that is the lexer author's, through `diag-lang`), or store
//! sources (that is `source-lang`).
//!
//! It is the first MAIN-tier crate: the seam where a front-end stops handling raw
//! bytes and starts handling tokens.
//!
//! ## The model
//!
//! A lexer is a loop. Look at the next character, decide what kind of token starts
//! there, consume its run, and [`emit`](Cursor::emit) a token. The cursor supplies
//! exactly the primitives that loop needs:
//!
//! - look ahead — [`first`](Cursor::first), [`second`](Cursor::second),
//! [`is_eof`](Cursor::is_eof);
//! - consume — [`bump`](Cursor::bump), [`bump_if`](Cursor::bump_if),
//! [`eat_while`](Cursor::eat_while);
//! - read the run — [`lexeme`](Cursor::lexeme), [`token_span`](Cursor::token_span),
//! [`intern_lexeme`](Cursor::intern_lexeme);
//! - finish a token — [`emit`](Cursor::emit), which builds the `Token<K>` and starts
//! the next, or [`reset_token`](Cursor::reset_token), which drops the run (for
//! trivia the lexer discards rather than emits).
//!
//! Everything is zero-copy: the cursor borrows the source, reports positions as
//! [`BytePos`] and [`Span`], and hands lexemes back as borrowed `&str`. Nothing here
//! allocates.
//!
//! ## Positions
//!
//! The spans a cursor emits live in a global position space offset by a base.
//! [`Cursor::for_source`] sets that base from a [`SourceFile`], so the spans resolve
//! directly against the [`SourceMap`](source_lang::SourceMap) a diagnostic renders
//! through — token spans and error labels share one coordinate space.
//!
//! ## Quickstart
//!
//! ```
//! 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");
//! let mut kinds = Vec::new();
//! 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))
//! };
//! kinds.push(cursor.emit(kind).into_kind());
//! }
//! assert!(matches!(kinds[0], Kind::Ident(_)));
//! assert_eq!(kinds[1], Kind::Whitespace);
//! assert_eq!(kinds[2], Kind::Number);
//! ```
//!
//! ## `no_std`
//!
//! The crate is `no_std`-compatible and allocation-free: the cursor borrows its
//! source and needs neither the standard library nor `alloc`. The default `std`
//! feature only forwards to the standard-library builds of the family crates.
//! Disable default features to build for a `no_std` target.
//!
//! ## Stability
//!
//! As of `1.0.0` the public surface is **stable** and follows Semantic Versioning:
//! no breaking changes before `2.0`, additions arrive in minor releases, and the
//! MSRV (Rust 1.85) only rises in a minor. The frozen surface is catalogued in
//! [`docs/API.md`](https://github.com/jamesgober/lexer-lang/blob/main/docs/API.md).
pub use Cursor;
// Re-exported so a lexer can name the types the cursor's API speaks without also
// depending on the family crates directly.
pub use ;
pub use SourceFile;
pub use ;
pub use ;