cp-02 — Arithmetic Evaluator

The first real compiler. Read source text. Produce a number. Use every classical frontend technique compressed into ~400 lines of C++17.

Why This Lab

This is the foundational lab of the entire curriculum. Almost every concept in the rest of the course is a generalization of something built here:

Built hereGeneralizes to (later)
Hand-written DFA lexerClang's lexer; Phase 3 bytecode-compiler lexer
Recursive-descent parserPratt parser (cp-03); Clang's parser
AST + Visitor patternEvery IR in every later lab
Post-order tree-walk evalBytecode VM (cp-06); LLVM IRBuilder traversal
EBNF grammarAll MiniLang grammar throughout the curriculum
Operator precedence via grammar nestingPratt's "binding powers" (cp-03); MLIR op verifiers

Read First

  • CONCEPTS.md — the 8-part deep dive: lexers, parsers, AST, EBNF, precedence, associativity, the Visitor pattern.
  • references.md — Crafting Interpreters chapters, Clang lexer source, V8 parser source.
  • docs/analysis.md — design tradeoffs (DFA vs regex, recursive descent vs Pratt vs LR, Visitor vs std::variant).

Walk The Steps

  1. steps/01-the-lexer.md — tokens, DFA, single-char lookahead.
  2. steps/02-the-ast.md — node hierarchy, ownership, the Visitor pattern.
  3. steps/03-recursive-descent-parser.md — grammar, precedence-via-nesting, associativity-via-recursion-direction.
  4. steps/04-the-evaluator.md — post-order tree walk implemented as a Visitor returning double.
  5. steps/05-repl-tests-and-cli.md — wire it into a usable tool with a test suite.
  6. steps/06-extensions.md — extension exercises (right-associative ^, AST printer, source-location errors, constant folding).

Lab Docs

Code

  • src/cpp/ — full reference implementation (CMake project, ~450 lines of C++).

Build & Run (TL;DR)

cd src/cpp
cmake -B build && cmake --build build
./build/eval "1 + 2 * 3"      # 7
./build/eval                  # REPL
ctest --test-dir build        # 19 tests

Outcomes

You leave this lab able to:

  • Hand-write a lexer for an arbitrary regular language (no lex / flex).
  • Write a recursive-descent parser for any LL(1) grammar.
  • Design an AST with the Visitor pattern.
  • Explain why the grammar shape determines operator precedence and associativity.
  • Recognize what changes (and what doesn't) when you swap a tree-walker for a bytecode VM or LLVM backend in later labs.