A long-form, chapter-by-chapter walk through Compilers: Principles, Techniques, and Tools (Aho, Lam, Sethi, Ullman — 2nd edition), implemented in Rust as a single Cargo workspace.
Each chapter that lends itself to construction gets its own crate — a self-contained scaffold of provided types, fixed test suite, and numbered TODOs that the user fills in. There are no solutions in the repo and no encoded hints. The pedagogy is pain → theory → construction: every chapter starts with a tiny script that makes you experience the problem the chapter is solving, then a reading guide that maps book sections to modules, then a TODO-scaffolded crate with a test suite that turns green progressively as you implement.
| Ch. | Title | Crate | Status |
|---|---|---|---|
| 1 | Introduction | — (background) | n/a |
| 2 | A Simple Syntax-Directed Translator | chapter-2-tinylang/ |
in progress |
| 3 | Lexical Analysis | chapter-3-lexer/ |
planned |
| 4 | Syntax Analysis | chapter-4-parser/ |
planned |
| 5 | Syntax-Directed Translation | (folded into Ch. 4 crate) | planned |
| 6 | Intermediate-Code Generation | chapter-6-irgen/ |
planned |
| 7 | Run-Time Environments | chapter-7-runtime/ |
planned |
| 8 | Code Generation | chapter-8-codegen/ |
planned |
| 9 | Machine-Independent Optimizations | chapter-9-opt/ |
maybe |
| 10 | Instruction-Level Parallelism | — | reading only |
| 11 | Optimizing for Parallelism and Locality | — | reading only |
| 12 | Interprocedural Analysis | — | reading only |
The later chapters (10–12) lean theoretical and aren't great construction targets at this scale. The construction-friendly arc is 2 → 3 → 4 → 6 → 7 → 8, which is the path most undergraduate compilers courses follow.
dragon-book/
├── Cargo.toml ← workspace manifest
├── README.md ← you are here
├── assets/banner.svg
├── chapter-2-tinylang/ ← block-scoped expression language + VM
│ ├── Cargo.toml
│ ├── README.md
│ ├── reading_guide.md
│ ├── grammar.md
│ ├── src/ ← lexer, parser, symtab, codegen, vm
│ ├── tests/
│ └── examples/feel_the_problem.rs
└── (future chapter crates)
# Run every chapter's tests in one go.
cargo test --workspace
# Or just one chapter.
cargo test -p tinylang
# Run a chapter's "feel the problem" demo (Ch. 2):
cargo run -p tinylang --example feel_the_problemAdding a new chapter is a one-liner in the root Cargo.toml — uncomment the
member entry once the directory exists, and cargo test --workspace picks
it up automatically.
- One git history for the whole journey through the book — easier to see how my understanding evolves chapter to chapter.
- Shared
target/at the workspace root means common dependencies build once, not 10 times. - Cross-chapter reuse stays open. Chapter 4's parser can depend on
Chapter 3's lexer crate via
path = "../chapter-3-lexer"if I want it to. - One
cargo test --workspaceruns everything — a regression in a chapter is caught immediately.
- Feel the problem —
cargo run --example feel_the_problemfrom the chapter directory. A deliberately broken or naive implementation that fails in pedagogically-loaded ways. Run it before reading. - Read the chapter —
reading_guide.mdin each chapter maps Phase-1 failures to book sections, then book sections to modules, and tells you what to skim vs. study carefully. - Build — work through numbered TODOs in order. Tests run from
day one (every TODO panics with
todo!()so the test infrastructure works before you write anything). Each TODO unlocks a labeled slice of the test suite.
- Skeletons have zero solutions and zero encoded hints. Every TODO has a what (the goal, what the function should do, which tests it unlocks) and never a how (the implementation steps).
- Public types, function signatures, and tests are fixed. Only function bodies get filled in.
- Modifying a test = cheating. (Adding new tests is fine.)
- The reading is the point. Skipping the chapter and trying to brute-force the TODOs is faster but produces no understanding.
MIT.