Write a tree-walking interpreter for your own dynamic language, Sprout, in Rust — scanner, parser, and evaluator — until it runs recursive Fibonacci and real closures. The best possible tour of Rust's enums, pattern matching, and ownership.
Every program you write is read by an interpreter or compiler — yet for most developers that machinery is pure magic. In this project you pull back the curtain by building a working programming language, Sprout, entirely from scratch in Rust. No parser generators, no libraries: just the standard library and the three classic stages every language is made of.
You'll start with a scanner that turns source text into tokens, build a recursive-descent parser that assembles those tokens into an Abstract Syntax Tree with correct operator precedence, then write a tree-walking evaluator that executes the tree. From there you add variables and lexical scope, control flow (if, while, blocks), and finally first-class functions with return and closures — capping it off by running a recursive Fibonacci and a stateful counter that captures its environment.
There is no better project for learning what makes Rust Rust. An interpreter is built out of exactly the features Rust is famous for: algebraic enums model tokens, AST nodes, and runtime values; exhaustive match walks every variant with the compiler guaranteeing you missed nothing; Box<Expr> gives recursive types a known size; Result<T, SprError> and the ? operator thread errors cleanly through every stage; and the capstone — closures that outlive the call that created them — is implemented with Rc<RefCell<Environment>>, the canonical Rust pattern for shared, mutable state. Every challenge is graded by running real cargo test suites, so the moment your language behaves correctly, you'll know.