Lexing: From Text to Tokens
Work through examples, try the reasoning yourself, then put it into practice.
View SlidesBefore you start
- Recognize the expression milestone and one-based source positions.
What you will practice
- Trace maximal-munch scanning and keyword boundaries.
- Maintain source cursor positions through comments and newlines.
- Separate the tiny lexer from later string-capable lexers.
In this lesson
Lesson
3 min readLexing: From Text to Tokens
The tiny-lexer produces tokens for the first milestone. It recognizes integer
literals, identifiers, the keywords let/fn/if/else/return, the listed operators,
and punctuation including .. It does not recognize strings, booleans as
keywords, &&, or arrays. Later lexers extend this contract.
Cursor invariant
Keep a byte index plus a one-based (line,column). Capture a token's start before
consuming its characters. Newline advances the line and resets column to 1;
a tab consumes one source column in this ASCII exercise. EOF describes the
cursor after the final character, not the start of the last token.
Read a full identifier before testing keyword membership: ifx is one
identifier, not keyword if followed by x. For =, look ahead for another
=; for /, look ahead for / before emitting division. A comment ends at
newline or EOF. Consume the comment text without emitting tokens, then update
the newline through the usual cursor rule.
Worked reconstruction
For the exact source let x=12;\n//n\nx!=3, where \n means an actual newline:
| Token | Start |
|---|---|
LET let |
1:1 |
IDENT x |
1:5 |
TokenAssign = |
1:6 |
NUMBER 12 |
1:7 |
SEMICOLON ; |
1:9 |
IDENT x |
3:1 |
TokenNotEqual != |
3:2 |
NUMBER 3 |
3:4 |
| EOF | 3:5 |
The comment produces no tokens but still consumes line 2. A leading minus is
an operator token followed by a number, so -12 is not one signed-number token.
Tokenizing first makes subtraction and unary negation the parser's decision.
From trace to implementation
Start with identifiers/numbers and EOF, then operators with lookahead, then comments/position updates. Every loop iteration must consume input, emit EOF, or return an error; otherwise an unexpected character can cause an infinite loop. Unexpected characters return an error rather than disappearing.
Debugging lab
A scanner reads returnValue==7 as RETURN, IDENT(Value), TokenAssign, TokenAssign, NUMBER. Identify two independent defects and specify the correct token boundaries.
Compare your diagnosis after attempting the lab
Scan the complete identifier returnValue before keyword lookup. Prefer the legal two-character == token to two = tokens. The correct boundaries are IDENT(returnValue), TokenEqual, NUMBER(7), EOF.
Transfer task
Construct three inputs ending immediately after /, after // note, and after a newline. Predict each EOF position before running. Then change one character to an unsupported symbol and confirm the scanner makes progress by returning an error.
Carry forward
Carry deterministic tokens with start positions into expression parsing. Keep token boundary tests separate from AST tests; a parser cannot recover a token that the scanner split incorrectly.
Build tiny-lexer. Keep a before/after trace of one failing test. Use the checkpoint to diagnose specific skills; passing it does not replace implementing and testing the artifact.
Practice sequence
Use the order below for a guided path, or choose an item to revisit. A pass records a successful attempt; try a fresh example later to check what you can reconstruct.
- Step 1
Tiny Lexer
Turn source text into tokens with line and column positions.
- Step 2
Lexing: From Text to Tokens Checkpoint
Predict fresh artifacts, diagnose a broken invariant, and test a version boundary. Select one answer per multiple-choice question; numeric responses use the stated integer format. Use review links to revisit the relevant representation, then attempt the independent transfer task.