zuri.compile
import zuri
Everything here is re-exported by
zuri, soimport zuriis enough and the names are called aszuri.*. Importingzuri.compileon its own works too and reaches the same definitions.
The Instr type zuri.compile() returns: a generic
representation of one VM instruction, tagged by op ('LoadConst',
'Add', 'Jmp', …) with a fields dict holding that opcode’s own
operands. As with zuri.ast.Node, there’s one
Instr class rather than one class per opcode. The instruction set has
around sixty opcodes, and a dedicated class per opcode would be sixty
near-identical classes to keep in sync with the compiler forever after.
import zuri
for instr in zuri.compile('var x = 1 + 2') {
echo '${instr.op} ${instr.fields}'
}
Resolved operands
compile() returns a flat instruction list, matching the VM’s own
Instr type, but several opcodes (LoadConst, GetGlobal, GetField,
Invoke, …) only carry a raw index into the compiler’s own constant
pool, which isn’t otherwise exposed. Rather than force every caller to
also separately fetch and correlate a constants table, each such field
gets its already-resolved value embedded right on the instruction too,
alongside the raw index: e.g. a LoadConst node’s fields has both
const_idx (the raw index) and value (what it actually points at). A
resolved field is never literally named after the raw index’s own
subject when that word is a reserved keyword (const, class, …),
since a dict key can’t be read back with plain dot-access syntax then;
class becomes class_reg (it’s a register index anyway, not a
resolved class value), and similarly for a handful of others.
A Closure instruction’s resolved value is a nested function prototype
({ name, arity, variadic, instructions }), whose own instructions is
recursively wrapped the exact same way, so a script with functions or
methods in it exposes their bodies too, not just the code immediately
around them.
Every instruction node carries the source line it was compiled from,
but never a column. The compiler’s own line tracking is line-only
(statement granularity), unlike a
zuri.token.Token’s or
zuri.ast.Node’s position, which both have real
column info from the lexer/parser.
Functions
walk_instrs()
zuri.walk_instrs(instrs, visitor)
Recursively visits every instruction reachable from instrs (typically
zuri.compile()’s own result, or any single Instr plucked
out of it), including inside a Closure instruction’s own nested
function body, in the order they appear, calling visitor for each one.
visitor is either a plain function, called with every instruction
(visitor(instr)), or a dict from opcode name to function, where only a
matching instruction gets called (visitor[instr.op](instr)). Reach for
the dict form when you only care about one or two opcodes.
Always visits the whole tree; there’s no way for visitor to skip
descending into a nested function body.
import zuri
var instrs = zuri.compile(source)
# every global name this script writes to, anywhere, including
# inside nested function bodies
var written = []
zuri.walk_instrs(instrs, {
SetGlobal: @(instr) { written.append(instr.fields.name) },
})
Parameters
instrs(Instr|list[Instr])visitor(function|dict)
find_instrs()
zuri.find_instrs(instrs, op) -> list[Instr]
Every instruction reachable from instrs whose op is op, in the
order they appear. Shorthand for the common “just give me a flat list of
these” case walk_instrs() itself is built on.
import zuri
var instrs = zuri.compile(source)
for jmp in zuri.find_instrs(instrs, 'Jmp') {
echo jmp.fields.offset
}
Parameters
instrs(Instr|list[Instr])op(string)
Returns list[Instr]
compile()
zuri.compile(source) -> list[Instr]
Compiles source as a standalone script and returns its instructions as
a flat list of Instr. See this module’s own doc comment for
exactly what each instruction carries.
Raises (rather than returning partial bytecode) on a lexer, parser, or compiler error.
Parameters
source(string) — The Zuri source to compile
Returns list[Instr]
Raises Error if source doesn’t compile
compile_file()
zuri.compile_file(path) -> list[Instr]
Reads the file at path and compiles its contents; exactly
compile(file(path).read()), for the common case of compiling a real
file rather than a source string you’ve already got in hand.
Parameters
path(string) — Path to the Zuri source file to compile
Returns list[Instr]
Raises Error if path can’t be opened or read, or doesn’t compile
check()
zuri.check(source: string) -> list[Diagnostic]
Every problem compiling source would report, as a list of
zuri.Diagnostic in the order they
occur, without running anything and without raising. An empty list means
source compiles.
Source that does not parse reports its syntax errors, exactly as
parse_partial() finds them. Source that parses goes on to the
compiler, which reports a name declared twice in one scope, self or
parent outside a method, parent in a class with no superclass,
break or continue outside a loop, an assignment to a constant, a
private member reached through anything other than self or parent, a
class extension declaring a field or an instance method, and a function
too large for its registers.
import zuri
for problem in zuri.check('if true {\n break\n}') {
echo problem # 2:3: 'break' used outside of a loop
}
echo zuri.check('var x = 1').length() # 0
Parameters
source(string) — The Zuri source to check
Returns list[Diagnostic]
check_file()
zuri.check_file(path: string) -> list[Diagnostic]
Reads the file at path and checks it; exactly
check(file(path).read()).
Parameters
path(string) — Path to the Zuri source file to check
Returns list[Diagnostic]
Raises Error if path can’t be opened or read
Classes
Instr
class zuri.Instr
One VM instruction.
- printable — has a
@to_string(), soechoandprint()show something useful
Constructor
zuri.Instr(op, line, fields)
Returns a new instance of an Instr directly, from already-
wrapped field values. Ordinary Zuri code should use zuri.compile()
instead; this constructor does no wrapping of its own.
Parameters
op(string) — The opcode name, e.g.'LoadConst','Add'line(?number) — The source line this instruction was compiled from, ornilif the compiler never stamped onefields(dict) — This opcode’s own operands, already wrapped
Instr.to_string()
zuri.Instr.to_string() -> string
This instruction as op@line, e.g. 'LoadConst@3', or just op when
it carries no line. A one-line summary; use dump() to actually see
what’s inside an instruction.
Returns string
Instr.dump()
zuri.Instr.dump() -> string
A readable, indented, multi-line dump of this instruction and everything
inside it, recursively (including a Closure instruction’s own nested
function body). Same idea as
zuri.ast.Node.dump: the actual way to find out
what an opcode’s fields looks like is to run something and read the
dump, not to guess a key name from the opcode’s name alone.
import zuri
var instrs = zuri.compile('var x = 1')
echo instrs[0].dump()
prints
LoadConst@0
dst: 0
const_idx: 1
value: 1
Returns string
2026, Richard Ore and Zuri contributors