writeonce/compiler
shoney.arickathil 02b4b13a52 Merge master into db-residency-doctrine — and close the two half-exposed features
The branch was 17 ahead / 25 behind with 11 conflicting files, and drifting
further: db.c had been rewritten twice on master since (group commit, then
compaction). Resolved rather than rebased so both histories stay legible.

Conflicts, and how each was settled:

- db.c: BOTH semantics kept. Master's fatal path and compaction check now sit
  behind the branch's `table_is_durable` predicate, in all three inline arms —
  a volatile table reaches neither the barrier nor the compaction check
- db-bench sample: every mode from both sides (growth, growth-verify, randread,
  replayseed, wmix) and ONE `boot` mode, which both sides had added
  independently
- db-bench.py: all six legs kept. Both sides had also grown the same
  WAL-size helper under different names; collapsed into one
- perf-targets: the branch's §5 (RAM ceiling) then master's §6/§7 — master's
  numbering had already assumed a §5 it did not have
- story frontmatter: master's `status` (the landing truth) plus the branch's
  `readiness` axis. 03 would have read `done` + `refine`, which is a
  contradiction — it was brainstormed and landed on master, so `ready`
- board: both standup blocks newest-first; master's chain rows (a superset);
  the branch's databasev2 1-2 rows with master's 3-4. Fixed a stray `|` in
  master's row 3
- baseline: master's, then REGENERATED from a full campaign — 143 metrics,
  132 checks, 0 failures with both sides' legs present

TWO HALF-EXPOSED FEATURES FIXED, because the merge rule is that master gets
no feature that is honoured in name only:

- `resident: keys` PARSED, set a .wob flag, and did nothing: rows stayed fully
  resident. A developer could declare a 120 GB table keys-resident, watch it
  compile, and be OOM-killed. The loader now REFUSES it with a message naming
  what to write instead, until tasks 5c/5d land. The compiler still parses it
  and its AST golden still passes, so the grammar work stays tested
- `durable: false` was honoured ONLY on the inline path. wo_db_exec_req had no
  guard at all, so a volatile table written from an actor on a worker shard
  would still be logged — precisely porch's session-table case, and precisely
  what iteration 2 exists to provide. All three request-path arms now carry the
  same predicate. Found by reading the merged code, not by a test: the obvious
  probe runs main() on the primary and therefore only exercises the inline path

Verified on the merged tree: wovm-test 0, woc-test 0, oop-e2e 122/0,
residency-accept 8/0, db-bench 132/0, linkcheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:14:25 +02:00
..
bin feat: iterations 19 + 17 — Float/Bytes scalars (.wob v5), library kind + internal/ 2026-08-20 19:24:15 +02:00
src Merge master into db-residency-doctrine — and close the two half-exposed features 2026-08-29 10:14:25 +02:00
test feat(wob): v7 — class descriptor carries durability and residency 2026-08-26 23:22:26 +02:00
dune-project feat(compiler): Tasks 1-4 — scaffold, diagnostics, lexer, declaration parser 2026-08-10 09:00:08 +02:00
README.md docs: audit all markdown against the code, fix findings, flatten status folders 2026-08-26 19:20:22 +02:00

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, consumes owner's four tables), disasm (bytecode disassembler, backs --dump-bc), dump (stable text dumps for all of the above)
  • bin/ — the woc executable: CLI parsing, file discovery, the multi-file/cross-file driver, --emit/build output
  • test/ — runner.ml (golden runner + CLI smoke) and test_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.