Skip to content

Step 1 of 3 · Reading · ~3 min

Variables — Naming Values

Statements & State

Giving the Language Memory

Every statement you've implemented so far is stateless — each one evaluates and moves on, with no memory of what came before. Variables change that: var x = 10; needs to be remembered so that a later statement, print x;, can look it up. This lesson adds the first piece of mutable interpreter state: a place to store variable bindings.

New Grammar: Declarations vs. Statements

program     → declaration* EOF
declaration → varDecl | statement
varDecl     → "var" IDENTIFIER ( "=" expression )? ";"

Note the new top-level rule name: declaration. This distinction (declarations vs. plain statements) exists because var is special — it's only legal in certain positions in more complex grammars (not inside an if without braces, for instance). For now, treat it as one more thing statement-level dispatch checks for, alongside print.

The initializer is optional: var y; is valid and gives y the value nil, while var x = 10; gives it 10 immediately.

The Environment: A Map With a Parent

The runtime home for variables is an Environment — conceptually just a dictionary from name to value:

python

Two operations, two different rules — this asymmetry is easy to overlook but important:

  • define (from var x = ...) always succeeds, even if x already exists — Lox-style languages allow re-declaring a variable with var in the same scope, which is intentionally more permissive than most statically-typed languages.
  • assign (from x = ... with no var) requires the variable to already exist. Assigning to a name that was never declared is a runtime error — this is what lets the interpreter catch typos like undeclared_var = 5; instead of silently creating a new global.

Two New Expression/Statement Node Types

python

Notice Assign is an expression, not a statement — x = 20 itself evaluates to 20, which is why chained assignment (a = b = 5) and things like print x = 5; are meaningful in C-family languages. Parsing assignment correctly requires special handling at the top of the expression grammar (above equality), because x = 5 isn't a binary operation in the usual sense — the left side must be validated as a valid assignment target (a Variable node) after the fact, not parsed as one.

The Error You Must Get Right

print y; where y was never declared must produce exactly: Undefined variable 'y'. — not a Python KeyError, not a crash. This is the first case where the interpreter needs to catch its own internal runtime errors, print a clean message, and terminate that statement's evaluation without corrupting the rest of the program's state.

Up nextScope & EnvironmentsStatements & State

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…