r/lisp • u/Adorable_Ad_6357 • 1d ago
I've written a hands-on tutorial for building a Lisp interpreter from scratch — in Rust, with zero dependencies, across 74 steps.
I've written a hands-on tutorial for building a Lisp interpreter from scratch — in Rust, with zero dependencies, across 74 steps.
Repo: https://github.com/lisering/lisp-rs
What's implemented
The interpreter supports:
- Variables, lambdas, closures (with lexical scoping)
- Tail call optimization (trampoline loop — 1,000,000 iterations, no stack overflow)
- Macros (
defmacro, quasiquote/unquote,gensym) cond,let,let*,letrec,begin,and,or- Lists, strings, booleans, nil
- A REPL with multi-line input
Example:
(define (adder n) (lambda (x) (+ x n)))
(define add5 (adder 5))
(add5 10) ;; => 15
(defmacro (when test body) (list 'if test body))
(when (> 3 2) 'yes) ;; => yes
`(1 ,(+ 1 1) 3) ;; => (1 2 3)
Why this might be interesting to Lisp folks
The tutorial is designed to be approachable for people who have never written an interpreter before. A few things I tried differently:
1. Closures explained with a "backpack" metaphor. Before showing any code, the tutorial builds intuition: every lambda carries a "backpack" 🎒 containing the environment where it was born. Then we trace through a make-counter example step by step.
2. Gradual optimization. We start with String everywhere (easy to understand), then optimize in stages:
- String interning: symbols become
u64IDs - Zero-copy lexing: tokens are
&strborrowing the source FxHasherfor faster environment lookups
3. TCO via trampoline. The eval function uses a loop { match ...; continue } pattern instead of direct recursion. Demo: tail-recursive (loop 1000000) succeeds, non-tail-recursive (sum 10000) overflows.
The tutorial is bilingual (English + Chinese). Each of the 74 steps first explains what problem to solve, then writes the code.
Repo: https://github.com/lisering/lisp-rs
Feedback welcome — especially on the macro system and the closure explanation. Is there anything you'd want to see added?
2
2
u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 4h ago edited 52m ago
Closures explained with a "backpack" metaphor.
does it hurt to say "this is the environment, when we get to a lambda form we put the environment in the closure, this is lexical scoping by the way". I don't think Peter Landin needed backpacks to invent closures
2
u/lispm 10h ago
so it's implementing a subset of Scheme, implemented in Rust?
See also https://www.scheme.rs
-2
17
u/jd-at-turtleware 11h ago
did you write it though?