Interpreter
The Forge interpreter is a tree-walking interpreter implemented in Rust. It traverses the AST directly, evaluating each node as it encounters it. As of v0.7.0, the bytecode VM is the default engine; the interpreter is available via the --interp flag and serves as the fallback for features that require it (HTTP server decorators, DAP debugger integration). It remains the most complete execution engine.
Architecture
Source (.fg) -> Lexer -> Parser -> AST -> Interpreter -> Result
|
Environment (scopes)
|
Runtime Bridge
(axum, reqwest, tokio, rusqlite)
The interpreter lives in src/interpreter/mod.rs (~8,100 lines) and is the largest single file in the codebase.
Key Components
Environment
The interpreter maintains a stack of scopes. Each scope is an IndexMap<String, Value> that maps names to values. Variable resolution walks the scope stack from innermost to outermost.
- Global scope: Pre-populated with all 16 stdlib modules and all built-in functions.
- Function scope: Created on each function call, closed over by lambdas.
- Block scope: Created for
if,for,while, and other block statements.
Value Type
The Value enum represents all runtime values:
Int(i64)– 64-bit integerFloat(f64)– 64-bit floatBool(bool)– booleanString(String)– heap-allocated stringArray(Vec<Value>)– dynamic arrayObject(IndexMap<String, Value>)– ordered key-value mapNull– null valueFunction { params, body, closure }– named function with captured environmentLambda { params, body, closure }– anonymous functionBuiltIn(String)– reference to a built-in function by nameResultOk(Box<Value>)/ResultErr(Box<Value>)– Result typeSome(Box<Value>)/None– Option typeChannel(Arc<ChannelInner>)– concurrency channelTaskHandle(Arc<TaskInner>)– async task handle
Dispatch
Built-in function dispatch is a single large match statement in call_builtin. When a BuiltIn("name") value is called, the interpreter matches on the name string and executes the corresponding Rust code.
Stdlib module functions (e.g., math.sqrt) are dispatched through the module’s call function. The interpreter detects dot-access on a module object and routes the call to the appropriate module.
Features Unique to the Interpreter
The following features are only available in the interpreter tier:
- HTTP server (
@server,@get,@post,@delete,@ws) - Database access (
db.open,db.query,pg.connect) - AI integration (
ask) - Web scraping (
crawl) - File download (
download ... to) - Terminal UI widgets (
term.table,term.menu,term.confirm) - GenZ debug kit (
sus,bruh,bet,no_cap,ick) - Execution helpers (
cook,yolo,ghost,slay) - Concurrency (
channel,send,receive,spawn)
Performance Characteristics
The tree-walking approach means the interpreter re-traverses the AST on every loop iteration and function call. This makes it approximately 20x slower than Python for deep recursion benchmarks like fib(35).
For most real-world scripts (file processing, HTTP handlers, database queries), interpreter overhead is negligible compared to I/O latency. While the VM is now the default for general-purpose work, the interpreter remains the recommended tier for HTTP server applications and debugger sessions.