★★★★★

Crafting Interpreters

by Robert Nystrom

A science-museum book report. Don't read — play. Each exhibit lets you experience language implementation from the inside.

Reviewed by 🐑 · February 2026

📚 Buy on Bookshop
↓ scroll to explore

Exhibit 1 · The Pipeline

Every language implementation follows the same path: raw text in, running program out. Type code and watch it flow through every stage.

Live Pipeline
📝 Source
🔍 Scan
🌳 Parse
⚙️ Compile
🖥️ Execute
"The pipeline is a mountain—you climb up from raw text to high-level semantic understanding, then descend back down to machine-executable form."— Chapter 2: A Map of the Territory

Exhibit 2 · The Pratt Parser

This is the hidden gem of the book. Each token type has a prefix function, an infix function, and a precedence level. Watch the parser decide what to do at each step:

Pratt Parser Visualizer
"Pratt parsing is the hidden gem of this book. It's so much more elegant than recursive descent for expressions."— Book Notes, Ch. 17

Exhibit 3 · Closures & Upvalues

When a function captures a variable from an outer scope, it creates an upvalue — a reference to that variable. When the outer function returns, the upvalue "closes over," moving the value from the stack to the heap.

Upvalue Simulator
"The upvalue mechanism is brilliant. It's one of those solutions that seems obvious in hindsight but required real insight to invent."— Book Notes, on Lua's contribution (Ch. 25)

Exhibit 4 · NaN Boxing

IEEE 754 has billions of unused NaN bit patterns. Lox stuffs nil, booleans, and pointers into those bits — halving memory usage. Toggle individual bits to see what happens:

64-Bit Explorer
"NaN boxing is one of the coolest bit-twiddling tricks in all of computer science. The way it exploits IEEE 754's structure to pack a dynamically-typed value into exactly 64 bits is beautiful."— Book Notes, Ch. 30

Exhibit 5 · Garbage Collection

Create objects, make references, break them, then trigger GC and watch tricolor mark-and-sweep in action. White = unmarked. Gray = discovered. Black = fully traced. White after marking = garbage.

Mini Heap Simulator
Click objects to select. Roots are pinned (📌).
"GC bugs are among the most pernicious in language implementation. They're non-deterministic, depend on allocation patterns, and can corrupt memory silently."— Book Notes, Ch. 26

The Journey: All 30 Chapters

The book builds two complete interpreters for Lox — a tree-walk interpreter in Java and a bytecode VM in C.

Part I — Welcome
Chapter 1
Introduction
Dismantling the myth that compilers are wizardry. "It's just code."
Chapter 2
A Map of the Territory
The full pipeline: scanning → parsing → analysis → IR → codegen → runtime.
Chapter 3
The Lox Language
Meet Lox: dynamic typing, closures, classes, and a standard library of… two functions.
Part II — Tree-Walk Interpreter (jlox, Java)
Chapter 4
Scanning
Turning raw characters into tokens. The "lexical analygator."
Chapter 5
Representing Code
Context-free grammars, ASTs, and the Visitor pattern.
Chapter 6
Parsing Expressions
Recursive descent: each grammar rule becomes a function.
Chapter 7
Evaluating Expressions
Walking the tree, computing values. The first "1+2=3" moment.
Chapter 8
Statements and State
Variables, environments, scope chains.
Chapter 9
Control Flow
if/else, while, for. Desugaring for-loops into while-loops.
Chapter 10
Functions
Calls, declarations, closures, return via exceptions.
Chapter 11
Resolving and Binding
The resolver pass. Fixing a subtle closure scoping bug.
Chapter 12
Classes
Classes as first-class objects. Methods, this, init().
Chapter 13
Inheritance
Single inheritance with super. Method resolution chains.
Part III — Bytecode Virtual Machine (clox, C)
Chapter 14
Chunks of Bytecode
Dense, linear instruction encoding. Run-length line info.
Chapter 15
A Virtual Machine
The stack-based VM. Push, pop, compute.
Chapter 16
Scanning on Demand
Rewriting the scanner in C, character by character.
Chapter 17
Compiling Expressions
Pratt parsing! The elegant single-pass expression compiler.
Chapter 18
Types of Values
Tagged unions: type enum + value union.
Chapter 19
Strings
Heap-allocated strings with interning for O(1) equality.
Chapter 20
Hash Tables
Open addressing, linear probing, FNV-1a, tombstones.
Chapter 21
Global Variables
Globals stored by name in a hash table.
Chapter 22
Local Variables
Locals on the stack. Compile-time slot resolution. O(1) lookups.
Chapter 23
Jumping Back and Forth
Control flow as jump instructions. Backpatching.
Chapter 24
Calls and Functions
Call frames, the call stack, native functions.
Chapter 25
Closures
Upvalues: references to enclosing stack slots that migrate to heap.
Chapter 26
Garbage Collection
Mark-and-sweep with tricolor marking. Stress-test mode.
Chapter 27
Classes and Instances
Classes as runtime objects. Instance fields in hash tables.
Chapter 28
Methods and Initializers
Method dispatch, bound methods, init() returns this.
Chapter 29
Superclasses
Inheritance copies methods. OP_GET_SUPER for super calls.
Chapter 30
Optimization
Hash caching, NaN boxing. ~10% faster for free.

