Use when designing language features - covers lexer, parser, AST, and interpreter patterns
77
97%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
The interpreter's evaluate loop lives in SKILL.md. This is the Environment
class it depends on: a lexically-scoped chain where each scope holds its own
bindings and falls back to its parent for lookups.
class Environment {
private values = new Map<string, Value>();
private parent?: Environment;
constructor(parent?: Environment) {
this.parent = parent;
}
define(name: string, value: Value): void {
this.values.set(name, value);
}
get(name: string): Value {
if (this.values.has(name)) return this.values.get(name)!;
if (this.parent) return this.parent.get(name);
throw new RuntimeError(`Undefined variable: ${name}`);
}
// Assign to an existing binding, walking up the chain.
// Unlike `define`, this fails if the variable was never declared.
assign(name: string, value: Value): void {
if (this.values.has(name)) {
this.values.set(name, value);
return;
}
if (this.parent) return this.parent.assign(name, value);
throw new RuntimeError(`Undefined variable: ${name}`);
}
}Environment(parent) is created per block / function call; discard it on
exit so bindings don't leak.define always writes to the current scope (shadowing is intentional);
assign mutates the nearest enclosing binding.