Inform (Z-machine / Glulx)
Status: Planned — No implementation started.
Background
Inform is the dominant compiler for Interactive Fiction. Two major versions:
- Inform 6 — C-like source language, been around since 1993
- Inform 7 — natural language source ("The ball is on the table.")
Both compile to one of two VMs:
- Z-machine — classic 8-bit-era IF VM with 6 standard versions (z5 most common, z8 for large games)
- Glulx — 32-bit extended VM that removes Z-machine's size and address-space limits
The VM specifications are exhaustively documented and stable. Many open-source interpreters exist.
Formats
Z-machine (.z3, .z5, .z8, .zblorb)
- Fixed-size header with version, entry point, object table address, dictionary address
- Static game data (object tree, property tables, dictionary) in low memory
- Dynamic game data (object attributes, variables) in high memory
- Z-machine bytecode in high memory
- Optional Blorb wrapper (
.zblorb) adds audio, graphics, metadata
Z-machine v5/v8 add:
@aread/@sread— line input with optional timed callbacks@print_unicode/@check_unicode— Unicode support- Extended instruction set (
@je,@jl, etc. in z5+)
Glulx (.ulx, .gblorb)
- 32-bit addressing throughout (removes Z-machine's 64KB/128KB/256KB limits)
- Glk I/O library for window management, file I/O, and sound
- Stack-based VM with register-indirect addressing
- Optional Blorb wrapper (
.gblorb) for multimedia
Glulx is documented in the Glulx specification by Andrew Plotkin.
Lifting Strategy
Full recompilation (Tier 2). The goal is emitted TypeScript (or Rust) per routine — not bundling an interpreter.
The Z-machine and Glulx are well-specified VMs with manageable opcode sets. Lifting means:
- Parse the story file header to identify VM type, version, and entry points
- Decode bytecode in reachable routines (Z-machine: variable-length instructions with 1-4 operands; Glulx: similar)
- Recover the object tree and property tables (Z-machine stores the world model as a tree structure with 31/255 attributes and properties per object)
- Emit IR per routine
- Replace Glk I/O calls with web UI equivalents
Z-machine Specifics
Z-machine programs use a global variable array (240 globals), a local variable stack per routine (up to 15 locals), and a call stack. The object tree (up to 255 objects in v1-3, 65535 in v5+) stores the game world. Objects have attributes (bitmask), properties (variable-length), and a parent/sibling/child tree.
Key opcodes to model:
call/call_1n/call_2n— routine calls with 0-3 argsstorew/storeb/loadw/loadb— word/byte memory accessput_prop/get_prop/get_prop_addr/get_next_prop— object property accessset_attr/clear_attr/test_attr— object attribute manipulationinsert_obj/remove_obj/move— object tree manipulationprint/print_addr/print_ret/print_num/print_char— outputread/aread— line inputsave/restore/save_undo/restore_undo— save state
Glulx Specifics
Glulx uses a simpler, more regular instruction set. The main complexity is Glk — the I/O abstraction layer. Glk provides windows (text, graphics, pair), streams, and file references. The web implementation maps:
- Text window →
<div>with scrollable text - Status window → fixed-height header
- Graphics window →
<canvas> - File I/O → localStorage or IndexedDB
What Needs Building
Format Parser (new crate: reincarnate-frontend-inform)
- [ ] Z-machine header parser (version detection, address table locations)
- [ ] Z-machine bytecode decoder (full opcode table for v5 and v8)
- [ ] Object tree extractor (objects, attributes, properties, parent/sibling/child)
- [ ] Dictionary extractor (word → Z-machine word code)
- [ ] Glulx header parser and bytecode decoder
- [ ] Glk call identification
- [ ] Blorb resource extractor (
.zblorb/.gblorb— Blorb is an IFF-like container)
IR Mapping
- Z-machine routine → IR function
- Local variables → IR function parameters + temporaries
- Global variables → module-level state
- Object tree → structured data (options: static JS object map, or dynamic object with attribute/property arrays)
printcalls →SystemCall("Inform.Print", text)read/aread→SystemCall("Inform.ReadLine")+Yield(coroutine)save_undo/restore_undo→SystemCall("Inform.SaveUndo")/SystemCall("Inform.RestoreUndo")
Replacement Runtime (runtime/inform/ts/)
- [ ] Glk window system: text buffer, text grid, graphics, pair windows
- [ ] Text output with style attributes (bold, italic, fixed-width, etc.)
- [ ] Line input with command history and timeout
- [ ] Character input
- [ ] Save/restore (serialize interpreter state)
- [ ] Undo stack
- [ ] Sound playback (Blorb sound resources)
- [ ] Image display (Blorb image resources)
- [ ] File I/O (Glk streams → localStorage / File API)
- [ ] Unicode support (Z-machine v5+
@print_unicode) - [ ] Z-machine-specific: object tree operations (insert_obj, remove_obj, move), property get/set
Known Challenges
- Inform 7 "natural language" — Inform 7 source is in English prose. The I7 compiler translates it to I6 (the older Inform 6 language) which then compiles to Z-machine/Glulx. The bytecode output is already I6-generated code, so decompiling to I6 is feasible. Recovering the original I7 source prose is not realistic (and not necessary for lifting).
- Compressed text — Z-machine uses Zscii encoding and a 3-character alphabet table compression scheme. Must be decoded to Unicode.
- Complex object model — The Z-machine object tree encodes the IF world model (rooms, things, actors). Some optimizations assume knowledge of which objects are rooms vs items vs NPCs — this information isn't directly in the bytecode.
- Undo granularity — Z-machine
@save_undo/@restore_undomust save/restore the entire interpreter state. The approach (copy-on-write snapshots vs diff-based) affects memory and performance.
Optionally: Lifting from Inform 7 Source
If the game's .inform project directory is available (not just the compiled story file), the I7 source can be used directly. This would require an Inform 7 → IR translation pipeline bypassing the VM entirely. Given the complexity of the I7 language (natural language grammar, extension mechanism), this is a separate, much larger project.
Note: Existing Interpreters
Parchment (Z-machine + Glulx via WASM) and Quixe (pure-JS Glulx) exist and work well for rapid deployment. But they ship an interpreter loop — the output is not emitted code, it's not analysable, and it can't target Rust. The reincarnate approach compiles routines to functions.