A full interpreter for the Lox programming language — this is my submission for the CodeCrafters “Build Your Own Interpreter” challenge.
) (
( /( )\ )
)\()) ( ( (()/( ) (
((_)\ ))\ )\ ) /(_)) ( ( /( )( (
__ ((_) /((_)(()/( (_)) )\ )\())(()\ )\
\ \ / /(_)) )(_))| | ((_)((_)\ ((_)((_)
\ V / / -_) | || || |__ / _ \\ \ / | '_|(_-<
|_| \___| \_, ||____|\___//_\_\ |_| /__/
|__/
A idiomatic Rust implementation of Lox from Robert Nystrom’s legendary book Crafting Interpreters, extended and sharpened through the CodeCrafters challenge.
This repository is my solution to the CodeCrafters — Build Your Own Interpreter challenge.
The challenge mirrors the journey in the book Crafting Interpreters and requires building Lox step-by-step:
- Tokenizer
- Parser (expressions + statements)
- AST
- Resolver (lexical scoping)
- Tree-walk Interpreter
- Classes, inheritance,
this,super - Closures, callables, bound methods
- Native functions (clock)
- Proper error handling & exit codes
This project became a (not so) complete, working interpreter for the Lox language.
This interpreter follows the structure of Crafting Interpreters by Robert Nystrom:
Before coding, the challenge recommends reading:
- Chapter 1 - Introduction
- Chapter 2 - A Map of the Territory
- Chapter 3 - The Lox Language
The implementation begins at Chapter 4 - Scanning.
- Variables, scopes
- Functions & closures
- Classes, objects, methods
- Inheritance &
super thisbinding- Logical ops, arithmetic, comparisons
- While, for-desugaring, if
- Return statements
- Error handling
Lox requires static resolution of variable references before interpretation.
The resolver tracks:
expr_id: lexical distance (# of scopes outward)
This ensures:
- Closures capture the right variable
- Late declarations don’t break earlier closures
this/superare used only in valid contexts- Shadowing works correctly
- Runtime avoids dynamic scope lookup bugs
Example:
print clock();
tokenize # print tokens
parse # print AST
evaluate # print value of single expression
run # run full program
src/
ast.rs # AST + runtime structures (Expr, Stmt, LiteralValue, classes, functions)
tokenizer.rs # Scanner / lexer
parser.rs # Pratt parser (expressions) + statement parser
resolver.rs # Lexical scope resolver (expr_id → distance)
interpreter.rs # Tree-walk interpreter + environments
natives.rs # Native functions (clock, etc.)
value.rs # Helpers for LiteralValue (truthiness, debug helpers)
error.rs # Error structs, utilities, exit-code helpers
debug_guard.rs # Debug-only utilities + debug! macro
main.rs # CLI entrypoint: tokenize / parse / evaluate / run
samples/
*.lox # Example Lox programs used for testing
scripts/
test-samples.sh # Test harness for running all sample programs
.github/
workflows/
ci.yml # GitHub Actions: build + test automation
var a = 46;
fun printAndModify() {
print a;
var a = 33;
print a;
}
print a;
a = 65;
printAndModify();
Output:
46
65
33
A simple test harness is included:
./scripts/test-samples.sh
You can add expected-output diffs as needed.
GitHub Actions CI runs:
cargo build
scripts/test-samples.sh
Here are some fun ideas/upgrades (which are and aren't in lox): I'll try to update
- Add arrays:
[1, 2, 3] - Add map/object literals:
{ "x": 10 } - Add a REPL with
rustyline - Add more built-ins (string ops, math ops)
- Add anonymous
lambdaexpressions - Add modules & imports
- Add for-each loops
- Improve error reporting with line/column info
- Build a bytecode compiler + VM (as in Crafting Interpreters Part III)
- Add type checking / type inference (Hindley-Milner or gradual types)
- Add a basic LSP (syntax highlighting + autocompletion for Lox)
- Build a WebAssembly port
- Add garbage collection (mark-sweep or arena-based)
- Build tests in rust