cp-03 — MiniLang Frontend (Statements, Variables, Control Flow, Functions)
Status: ✅ Built — 34/34 tests pass.
Replaces the arithmetic grammar from cp-02 with a real language:
let/varbindings,if/while, functions, blocks, closures. Switches the parser from hand-rolled recursive descent to a Pratt parser for expressions.
What You'll Build
- A full Pratt expression parser driven by a binding-power table.
- A recursive-descent statement parser:
let/var, blocks,if/else,while,return,print,fn. - Two-tier AST (
Stmt+ templated-returnExpr<R>visitors). - Scope-chain
Environment(parent-linked maps) and lexical closures. - A tree-walking interpreter with first-class functions, recursion, higher-order calls.
- A
mliREPL and file runner.
Reading Order
- CONCEPTS.md — Pratt parsing, binding powers, statement/expression split, closures.
- src/cpp/steps/ — seven guided steps (tokens → lexer → AST → Pratt → environment/interpreter → functions → REPL+tests).
- src/cpp/src/ — the full source.
Prereqs
- cp-02 complete (recursive descent + AST + Visitor internalized).
Outcomes
- Hand-write a Pratt parser for any precedence-rich expression language.
- Understand the statement/expression distinction and where each fits in the AST.
- Implement lexical scope via a parent-linked environment chain.
- Build first-class functions and closures using shared environments.
Build & Run
cd src/cpp
cmake -S . -B build && cmake --build build -j
ctest --test-dir build --output-on-failure
./build/mli # REPL
./build/mli script.ml # run a file
Sample
fn fact(n) { if (n <= 1) return 1; return n * fact(n - 1); }
fn fib(n) { if (n < 2) return n; return fib(n-1) + fib(n-2); }
print fact(10); // 3628800
print fib(15); // 610
fn makeAdder(a) { fn add(b) { return a + b; } return add; }
let plus5 = makeAdder(5);
print plus5(10); // 15