Projects/Build a Programming Language

Build a Programming Language

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.

48 lessonsMedium~20 min / lessonPratt parsingTree-walking evaluationClosures
The project

What you'll build over the next 48 lessons

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.

build-a-programming-language / lesson-01.md
Lesson 01 · Tokenizing

A REPL that echoes

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.

The goal

Build a prompt loop that reads a line of input and prints it straight back.

Start here - the target
TO DO
Scenario: The REPL echoes a line of input
Giventhe program is running and shows a prompt
Whenthe user types the line hello world and presses enter
Thenthe program prints hello world
Andit shows the prompt again, ready for the next line
Background

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.

Make it work
// read lines until end-of-input, echo each one
for {
fmt.Print(">> ")
line, ok := readLine()
if !ok { break } // Ctrl-D / EOF ends the loop
fmt.Println(line)
}
CheckpointDONE
You have a running program with a prompt that reads and prints lines. Commit and stop for today.
Scope & extensions

Where this project stops - and where to go next

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.

Extend it next
  • Track source line and column on every token so parser and runtime errors say where the problem is, not just what it is
  • Support else if chaining directly instead of requiring nested else { if (...) { } }
  • Allow identifiers to contain digits after the first character (x1, arr2) and add // line comments to the lexer
  • Give hashes a stable, sorted key order when printed instead of Go-style random map iteration
  • Add floating-point numbers so division stops truncating, and a null literal so absence is expressible directly
  • Compile the syntax tree to bytecode and run it on a virtual machine (the natural sequel to a tree-walker) for real speed
Recommended reading

Books & references that go deeper

  • Crafting Interpreters · Robert Nystrom

    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.

  • Writing a Compiler in Go · Thorsten Ball

    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.

  • Structure and Interpretation of Computer Programs · Harold Abelson, Gerald Jay Sussman

    The classic on evaluation, environments, and closures - chapter 4 builds a metacircular evaluator that explains why the environment model you use works.

  • Compilers: Principles, Techniques, and Tools (the Dragon Book) · Aho, Lam, Sethi, Ullman

    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.