What Nystrom Got Right

The book was published in 2021. What has aged well?

Pratt Parsing Everywhere

Nystrom championed Pratt parsing when most textbooks ignored it. Today it's the go-to technique in Rust's compiler, TypeScript tooling, and virtually every new language project. The book helped make it mainstream.

Bytecode VMs as the Default

Tree-walk interpreters remain teaching tools; real-world languages (Python 3.11+, Ruby YJIT, Lua) doubled down on bytecode VMs with JIT layers—exactly the trajectory the book's structure implies.

"Little Languages" Boom

Chapter 1 predicted that embedded DSLs would proliferate. In 2024-2026 we've seen config languages (CUE, Pkl, Nickel), query languages (Malloy, PRQL), and AI prompt templating languages everywhere.

NaN Boxing Lives On

JavaScriptCore, LuaJIT, and newer runtimes continue to use NaN boxing. The trick Nystrom dedicated Ch. 30 to remains the state of the art for dynamic value representation.

🤔

GC vs. Ownership

The book presents garbage collection as the natural choice. Rust's ownership model and languages like Vale/Mojo suggest alternatives are gaining traction—though GC remains dominant for scripting languages.

🔮

AI Writing Interpreters?

Nystrom's thesis—"it's just code"—proved more prophetic than expected. LLMs can now scaffold a basic interpreter from a grammar spec. The book's educational model (understand deeply, then build) is more valuable than ever as a counterweight.

The Verdict

★★★★★

A Masterpiece of Technical Writing

Crafting Interpreters manages to be simultaneously rigorous and accessible, comprehensive and readable, theoretical and practical. It's the rare programming book that you can read cover-to-cover like a novel AND use as a reference for years afterward.

The book's central thesis—that compilers aren't magic, they're just code—is proven conclusively across 800 pages and two complete implementations. Nystrom writes with warmth, humor, and deep respect for the reader's intelligence.

Best chapters: Ch. 6 (Parsing Expressions), Ch. 11 (Resolving and Binding), Ch. 17 (Pratt Parsing), Ch. 26 (Garbage Collection), Ch. 30 (NaN Boxing)

Who should read it: Anyone who writes code for a living. Not because you'll necessarily build a language, but because you'll understand, at a visceral level, how the tools you use every day actually work. And that understanding changes how you think.

"My hope is that if you've felt intimidated by languages, and this book helps you overcome that fear, maybe I'll leave you just a tiny bit braver than you were before."— Robert Nystrom, closing words
"Every introduction to every compiler book seems to have this section. I don't think ornithology books worry about justifying their existence. They assume the reader loves birds and start teaching."— Robert Nystrom, Chapter 1