builds / interpreter / stage-4SHEET 4 / 6 · REV ASIGN IN
ASSEMBLY DIAGRAM — YOUR INTERPRETERSCALE: LEARNING
source filethe outside worldSCANNER✓ builtPARSER✓ builtEVALUATOR✓ builtENV ROOMS⚙ buildingCLOSURE CELLSstage 5DIAG PANELstage 6
BUILTUNDER CONSTRUCTIONNOT YET IMAGINED INTO EXISTENCE
STAGE 4 · THE ENVIRONMENT ROOMS

Names, Bindings, and Rooms

let x = 5 is a promise the machine has to keep — but only in here.

What you're wiring up

An environment is a map from names to values plus one pointer to the enclosing environment. Lookup walks outward, room by room, until it finds the name or runs out of rooms. That single linked-map idea is the entire theory of variable scope, and building it makes shadowing and undefined-variable errors obvious instead of mystical.

Statements arrive with it. A program stops being one expression and becomes a sequence: let statements, return statements, expression statements, and blocks that are statement lists of their own. Each block gets its own room, so an inner let shadows the outer binding and vanishes at the closing brace.

return needs to punch up through nested blocks. The trick is to wrap the value in a special return object that statement-list evaluation stops on and passes upward untouched, unwrapping only at the program or function boundary.

Assembly steps

[ 01 ]
Extend the parser and AST with statements: let, return, expression statements, blocks as statement lists, and a Program that is a statement list.
[ 02 ]
Build Environment with Get and Set over a map, plus NewEnclosed(outer) where Get falls through to the outer room on a miss.
[ 03 ]
Thread the environment through Eval(node, env): let stores, identifiers look up, and an unknown identifier produces an error value naming the identifier.
hint

Resist a global environment variable. Passing env explicitly is exactly what makes closures nearly free next stage.

[ 04 ]
Give each block its own enclosed environment so inner bindings shadow outer ones and disappear when the block ends.
hint

Type let x = 1; if (true) { let x = 2; } x into the REPL. If it prints 1, your rooms work.

[ 05 ]
Implement return: evaluating one produces a wrapper that halts statement evaluation and is unwrapped only at the function or program boundary.
hint

The classic bug is unwrapping too early. if (true) { if (true) { return 10; } return 1; } must be 10 — it is a shipped golden.

Go deeper (after it passes)

Environment chains and the parser's token cursor are both state made explicit rather than hidden in flags — pair with HFDP M9 — The Gumball State Machine. Then read your own Get method again: that fall-through is what every scoping rule in every language you use reduces to.