// the platform
Two languages in,
native code out.
Everything between the source file and the executable is written for this project: the parsers, the intermediate language, the optimizer, the register allocators, the assemblers, and the linkers. There is no LLVM underneath, and no system toolchain is invoked to produce a binary.
// frontends
Zia, and a BASIC that isn’t a toy
Zia is the language the platform is built for and built in — Zanna Studio is written in it. BASIC is a second, complete frontend rather than a demo: it has classes, structured control flow, and file I/O, and it lowers to exactly the same IL.
module Hello;
bind Zanna.Terminal as Term;
func start() {
var total = 0;
for i in 1..6 {
total = total + i;
}
Term.Say("Zanna says hello");
Term.SayInt(total);
}
Every program on this page was compiled and run with the toolchain in the repository. The comments showing results are the output those programs actually produced.
What Zia has
- Static typing with inference; classes with reference semantics, structs with copy semantics
- Interfaces with runtime dispatch and default method bodies
- Generics with interface constraints, enums with exhaustiveness checking
- Lambdas, optionals (
T?,?.,??),Result[T],try/catch/finally - File-based modules with an explicit
bindsystem - No raw pointers.
Ptris not part of the Zia source surface
What BASIC is for
- A modernised QBasic-style dialect:
LET, clean arrays, short-circuit booleans, lightweight objects - Structured control flow,
SUB/FUNCTION,CLASSwithSUB New, file and console I/O - Complete games ship in it — Tetris, Frogger, Pac-Man and Centipede are all BASIC demos in the tree
- Cross-language calls are real: Zia declares
expose foreign funcand calls into a BASIC module
// zanna il 0.3.0
The thin waist
Both frontends compile to one typed intermediate language. Everything downstream — the verifier, the optimizer, the VM, both native backends — only ever sees IL. That is what keeps a new frontend from becoming a fork of the whole toolchain, and it is why the VM and a native binary are required to agree.
Design properties
- Typed.
i1,i16,i32,i64,f64,ptr,str,void, plus error and resume-token types used only for exception lowering - SSA with block parameters rather than phi nodes — values are passed along edges, which is easier to read and to verify
- Explicit terminators, no fall-through. Every block ends in
ret,br,cbror similar - Checked arithmetic by default. Signed integer maths must use
.ovfforms; the verifier rejects a bareadd - Textual and hand-writable. You can write an
.ilfile yourself and run it - Versioned. Every module opens with
il 0.3.0
Why it is written down
The IL has a normative specification, and changes to opcodes, grammar or verifier rules go through a written design review before any code lands. The tools are held to the document rather than the document being back-filled from the tools.
Two registry dumps are generated from the live binary rather than
maintained by hand, so they cannot drift from what the compiler actually
implements: zanna --dump-opcodes and
zanna --dump-runtime-api.
The optimizer, shown rather than claimed
mem2reg promotes stack slots to SSA registers. Because the IL
uses block parameters, the result is unusually legible: the two stores and
the load disappear, and the value arrives at Join as an
argument. This is the real output of
zanna il-opt before.il -o after.il --passes mem2reg.
before
func @main() -> i64 {
entry:
%t0 = alloca 8
%t1 = icmp_eq 0, 0
cbr %t1, T, F
T:
store i64, %t0, 2
br Join
F:
store i64, %t0, 3
br Join
Join:
%t2 = load i64, %t0
ret %t2
}
after mem2reg
func @main() -> i64 {
entry:
%t1 = icmp_eq 0, 0
cbr %t1, T, F
T:
br Join(2)
F:
br Join(3)
Join(%t4:i64):
ret %t4
}
// passes
One optimizer, shared by both languages
Passes are registered and composable, and you can run any subset by hand
with zanna il-opt --passes. This is the default
-O1 pipeline as the driver reports it:
simplify-cfg, mem2reg, simplify-cfg, sccp, constfold, peephole, dce,
simplify-cfg, sccp, inline, peephole, dce, simplify-cfg
The registered set is wider than any single pipeline uses — SSA promotion, sparse conditional constant propagation, global value numbering, loop rotation, loop-invariant code motion, induction variable simplification, loop unrolling, early CSE, reassociation, inlining, sibling-recursion elimination, dead store elimination, exception-handling cleanup, peephole rewriting, and CFG simplification.
zanna build defaults to -O1;
zanna run defaults to -O0 to keep the edit-run loop
fast. -O2 is the release profile.
// execution
A VM and two native backends that must agree
The VM
The primary development target: fast to start, deterministic, and step-budgetable. Dispatch is pluggable — function table, switch, or threaded computed-goto, which is the default where the compiler supports it.
There are in fact two engines. A tree-walking IL VM, and a bytecode VM that compiles IL to a compact form. The bytecode engine is parity-tested against the tree-walker across a shared IL corpus, comparing return values, runtime stdout, and trap kinds.
Native code
AArch64 targeting AAPCS64, validated end-to-end on Apple Silicon across the demo games. x86-64 targeting both System V AMD64 and Windows x64, with linear-scan allocation, validated on Windows with the full codegen suite passing.
Zanna writes its own ELF, Mach-O and PE objects, assembles them, links them, and emits DWARF — which is what makes a dependency-free native build possible in the first place.
Determinism is a rule, not an aspiration
- The VM and a native executable must produce identical output for every defined program. That equivalence is enforced by tests.
- macOS native linking targets Apple Silicon. macOS x86-64 is not a supported native-link target.
- The bytecode VM is useful for bounded probes and tests; it is not a real-time game execution target. Games are built with
--build-profile release.
// standard library
The runtime both languages share
One library, reachable from Zia and from BASIC, written in portable C with a stable ABI and no compiler dependencies. The table below is generated from the runtime definition files, so it tracks the code rather than the documentation.
| Domain | Classes | Functions | What it covers |
|---|---|---|---|
| Graphics3D | 64 | 1240 | Meshes, materials, lights, animation, terrain, water, navmesh, post-FX |
| GUI | 79 | 1137 | Desktop widget toolkit, themes, accessibility, code editor |
| Game3D | 61 | 803 | World, entities, prefabs, character and camera controllers |
| Game | 52 | 781 | 2D game systems, HUD widgets, particles, tilemaps |
| Graphics | 47 | 573 | 2D canvas, pixels, images, colour, text |
| Collections | 31 | 487 | Lists, maps, sets, sequences |
| Network | 27 | 299 | TCP, UDP, HTTP, WebSocket, TLS |
| Input | 7 | 265 | Keyboard, mouse, gamepads, action maps, chords |
| Math | 12 | 264 | Vectors, matrices, quaternions, bit operations |
| IO | 16 | 233 | Files, paths, directories, temporary storage |
| Game2D | 1 | 143 | Scene documents, tilemaps, typed object properties |
| Audio | 9 | 132 | Mixer, voices, music, synthesis, spatial audio |
| Localization | 10 | 121 | Locales, number and date formatting, message catalogs |
| Graphics2D | 4 | 109 | Vector paths, transforms, gradients |
| Data | 9 | 97 | JSON, CSV, structured serialisation |
| Crypto | 10 | 81 | Hashing, digests, random, encoding |
Abridged — the full inventory also covers threads, time, processes, PTYs,
diagnostics, functional helpers, options and results. Run
zanna --dump-runtime-api for the complete machine-readable
contract.
// tooling
Built to be called by programs
Editors, scripts and coding agents are first-class consumers of this toolchain. Diagnostics are structured, exit codes are differentiated so a caller never has to parse prose, and the language servers speak both LSP and MCP.
Type-check without running
$ zanna check broken.zia --diagnostic-format=json
{
"diagnostics": [{
"severity": "error",
"code": "V-ZIA-UNDEFINED",
"stage": "sema",
"message": "Undefined identifier: mesage",
"range": {
"begin": { "line": 6, "column": 14 },
"end": { "line": 6, "column": 20 }
},
"source": " Term.Say(mesage);",
"help": "Declare the symbol, import it, or correct the spelling.",
"notes": [],
"fixits": []
}]
}
Pretty-printed here; the tool emits one line, and carries absolute file
paths and per-file ids that are trimmed above. Exit code
2 means compile or verification errors.
Evaluate a snippet
$ zanna eval '2 + 3 * 4' --json --type
{"success":true,"trapped":false,"resultType":"Integer","output":"14\n","error":"","type":"Integer"}
Ask the binary what it contains
Two inventories are generated from the live binary rather than maintained by hand, so they cannot drift from the implementation. They are what the editor completion, the language servers and the numbers on this site are all reading.
The IL has 84 opcodes, and the runtime registers 531 classes across 7,844 functions.
$ zanna --dump-opcodes | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["ilVersion"], len(d["opcodes"]))'
0.3.0 84
$ zanna --dump-runtime-api | python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d["classes"]), len(d["functions"]))'
531 7844
Run on 2026-08-05 against the tree at v0.2.99. Both dumps are JSON on one line; the counting is done here rather than eyeballed.
Look up a diagnostic code
$ zanna explain V-ZIA-UNDEFINED
V-ZIA-UNDEFINED (zia-sema)
Use of an undefined identifier; may include a did-you-mean fix-it
| Exit code | Meaning |
|---|---|
| 0 | Success — no errors |
| 1 | Usage error, or the target could not be resolved |
| 2 | Compile or verification errors |
| 3 | Runtime trap (zanna eval) |
// where it stands
Solid, and rough
Working well
- Both frontends compile substantial real programs — the demo games are tens of thousands of lines each
- The IL, verifier and optimizer are the most settled part of the tree
- AArch64 is validated end-to-end on Apple Silicon; x86-64 passes the full codegen suite on Windows
- The machine-readable surfaces are generated from the binary and do not drift
Still moving
- Language surface, diagnostics, IL rules and tooling all still change without notice
- The IL reference is
0.3.0; the source tree isv0.2.99and unreleased - There is no stable release, and no compatibility promise across versions