Zorb uses the Orb DSL to compile Z-machine stories into WebAssembly. This document covers the Orb concepts the codebase relies on.
1. Module Composition
A capsule is assembled from three pieces:
- Interpreter engine:
lib/zorb/interpreter.ex, read and quoted at build time by the assembler inlib/zorb/capsule/assembler.ex. - Host imports:
lib/zorb/capsule/host.exdeclares thezionamespace and is attached to the generated module withOrb.Import.register/1. - Story data: header fields, dictionary hash tables, and story memory, injected as literals into the module.
Version-specific interpreter branches are pruned from the quoted AST before the story data is added, so each capsule only contains code its version can reach.
2. Elixir Compiler Integration
Zorb transforms the interpreter AST with ordinary Elixir metaprogramming (quote, pattern matching, Macro) — there is no source-code round trip. Assembler.assemble/2 returns a final AST that is evaluated with Code.eval_quoted/3, which means dictionary hashing, version pruning, and story-data embedding all run while the capsule module is being compiled.
3. Custom Types
Orb allows defining custom types via the Orb.CustomType behaviour. These types map to underlying WebAssembly primitives (like :i32) but provide Elixir-side type safety and clarity. In Zorb, we use these for:
T.Address: Ensuring memory offsets are correctly handled.T.Variable: Clarifying when an integer represents a Z-machine variable index.T.Object: Distinguishing object IDs from raw numbers.
These types exist only at compile-time and do not introduce any runtime overhead in the final WebAssembly binary.