Strada Interpreter

A bytecode VM that compiles Strada's AST to bytecode and executes it via a computed goto dispatch loop. 4-5x faster than Perl 5.38.

Opt-in build: strada-interp is no longer built by default — run make interpreter in the repo to build it. The everyday strada --repl and strada --script flows now route through strada-jit (real native compilation via tcc/gcc + dlopen) so they support the full Strada language with no semantic gaps. The interpreter described on this page is still useful for embedding, experimentation, and anywhere you genuinely want bytecode execution without invoking gcc.
No C compiler needed at runtime The interpreter compiles Strada code to bytecode and runs it on the VM. No gcc or tcc required (except for __C__ blocks, which are JIT-compiled and cached). A tree-walking backend is available as a fallback via --tree-walk.

Overview

Strada offers three execution paths for your code:

Compiled (stradac) VM (strada-interp) Tree-Walk (--tree-walk)
How it works Generates C code, compiles to native binary AST → bytecode → VM dispatch Walks the AST directly at runtime
Requires C compiler Yes (gcc) No (except __C__ blocks) No
Startup time Slow (compilation step) Fast (parse + bytecode compile) Fast (parse and run)
Runtime speed Fastest (native code) Fast (4-5x faster than Perl 5.38) Slower (interpreted)
Best for Production, performance Scripts, file execution REPL, legacy fallback

All paths share the same Lexer and Parser front-end, so valid Strada code works with any of them.

Running Programs

# Build the interpreter
make interpreter

# Run a Strada program (uses bytecode VM by default)
./strada-interp program.strada

# Use tree-walking backend instead
./strada-interp --tree-walk program.strada

# With library search paths
./strada-interp -L lib program.strada

# Start the interactive REPL (uses tree-walk)
./strada-interp

Interactive REPL

When run without arguments, the interpreter starts an interactive REPL with readline support (line editing, history):

strada> my int $x = 42;
strada> $x * 2
=> 84

strada> func square(int $n) int {
...        return $n * $n;
...    }
strada> square(7)
=> 49

Variables and function definitions persist across evaluations. Multi-line input is detected automatically when braces, parentheses, or brackets are unbalanced.

See REPL & Scripting for REPL commands, scripting, and profiling features.

Embedded Eval

The interpreter can be embedded inside compiled Strada programs using the Strada::Interpreter library. This enables runtime code evaluation without needing a C compiler at runtime:

use lib "lib";
use Strada::Interpreter;

# Initialize the interpreter engine
Strada::Interpreter::init();

# Evaluate expressions
my scalar $result = Strada::Interpreter::eval_string("1 + 2");
say($result);  # 3

# Variables persist across calls
Strada::Interpreter::eval_string("my int \$x = 10;");
my scalar $val = Strada::Interpreter::eval_string("\$x * 5");
say($val);  # 50

# Function definitions persist
Strada::Interpreter::eval_string("func double(int \$n) int { return \$n * 2; }");
say(Strada::Interpreter::eval_string("double(21)"));  # 42

# Reset all state
Strada::Interpreter::reset();

Eval API

Function Description
Strada::Interpreter::init() Initialize the shared interpreter instance (auto-called by eval_string if needed)
Strada::Interpreter::eval_string($code) Evaluate Strada code, return the result (undef for void statements)
Strada::Interpreter::reset() Clear all persisted state (variables, functions, enums, constants)

Interpreter vs JIT

Strada also provides Strada::JIT, which evaluates code by compiling to a shared library at runtime. The two approaches serve different needs:

Strada::Interpreter Strada::JIT
Execution Bytecode VM (default) or tree-walk Compile to .so, dlopen, execute
Requires C compiler No Yes (gcc or tcc)
__C__ blocks JIT-compiled and cached Supported
Async/await No Yes
Per-eval overhead Low (parse + bytecode compile) Higher (compile + link + load)
Execution speed Fast (bytecode VM) Fastest (native code)
Best for REPL, scripting, no-compiler envs Plugin systems, perf-sensitive eval

Architecture

The interpreter reuses the compiler's Lexer and Parser front-end. By default, the AST is compiled to bytecode and executed on a register-based VM. A tree-walking backend is available as a fallback via --tree-walk.

Component Source Purpose
Lexer compiler/Lexer.strada Tokenizes source (shared with compiler)
Parser compiler/Parser.strada Builds AST (shared with compiler)
VM Compiler interpreter/vm_compiler.c Compiles AST to bytecode
Bytecode VM interpreter/vm.c Register-based VM with JIT for __C__ blocks
Tree-walker lib/Strada/Interpreter.strada Fallback AST evaluator (--tree-walk)
Driver interpreter/Main.strada File execution and REPL

Scoping

The interpreter maintains a chain of environment hashes for lexical scoping. Each scope has a vars hash and a parent pointer. Variable lookup walks from innermost to outermost scope. my creates in the current scope; assignments update the nearest scope containing the variable.

Control Flow

Loop control (next, last, redo) and return are implemented as exception-based signals caught by the enclosing loop or function call handler. Labeled loops match signals by label name.

Supported Features

The interpreter supports the full Strada language:

The bytecode VM runs the entire compiler test suite at parity: 108 pass, 0 fail (12 skips need shared-object compilation or compiled-format stack traces).

Limitations

Not supported in the interpreter
  • Async/await (requires compiled thread pool runtime)

Native shared libraries loaded via import_lib still work — they are called through dlopen/dlsym regardless of execution mode.

Building

# Build the interpreter
make interpreter

# The interpreter binary
./strada-interp --help

# Usage
Strada Interpreter v0.3
Usage:
  strada-interp                          Interactive REPL
  strada-interp [OPTIONS] <file.strada>  Execute file
  strada-interp -e 'code'                Execute code string

Options:
  -e CODE       Execute code (wraps in main() if needed)
  -L PATH       Add library search path
  -h, --help    Show this help message

One-Liner Execution

Use -e to evaluate Strada code directly from the command line:

# Simple expression
strada-interp -e 'say("hello world")'

# Multiple -e flags are concatenated
strada-interp -e 'my $x = 6;' -e 'say($x * 7)'

# No func main() boilerplate needed
strada-interp -e 'my @nums = (1, 2, 3); foreach (@nums) { say($_); }'