builds / interpreter / stage-6SHEET 6 / 6 · REV ASIGN IN
ASSEMBLY DIAGRAM — YOUR INTERPRETERSCALE: LEARNING
source filethe outside worldSCANNER✓ builtPARSER✓ builtEVALUATOR✓ builtENV ROOMS✓ builtCLOSURE CELLS✓ builtDIAG PANEL⚙ building
BUILTUNDER CONSTRUCTIONNOT YET IMAGINED INTO EXISTENCE
STAGE 6 · THE DIAGNOSTICS PANEL

Errors Worth Reading

The difference between a toy and a tool is what happens when the input is wrong.

What you're wiring up

Right now a bad program dies with whatever string was handy. This stage makes errors first-class. Every diagnostic carries the line and column you stamped onto tokens back in Stage 1, prints the offending source line, and points a caret at the exact column.

The parser learns to recover: on an unexpected token it records a diagnostic, skips forward to a statement boundary, and keeps going — so one typo stops hiding the next three. This is panic-mode plus synchronize, two small functions, and real compilers mostly do not do anything cleverer.

Runtime errors get the same treatment plus a call trace: the evaluator keeps a stack of active call positions and prints frames innermost-first. This is the stage that explains why compiler engineers obsess over spans, and it is pure plumbing you already laid — positions flow token to node to value, and now you finally spend them.

# blu run play.blu   (stderr)
play.blu:3:11: type mismatch: INTEGER + BOOLEAN
  let z = 5 + true;
            ^
  in fib at play.blu:7:3
  in main at play.blu:12:1

Assembly steps

[ 01 ]
Build a Diagnostic type — severity, message, position — plus a renderer that prints file:line:col, the source line, and a caret underneath.
hint

Keep the source in memory keyed by line. The lexer already counts lines, so slicing the right one is a map lookup, not a re-read.

[ 02 ]
Add parser recovery: record the diagnostic, skip tokens until a semicolon or closing brace, then continue parsing.
hint

Two functions total. Resist anything smarter — you will just hide errors differently.

[ 03 ]
Upgrade runtime errors to carry the position of the AST node being evaluated: type mismatch, identifier not found, not a function — each with file:line:col and a caret.
[ 04 ]
Add a call trace: push a frame on call, pop on return, and print frames innermost-first when a runtime error escapes.
hint

defer makes the pop unforgettable.

hint

Cap the printed trace at ten frames so infinite recursion stays readable.

[ 05 ]
Make exit codes contractual: 0 clean, 1 for any diagnostic, and the REPL prints diagnostics without ever exiting.

Go deeper (after it passes)

The blueprint is complete — solid ink. Pair this stage with Clean Code M5 — Errors Without the Clutter, which is exactly what you just built for real. Victory lap: write something real in blu. Then take the lexer and parser straight into build-your-own-database, whose SQL front end is these two units reapplied.