Task 3 of docs/superpowers/plans/2026-08-26-table-residency.md. - NO LAYOUT CHANGE. The plan said to add descriptor fields; the descriptor already had a `flags` u32 with only bit0 used, so both properties ride spare bits (WO_CLASSF_VOLATILE 0x02, WO_CLASSF_RESIDENT_KEYS 0x04). A v7 class record is byte-identical in shape to a v6 one, which is a much smaller and safer change than the plan assumed - both spelled as the NON-default, so a zero flags word means exactly what every pre-v7 image meant: durable, every row resident. A non-@table class has both clear by construction - the loader refuses the meaningless pair (bit1+bit2) independently of woc, on the standing principle that what the loader accepts the interpreter trusts. Verified by FORGING the flags word in an otherwise valid image, since woc will not emit one: flags=6 gives "durable:false with resident:keys", flags=8 still gives "unknown flags" - WOB_VERSION 6 -> 7. Kept because an OLDER runtime reading a v7 image would otherwise treat a volatile table as durable and quietly disagree with its own source. loader.c's check is exact-match, so a v6 image is refused rather than read with the bits clear — verified by patching a v7 header back down to 6 GAP FOUND AND CLOSED: woc ACCEPTED `durable: false, resident: keys`. Task 1's steps covered duplicates and bad values but never the combination, and the plan had only put that refusal in the loader. The spec wants both, so the compiler now refuses it too (WO-E102, checked after the argument list is complete since it is a property of the pair). A compile error is the one a developer can act on. VERSION DRIFT: the constant lives in FOUR places, not one. wob.h, emit.ml:157, disasm.ml:186, and compiler/test/runner.ml:2405 — the last is a deliberately independent reimplementation of the loader battery, and it caught the drift as 14 failures rather than silently passing. Its flags mask and the combination refusal are now in sync too, which is the point of it being independent rather than shared. Gates: woc-test 557/0, 18 runtime suites 0 fail, 18 ISO-flavour suites 0 fail, cli_smoke OK, oop-e2e 118/0, employee 8/0, db-actor 8/0, site 21/0. Zero goldens moved (git diff over golden/ empty). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| bin | ||
| src | ||
| test | ||
| dune-project | ||
| README.md | ||
compiler/ — the OCaml woc compiler
Lexer → parser → typechecker → ownership pass → bytecode emitter, for .wo. OCaml stdlib only (no Menhir, no ppx); dune is the build runner. Sibling of the C wovm bytecode VM (runtime/) — the two halves of the OOP track's spec (docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md) meet at plan 3, where woc's emitted .wob runs on wovm.
Stage: well past plan 3. Plan 2 (lexer through ownership pass) and plan 3 (docs/plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md, Tasks 1–6 + 8 — Task 7, a parity harness against the since-removed Rust runtime, was deferred by explicit decision) closed the milestone: .wo source compiles to .wob bytecode (--emit) and to a single self-contained executable (build) that runs wovm with no arguments and no repo-relative dependency. Milestone 1's acceptance gate — compile-time budget, the full conformance corpus under ASan, the single-binary smoke, both unit suites — is just oop-accept.
Since then the front end has taken iterations 15 ([deps], wo.lock, --update-deps), 17 (kind = "library", entry-less check mode, internal/ as WO-E108), 19 (Float and Bytes), 24 (call's typed reply, WO-E226), 34 (digest builtins), 35 (net deadline seams), 36 (not, bitwise operators, hex/binary literals, compound assigns — .wob v6) and 37 (the backtick raw text literal with {{ }} auto-escaping). Current language surface: docs/guides/language-surface.md. Current status: the board.
Requirements
OCaml 4.14.1, dune 3.14.0 — Ubuntu 24.04 apt packages (sudo apt install ocaml dune), the version floor. Confirm with ocaml -version / dune --version. No opam packages, no Menhir, no ppx — stdlib only.
Build, test
cd compiler && dune build # -> _build/default/bin/woc
cd compiler && dune runtest # test_diag unit checks + runner golden/CLI-smoke suite
just woc-build # same, from the repo root
just woc-test # same, from the repo root
WOC_BLESS=1 dune runtest (from compiler/) rewrites golden .expected files to match current output — use it once, by hand, to seed or intentionally update a fixture.
Running woc
woc <path> # compile (lex, parse, typecheck, ownership-check); nothing prints on success
woc <dir> # BUILDS instead, when <dir>/wo.toml exists — the primary mode
woc version # e.g. "writeonce 0.1.0 linux/amd64"
woc --emit <path> -o <out.wob> # compile through to a .wob bytecode module, runnable by wovm
woc build <dir> -o <app> [--runtime <path>]
# compile + append the .wob image to a copy of wovm (--runtime,
# else $WO_RUNTIME, a wovm beside this woc, or runtime/wovm)
woc --update-deps <dir> # re-fetch [deps] at their manifest revs, rewrite wo.lock
woc -D <name> ... # define a build flag for the #if/#else/#end token filter
woc --dump-tokens <path> # stdout: one line per lexed token
woc --dump-ast <path> # stdout: the declaration + body AST, indented
woc --dump-owner <path> # stdout: the ownership pass's four tables (moves, drops, rc, residual)
woc --dump-gc <path> # stdout: the inferred-GC pass's traced set
woc --dump-bc <path> # stdout: disassembled bytecode for every emitted method
woc <dir> on a directory holding a wo.toml is the mode every sample and the install docs use: it reads the manifest's name plus the optional [build] runtime/target keys and produces <target>/<name> exactly as woc build would. A manifest with kind = "library" is checked entry-less and writes nothing.
<path> is a single .wo file or a directory. A directory is discovered recursively for every .wo file under it: dot-prefixed entries and target/data/node_modules are skipped, results are sorted by path. Every discovered file compiles as one program (declarations in one file resolve for bodies in another, regardless of discovery order); diagnostics from every file and every stage print sorted by (file, line, col). For multi-file --dump-* output, each file's dump is preceded by a === path === header line (compiler/src/dump.ml's file_header) — a single-file run never prints one.
Diagnostics render as file:line:col: severity CODE: message plus a source excerpt with a caret; every shipped code is cataloged in docs/plan/oop-vm/01-error-catalog.md. Exit codes: 0 clean compile, 1 diagnostics reported, 2 usage/IO failure.
Layout
src/— one module per stage:diag(diagnostics, collector, exit-code decision),token/lexer,ast/parser,types(typechecker),gcinfer(the inferred-GC pass, backs--dump-gc),owner(MVS ownership pass),emit(bytecode emitter, consumesowner's four tables),disasm(bytecode disassembler, backs--dump-bc),dump(stable text dumps for all of the above)bin/— thewocexecutable: CLI parsing, file discovery, the multi-file/cross-file driver,--emit/buildoutputtest/—runner.ml(golden runner + CLI smoke) andtest_diag.ml(diag.ml unit checks);test/golden/<stage>/holds one-file-per-fixture goldens (tokens,ast,owner,owner-err,bc);test/fixtures/driver/holds the multi-file CLI-smoke fixtures (directory discovery, cross-file symbols, diagnostic ordering) that don't fit the one-.wo-file-per-fixture golden shape
Governing docs (all under docs/, not here — this file stays an orientation README): spec docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md; plans docs/plan/compiler/2026-08-01-woc-compiler-front.md and 2026-08-01-wob-emit-e2e-single-binary.md (+ architecture.md, nullable-types-implementation.md, 2026-08-01-haxe-parity-language.md in the same directory). Format contract: docs/plan/oop-vm/00-wob-format.md. Error catalog: docs/plan/oop-vm/01-error-catalog.md. Conformance corpus contract (fixture layout woc's golden output feeds into): docs/plan/oop-vm/02-corpus.md; source-language builtin surface woc accepts: docs/plan/oop-vm/08-builtin-surface.md. Runtime sibling: runtime/README.md.