Design a small language and make it run. Start with a REPL that echoes what you type and end with one that executes programs with variables, functions, closures, arrays, and maps. Every token, node, and evaluation rule comes with a concrete spec, so the interpreter stays correct as it grows.
Over 48 lessons you build a working interpreter for a small dynamically-typed language, from scratch. You start with a REPL that echoes your input, then grow it one layer at a time: a lexer that turns source text into tokens, a Pratt parser that turns tokens into a syntax tree, and a tree-walking evaluator that runs that tree into real values.
By the end your REPL - and a runner that executes a source file - handles integers, booleans, strings and null; arithmetic and comparison with correct precedence; `let` bindings and assignment; `if`/`else` and `while`; first-class functions with closures and recursion; arrays and hash maps with an index operator; and a set of built-in functions. You will write higher-order functions like `map` in the language itself.
This is a teaching-grade interpreter: it walks the syntax tree directly and correctly, but stops short of what a production language runtime adds - a bytecode compiler and virtual machine, a static type system, garbage-collection tuning, floating-point numbers, and a module system. What you finish with is the honest core those systems are built on.
Every interpreter needs a way to feed it source and see a result. Today you build the read-eval-print loop that will be your window into the language for the entire project - for now it just echoes, but it is the runnable program everything else plugs into.
Build a prompt loop that reads a line of input and prints it straight back.
An interpreter is a pipeline - source text goes in one end, tokens become a tree, the tree becomes a value that comes out the other end. You will build that pipeline one stage at a time, but you need a way to drive it from day one. That driver is a REPL: read a line, evaluate it, print the result, loop.
Right now there is nothing to evaluate, so the “eval” step is just an echo. That is fine - the point today is a real, runnable program with a prompt you can type into. Every lesson from here thickens the middle of this loop, and you will run this same program to watch the language come alive.
// read lines until end-of-input, echo each onefor {fmt.Print(">> ")line, ok := readLine()if !ok { break } // Ctrl-D / EOF ends the loopfmt.Println(line)}
This is a working tree-walking interpreter with a REPL, a source-file runner, and a zero-setup demo, correctly executing the whole language the lessons build - but it stops at a teaching-grade core and skips conveniences (comments, else-if, digits in identifiers, line numbers in error messages) the lessons never reach.
The clearest book on building a language - a tree-walking interpreter then a bytecode VM. The scanning, parsing, and evaluation chapters map directly onto this project.
A compact, hands-on build of a lexer, Pratt parser, object system, and tree-walking evaluator for a small language - the closest single match to the arc you build here.
The sequel that takes the same language past a tree-walker into a bytecode compiler and stack-based virtual machine - the natural next step after this project.
The classic on evaluation, environments, and closures - chapter 4 builds a metacircular evaluator that explains why the environment model you use works.
The deep reference on lexical analysis, grammars, and parsing theory for when you want the formal treatment behind the hand-written lexer and parser.
The original 1973 paper describing the Pratt parsing technique used throughout the parsing chapter.