Overview
Single pass parser: consumes the lexer token stream and emits bytecode directly. No abstract syntax tree is built — each construct is parsed and emitted in one traversal, keeping memory usage minimal.Bytecode Model
EachInstruction carries an OpCode and a u16 operand. Constants are stored in Chunk.constants, names in Chunk.names. Both are referenced by index in the operand field.
| OpCode | Operand |
|---|---|
LoadConst | Index into constants |
LoadName | Index into names |
StoreName | Index into names |
Call | Argument count |
PopTop | — |
ReturnValue | — |
BuildString | Part count |
FormatValue | — |
Minus | — |
CallPrint | Argument count |
CallLen | Argument count |
CallAbs | Argument count |
CallStr | Argument count |
CallInt | Argument count |
CallRange | Always 3 |
Expression Parsing
expr() advances one token and dispatches on its kind. Every expression leaves exactly one value on the stack. Unrecognized tokens are silently skipped.
Supported expression types: Name, String, Int, Float, True, False, None, FstringStart, Minus.
Assignment and Type Annotations
Type annotations (name: type = value) are supported syntactically — the colon and type name are consumed and discarded. Only the value is emitted.
Built-in Dispatch
Built-in calls are resolved at parse time by name and emitted as dedicated opcodes, bypassing the generalCall path:
Range Normalization
range arguments are normalized at compile time. The VM always receives exactly 3 LoadConst values followed by CallRange 3, regardless of how many arguments were passed.
| Source | Emitted |
|---|---|
range(n) | 0, n, 1 |
range(a, b) | a, b, 1 |
range(a, b, c) | a, b, c |
F-String Interpolation
F-strings are parsed from theFstringStart → FstringMiddle → FstringEnd token sequence produced by the lexer. Each FstringMiddle is scanned for {name} expressions. Literal segments emit LoadConst, interpolated names emit LoadName + FormatValue. All parts are combined with BuildString N.
{name}. Expressions {1 + 2}, format specs {x:.2f}, and conversion flags {x!r} are not yet supported.
Integration Tests
Tests live inparser_test.rs and load cases from cases/parser_cases.json. Each case is a tuple of [source, expected_constants, expected_names, expected_instructions]. test_cases compiles each source and asserts constants, names, and bytecode sequence match exactly.