Lexical Structure
The lexer is a hand-written scanner over a UTF-8 source string. It is
non-panicking and never stops early: recoverable problems are collected and
scanning continues, so one run reports every lexical error in the file. The
token stream always ends with exactly one Eof token.
Trivia
Section titled “Trivia”- Whitespace (any Unicode whitespace) is skipped.
- Comments are
//to end of line. Block comments (/* */) are not implemented yet — they arrive in Phase 7, so/*currently lexes asSlashthenStar. - Trivia produces no tokens.
Identifiers
Section titled “Identifiers”An identifier starts with _ or any alphabetic character, and continues with
alphanumerics and _. Unicode letters and digits are allowed (e.g. café).
Keywords are recognised by spelling; a keyword prefix is an ordinary identifier
(iff, letx, returns, Self, _ are all identifiers).
Keywords
Section titled “Keywords”The 23 reserved words:
fn let mut if else for while return struct enum matchuse mod pub true false in break continue const type impl selftrue and false are keyword tokens (not identifiers).
Literals
Section titled “Literals”Integers
Section titled “Integers”- Decimal, plus
0x(hex),0o(octal),0b(binary) prefixes. _is allowed anywhere among the digits as a separator:1_000_000,0x_ff.- Values must fit in
i64. Overflow reportsIntegerOverflowand yields the value0. A prefix with no digits (0x) reportsMalformedNumber.
Floats
Section titled “Floats”1.0,0.5, with optional exponents:1e10,2.5e-3,1E+5.- A
.only starts a fraction when a digit follows, so0..10lexes asInt(0) DotDot Int(10)— the range is not a float. 1.foo(dot before an identifier) isMalformedNumber.- A dangling exponent marker is not consumed:
1eisInt(1)followed byIdent("e"), not a broken float.
Strings
Section titled “Strings”- Double-quoted:
"...", with escapes\n \r \t \0 \\ \"and\xNN(exactly two hex digits). - Unknown escapes (
\q) reportInvalidEscapebut recover, keeping the character in the value. \xwithout two hex digits reportsInvalidHexEscape.- A string that runs to the end of the line or end of file reports
UnterminatedStringand recovers (scanning picks back up on the next line).
Characters
Section titled “Characters”Character literals ('a') are not part of the language.
Operators and punctuation
Section titled “Operators and punctuation”Matched longest-first:
| Token(s) | Spelling |
|---|---|
| arithmetic | + - * / % |
| bitwise | & | ^ << >> |
| logical | && || ! |
| comparison | == != < <= > >= |
| assignment | = |
| compound assignment | += -= *= /= |
| arrows | -> => |
| ranges | .. ..= |
| path separator | :: |
| delimiters | : ; , . ( ) { } [ ] |
Errors
Section titled “Errors”All lexer errors are recoverable; the lexer reports every instance in one run.
| Error | Meaning | Help shown |
|---|---|---|
UnknownChar(c) |
character that can’t start any token | — |
UnterminatedString |
string ran to end of line or end of file | add a closing " |
InvalidEscape(c) |
unknown escape sequence \c |
lists valid escapes |
InvalidHexEscape |
\x without exactly two hex digits |
— |
MalformedNumber |
0x/0b/0o with no digits, or 1. followed by a non-digit |
a digit must follow the prefix or decimal point |
IntegerOverflow |
integer literal too large for i64 |
maximum is 9223372036854775807 |
InvalidFloat |
float literal that failed to parse | — |
Errors carry the same Span machinery as tokens, so diagnostics can point
exactly at the offending text.
Every token and error carries a Span: a half-open byte range [start, end)
into the source file (u32 offsets, so spans track multi-byte characters
correctly). The Eof token’s span is the empty range at the end of the file.
Span::text(src) slices the original source back out of the span.
The public API
Section titled “The public API”nex-lexer exposes:
tokenize(src) -> (Vec<Token>, Vec<LexError>)— scan everythingtokenize_kinds(src) -> Vec<TokenKind>— drop the spans, for testsdump_tokens(src) -> String— oneKind@start..endline per token (plus an-- errors --section); the format used bynex lexand the snapshot testsLexer— the scanner itself, usable as anIteratorover tokens
Robustness guarantees
Section titled “Robustness guarantees”The test suite verifies that the lexer:
- never panics, even on a 200 000-character run of junk
- always terminates with a single
Eofunder a deterministic mutation fuzzer - produces non-overlapping, in-bounds spans for any input