Primary Sources

Ordered by when they become useful, not by importance. Read them alongside the section that needs them; reading them all up front is how they stop being useful.

Prefer the primary source. Almost every claim in this curriculum can be checked against one of these, and several of them are shorter than the blog post summarizing them.


Companion for the Whole Curriculum

  • Robert Nystrom, Crafting Interpreters — free at craftinginterpreters.com. Part II (jlox, a tree walker in Java) alongside Sections 1–2; Part III (clox, a bytecode VM in C) alongside Sections 3–4. The best second opinion available, and it makes different choices from Ember at several points — which is the value.

Sections 1–2: Front End and Semantics

  • Vaughan Pratt, Top Down Operator Precedence (POPL 1973) — the original Pratt parser. Short.
  • Aleksey Kladov ("matklad"), Simple but Powerful Pratt Parsing (2020) — the modern explanation, and the source of the (l_bp, r_bp) formulation Ember uses.
  • The Lua 5.4 Reference Manual — §3 (the language) and §9 (the complete grammar). Two pages of grammar, and Ember's is derived from it. Read §3.4.8 (precedence) verbatim.
  • lua/llex.c and lua/lparser.c — 500 and 2,000 lines for the whole front end and code generator. priority[] and subexpr are Ember's binding-power table and expr_bp, renamed.
  • PEP 657, Fine-grained Error Locations — Python adding column information after thirty years. The best available argument for storing spans rather than lines, with before/after examples.

Section 3: Bytecode and the VM

  • Ierusalimschy, de Figueiredo & Celes, The Implementation of Lua 5.0 (J. Universal Computer Science, 2005) — twelve pages: the register VM, the hybrid table, and closures, from the people who designed them. If you read one paper, read this one.
  • Shi, Casey, Ertl & Gregg, Virtual Machine Showdown: Stack Versus Registers (VEE 2005; extended in ACM TACO 2008) — a controlled comparison. Get the paper for the figures rather than trusting a remembered percentage.
  • Ertl & Gregg, The Structure and Performance of Efficient Interpreters (JILP 2003) and Optimizing Indirect Branch Prediction Accuracy in Virtual Machine Interpreters (PLDI 2003) — the primary sources on dispatch cost. Note the hardware they used; modern branch predictors have narrowed the gap they measured.
  • The JVM Specification, §4.10 (verification) — the most thorough treatment of "how do you trust bytecode" in existence.
  • The WebAssembly Core Specification, the "Validation" chapter, plus WebAssembly/design's Rationale.md — the same problem thirty years later, much more compactly, and unusually candid about tradeoffs.
  • lua/lopcodes.h — 83 opcodes with their semantics in the comment block; the model for Ember's opcode reference.
  • SQLite's src/vdbe.c and the VDBE documentation — a bytecode VM inside the most-deployed database on earth, written to be auditable. It will change your sense of how big a "real" system is.

Section 4: Objects, Heap, Collector

  • Jones, Hosking & Moss, The Garbage Collection Handbook (2nd ed.) — the reference. Chapters 1–3 cover Ember's collector; 15–16 cover what capstone project 3 builds.
  • Wilson, Uniprocessor Garbage Collection Techniques (1992) — the classic survey, free online, still the best single overview.
  • Dijkstra, Lamport, Martin, Scholten & Steffens, On-the-fly Garbage Collection (1978) — tri-color marking and the invariant a write barrier maintains.
  • Yuasa, Real-time Garbage Collection on General-Purpose Machines (1990) — the snapshot barrier.
  • Ungar, Generation Scavenging (1984) — generational collection and the weak generational hypothesis.
  • Cheney, A Nonrecursive List Compacting Algorithm (1970) — copying collection, in a page.
  • lua/lgc.c — a production incremental collector in ~1,200 lines, with both barrier flavors.
  • go/src/runtime/mgc.go and mbarrier.go — unusually well-commented for a concurrent collector; the barrier comment block is the best plain-English explanation available.
  • lua/ltable.c — rehash, computesizes, mainposition. The hybrid table and Brent's variation, in 800 lines.
  • lua/lfunc.c — luaF_findupval and luaF_close: open/closed upvalues, in about eighty lines.

Section 5: Embedding

  • The Lua 5.4 Reference Manual, chapter 4 (the C API). Read it as API design: every function operates on an explicit stack, and that is a rooting discipline made mandatory.
  • mlua's documentation — Lua, Value<'lua>, RegistryKey, and the send feature. Lifetime branding in production, with the escape hatch that shows where it became painful.
  • piccolo and gc-arena — the strongest alternative to Ember's handles: lifetimes making unrooted access a compile error. Read this one; comparing the two designs is the most educational hour after finishing the curriculum.
  • rhai — option A (owned values) in a Rust-native language, with good documentation of its safety limits and non-goals.
  • Klink & Wälde, Efficient Denial of Service Attacks on Web Application Platforms (28C3, 2011) — the hash-collision DoS disclosures. Why hash seeds exist.
  • The Firecracker and gVisor threat models — read them for structure and for how confidently they state what is not protected.

Sections 6–7: Production and Optimization

  • Deutsch & Schiffman, Efficient Implementation of the Smalltalk-80 System (POPL 1984) — inline caches, and the first dynamic translation.
  • Hölzle, Chambers & Ungar, Optimizing Dynamically-Typed OO Languages With Polymorphic Inline Caches (ECOOP 1991) — where mono/poly/megamorphic comes from.
  • Hölzle, Chambers & Ungar, Debugging Optimized Code with Dynamic Deoptimization (PLDI 1992) — the deoptimization paper. Short, and the most important reference in Section 7.
  • Gal, Probst & Franz, HotpathVM (VEE 2006) — trace compilation; the branch LuaJIT took.
  • PEP 659, Specializing Adaptive Interpreter — CPython's design, argued carefully, including why not just more static opcodes.
  • McKeeman, Differential Testing for Software (1998) — the technique, named.
  • Yang, Chen, Eide & Regehr, Finding and Understanding Bugs in C Compilers (PLDI 2011) — Csmith: 300+ bugs in GCC and LLVM by differential testing. The most persuasive case study for the technique.
  • SQLite's testing documentation (sqlite.org/testing.html) — read the fuzzing and index-cross-check sections; the second is metamorphic testing in production.
  • Cranelift documentation in the wasmtime repository, and cranelift-jit's examples.
  • LuaJIT's lj_record.c and lj_snap.c — trace recording and snapshots (LuaJIT's deopt maps). Mike Pall's mailing-list explanations are more informative than most published papers.
  • luajit/src/lj_obj.h — the canonical NaN-boxing implementation, with the bit layout explained.

Language Design, for Context

  • Ierusalimschy, de Figueiredo & Celes, The Evolution of Lua (HOPL III, 2007) — why Lua is the way it is, from Brazil's import restrictions onward. The best single explanation of a language's design constraints you will read.
  • Ierusalimschy, Programming in Lua — chapters 13 and 16 for the idiomatic patterns metatables enable.
  • Moura, Rodriguez & Ierusalimschy, Coroutines in Lua — the design rationale, including why asymmetric.
  • The Go 1.22 release notes on the loop variable — a language changing a fifteen-year-old semantic because the old one caused real bugs. The best case study in closure-capture decisions judged in retrospect.

How to Read Source Trees

git clone https://github.com/lua/lua        # ~30k lines; read lobject.h FIRST
git clone https://luajit.org/git/luajit.git # lj_obj.h, lj_record.c
git clone https://github.com/python/cpython # Python/ceval.c, Objects/gcmodule.c
git clone https://github.com/koto-lang/koto # small enough to read in a weekend
git clone https://github.com/kyren/piccolo  # the GC alternative to yours

Read lua/lobject.h first, always. In one 700-line header you get the value representation, the string object, the table, the closure, and the upvalue. It makes everything else in the tree legible, and it is the densest, highest-value reading in this entire list.


Back to: the Appendix index — or the Introduction, which will read differently now.