ww: rename toolchain to w-prefix + hare-style build/run/test driver
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:
cmd/wwc/ → cmd/wcc/ libwwc.a → libwcc.a
cmd/6{c,a,l} → cmd/w6{c,a,l} binary names too
test/wwc/ → test/wcc/ 6 test files w/ w6 prefix
selfhost/cmd mirror in lockstep
bootstrap/amd64/{w6c,w6a,w6l} snapshot binaries (gitignored)
WW_6{C,A,L} → WW_W6{C,A,L} env-var overrides
Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:
ww test [path] discover *_test.ww in a directory module, run
each; single-file mode for `ww test foo.ww`
Module-by-name `ww build foo` resolves to foo.ww or foo/foo.ww
via search path (cwd : -I dirs : $WW_LIB)
Default-to-cwd `ww build` / `ww test` build the cwd module
Run pass-through `ww run path arg1 arg2` reaches the program
lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.
Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.
Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.
This commit is contained in:
108
PLAN.md
108
PLAN.md
@@ -13,31 +13,31 @@ that prints its version. No compilation yet.
|
||||
Deliverables:
|
||||
|
||||
- `Makefile` (POSIX) with `all`, `test`, `clean`, `install` targets.
|
||||
- `cmd/wwc/` C library skeleton (builds `libwwc.a`):
|
||||
- `cmd/wcc/` C library skeleton (builds `libwcc.a`):
|
||||
- `ww.h` — central typedefs (`Node`, `Sym`, `Type`, `Lex`)
|
||||
- `mem.c` — bump arena allocator (no `malloc` in hot paths)
|
||||
- `err.c` — `errorf`, `fatal`, `warn`
|
||||
- (lex/parse/check stubs added in later phases)
|
||||
- `cmd/ww/` driver skeleton: argv parsing, version print.
|
||||
- `test/run` — shell test harness.
|
||||
- `test/wwc/000_smoke.c` — smoke test that asserts `ww -V` prints
|
||||
- `test/wcc/000_smoke.c` — smoke test that asserts `ww -V` prints
|
||||
the version.
|
||||
|
||||
Per-target binaries (`6c`, `6a`, `6l`) are NOT created here. They
|
||||
Per-target binaries (`w6c`, `w6a`, `w6l`) are NOT created here. They
|
||||
ship in their own phases (4, 5, 6).
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- `make` produces `out/bin/ww` and `out/lib/libwwc.a`.
|
||||
- `make` produces `out/bin/ww` and `out/lib/libwcc.a`.
|
||||
- `make test` runs the smoke test and passes.
|
||||
|
||||
## Phase 1 — Lexer (in `libwwc.a`)
|
||||
## Phase 1 — Lexer (in `libwcc.a`)
|
||||
|
||||
Goal: tokenize ww source into a stream of `Tok` structs.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/wwc/lex.c` — hand-rolled DFA. Handles:
|
||||
- `cmd/wcc/lex.c` — hand-rolled DFA. Handles:
|
||||
- identifiers, keywords (`fn`, `let`, `def`, `if`, `else`, `for`,
|
||||
`switch`, `case`, `return`, `use`, `type`, `struct`, `defer`,
|
||||
`break`, `continue`, `export`, `proc`, `chan`, `nil`, `true`,
|
||||
@@ -49,10 +49,10 @@ Deliverables:
|
||||
(`@symbol("name")`, etc.)
|
||||
- explicit `;` terminators — no automatic insertion (Hare rule)
|
||||
- `//` and `/* */` comments
|
||||
- `cmd/wwc/tok.c` — token names, debug printer.
|
||||
- `test/wwc/100_lex.c` — table-driven: input → token sequence.
|
||||
- `cmd/wcc/tok.c` — token names, debug printer.
|
||||
- `test/wcc/100_lex.c` — table-driven: input → token sequence.
|
||||
- `test/lang/lex/*.ww` — small files exercised via a tiny test
|
||||
harness that links against `libwwc.a`.
|
||||
harness that links against `libwcc.a`.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
@@ -60,15 +60,15 @@ Exit criteria:
|
||||
stream deterministically.
|
||||
- All lex tests green.
|
||||
|
||||
## Phase 2 — Parser & AST (in `libwwc.a`)
|
||||
## Phase 2 — Parser & AST (in `libwcc.a`)
|
||||
|
||||
Goal: parse the full grammar into an AST. No types yet.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/wwc/parse.c` — recursive descent. Pratt expression parser for
|
||||
- `cmd/wcc/parse.c` — recursive descent. Pratt expression parser for
|
||||
precedence. No yacc.
|
||||
- `cmd/wwc/ast.c` — `Node` constructor helpers; printer.
|
||||
- `cmd/wcc/ast.c` — `Node` constructor helpers; printer.
|
||||
- Grammar coverage:
|
||||
- `use` imports (paths use `.` not `::`)
|
||||
- `type` declarations (struct, alias, fn type) with trailing `=`
|
||||
@@ -81,7 +81,7 @@ Deliverables:
|
||||
- `defer`
|
||||
- body-less `fn` declarations and `@symbol("name")` attribute for
|
||||
FFI imports
|
||||
- `test/wwc/200_parse.c` — table-driven AST shape tests.
|
||||
- `test/wcc/200_parse.c` — table-driven AST shape tests.
|
||||
- `test/lang/parse/*.ww` — programs that should parse but not yet
|
||||
typecheck; harness only checks parser exits 0.
|
||||
|
||||
@@ -91,22 +91,22 @@ Exit criteria:
|
||||
`ref/hare/cmd/hare/main.ha`.
|
||||
- AST printer output is deterministic.
|
||||
|
||||
## Phase 3 — Type checker (in `libwwc.a`)
|
||||
## Phase 3 — Type checker (in `libwcc.a`)
|
||||
|
||||
Goal: resolve names, infer/check types, produce a typed AST.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/wwc/sym.c` — symbol table. Lexical scopes. Plan 9 style hash.
|
||||
- `cmd/wwc/type.c` — type representation, equality, conversion rules.
|
||||
- `cmd/wwc/check.c` — type checking pass over the AST. Errors carry
|
||||
- `cmd/wcc/sym.c` — symbol table. Lexical scopes. Plan 9 style hash.
|
||||
- `cmd/wcc/type.c` — type representation, equality, conversion rules.
|
||||
- `cmd/wcc/check.c` — type checking pass over the AST. Errors carry
|
||||
source locations.
|
||||
- Built-in types: all integer widths, `bool`, `f32`, `f64`, `rune`,
|
||||
`str`, `void`, pointers, slices `[]T`, fixed arrays `[N]T`,
|
||||
function types.
|
||||
- Slice semantics: `{ ptr, len, cap }`, indexing bounds-checked at
|
||||
runtime (debug builds; release may elide via flag).
|
||||
- `test/wwc/300_check.c` — table-driven: src → expected error or OK.
|
||||
- `test/wcc/300_check.c` — table-driven: src → expected error or OK.
|
||||
- `test/lang/check/*.ww` — both passing and failing cases.
|
||||
|
||||
Exit criteria:
|
||||
@@ -114,17 +114,17 @@ Exit criteria:
|
||||
- All built-in types correctly checked.
|
||||
- Negative tests produce stable, useful diagnostics.
|
||||
|
||||
## Phase 4 — `6c` amd64 compiler
|
||||
## Phase 4 — `w6c` amd64 compiler
|
||||
|
||||
Goal: turn typed AST into Plan 9-style amd64 assembly text. The
|
||||
binary `6c` is a Plan 9 cc analogue for amd64. There is no separate
|
||||
binary `w6c` is a Plan 9 cc analogue for amd64. There is no separate
|
||||
SSA IR file; the only on-disk intermediate is `.s`.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/6c/` (links against `libwwc.a`):
|
||||
- `cmd/w6c/` (links against `libwcc.a`):
|
||||
- `6.out.h` — amd64 opcode enum, register names, addressing
|
||||
modes. Mirror `ref/plan9front/sys/src/cmd/6c/` shape.
|
||||
modes. Mirror `ref/plan9front/sys/src/cmd/w6c/` shape.
|
||||
- `gc.h` — `Prog`, `Adr`, scratch regs, stack layout.
|
||||
- `cgen.c` — typed AST → `Prog` list (walking, not SSA).
|
||||
- `txt.c` — `Prog` list → textual Plan 9 amd64 asm.
|
||||
@@ -133,22 +133,22 @@ Deliverables:
|
||||
first; tighten later).
|
||||
- `mkfile` — Plan 9 mkfile next to the Makefile entries.
|
||||
- Output suffix `.s`. Plan 9 amd64 asm syntax (not GAS).
|
||||
- `test/lang/6c/*.ww` — golden tests: src → expected `.s` text.
|
||||
- `test/lang/w6c/*.ww` — golden tests: src → expected `.s` text.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- `6c hello.ww` produces `hello.s`.
|
||||
- `w6c hello.ww` produces `hello.s`.
|
||||
- 20+ programs round-trip identically across runs.
|
||||
- The output is consumable by phase 5's `6a`.
|
||||
- The output is consumable by phase 5's `w6a`.
|
||||
|
||||
## Phase 5 — `6a` amd64 assembler
|
||||
## Phase 5 — `w6a` amd64 assembler
|
||||
|
||||
Goal: assemble Plan 9 amd64 asm into ELF object files. ELF (not
|
||||
Plan 9 a.out) so we can interop with C archives in phase 8.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/6a/`:
|
||||
- `cmd/w6a/`:
|
||||
- `lex.c` — tokenize Plan 9 asm.
|
||||
- `parse.c` — hand-rolled grammar (Plan 9 uses yacc; we go
|
||||
hand-rolled to keep rule #6 honest).
|
||||
@@ -157,21 +157,21 @@ Deliverables:
|
||||
- `mkfile`
|
||||
- Pseudoregisters supported: `SP`, `FP`, `SB` (static base) per
|
||||
Plan 9 convention, on top of `AX`, `BX`, ..., `R8`-`R15`.
|
||||
- `test/wwc/500_asm.c` — round-trip known asm to known bytes.
|
||||
- `test/wcc/500_asm.c` — round-trip known asm to known bytes.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- `6a hello.s -o hello.o` produces a valid ELF amd64 object.
|
||||
- `w6a hello.s -o hello.o` produces a valid ELF amd64 object.
|
||||
- `objdump -d hello.o` matches expectations across a 20-program
|
||||
corpus.
|
||||
|
||||
## Phase 6 — `6l` amd64 linker
|
||||
## Phase 6 — `w6l` amd64 linker
|
||||
|
||||
Goal: link ELF objects into a static ELF executable.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `cmd/6l/`:
|
||||
- `cmd/w6l/`:
|
||||
- `obj.c` — load ELF `.o` files and `.a` archives.
|
||||
- `sym.c` — global symbol table; resolution.
|
||||
- `pass.c` — section layout, relocation application.
|
||||
@@ -182,14 +182,14 @@ Deliverables:
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- `6l -o hello hello.o libwwrt.a` produces a static ELF binary.
|
||||
- `w6l -o hello hello.o libwwrt.a` produces a static ELF binary.
|
||||
- `ldd hello` reports "not a dynamic executable".
|
||||
- The binary exits 0 with the program's intended semantics.
|
||||
|
||||
## Phase 7 — Runtime + driver wiring
|
||||
|
||||
Goal: a tiny static runtime, plus the `ww` driver wired to call
|
||||
`6c → 6a → 6l` end to end.
|
||||
`w6c → w6a → w6l` end to end.
|
||||
|
||||
Deliverables:
|
||||
|
||||
@@ -201,8 +201,8 @@ Deliverables:
|
||||
- `rt/slice.c` — slice helpers used by codegen
|
||||
(`bounds_check`, `slice_grow` for explicit user calls).
|
||||
- Build artifact: `out/lib/libwwrt.a`.
|
||||
- `cmd/ww/` driver: reads `ww build foo.ww`, runs `6c` then `6a`
|
||||
then `6l`, links `libwwrt.a` into the output.
|
||||
- `cmd/ww/` driver: reads `ww build foo.ww`, runs `w6c` then `w6a`
|
||||
then `w6l`, links `libwwrt.a` into the output.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
@@ -217,8 +217,8 @@ Deliverables:
|
||||
|
||||
- Body-less `fn` declarations + `@symbol("name")` attribute parser
|
||||
and checker rules already in phases 2/3 — finalize codegen.
|
||||
- amd64 SysV ABI: formalize struct-by-value and varargs in `6c`.
|
||||
- `6l` learns to slurp static archives from a search path.
|
||||
- amd64 SysV ABI: formalize struct-by-value and varargs in `w6c`.
|
||||
- `w6l` learns to slurp static archives from a search path.
|
||||
- `lib/c/libc/` — minimal libc bindings (`malloc`, `free`, `printf`,
|
||||
`read`, `write`, `open`, `close`).
|
||||
- `lib/c/tls/` — bindings to libtls or BearSSL (decide; BearSSL is
|
||||
@@ -226,7 +226,7 @@ Deliverables:
|
||||
- `lib/c/crypto/` — bindings to libcrypto subset (sha256, aes, hmac).
|
||||
- `lib/c/curses/` — ncurses bindings (initscr, getch, mvprintw, ...).
|
||||
- `ww` driver: `ww build -lcrypto -ltls -lncurses` resolves these
|
||||
via a `pkg-config`-style probe, hands `.a` paths to `6l`.
|
||||
via a `pkg-config`-style probe, hands `.a` paths to `w6l`.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
@@ -268,7 +268,7 @@ Exit criteria per module:
|
||||
|
||||
## Phase 10 — Self-host
|
||||
|
||||
Goal: rewrite `libwwc`, `6c`, `6a`, `6l`, and `ww` in ww. Drop the
|
||||
Goal: rewrite `libwcc`, `w6c`, `w6a`, `w6l`, and `ww` in ww. Drop the
|
||||
C bootstrap.
|
||||
|
||||
Steps:
|
||||
@@ -278,9 +278,9 @@ Steps:
|
||||
2. Port `lex.c` → `lex.ww`. Diff token streams against C output.
|
||||
3. Port `parse.c` → `parse.ww`. Diff ASTs.
|
||||
4. Port `check.c`. Diff typed ASTs.
|
||||
5. Port `6c` (cgen, txt, peep, reg, swt). Diff `.s` output byte
|
||||
5. Port `w6c` (cgen, txt, peep, reg, swt). Diff `.s` output byte
|
||||
for byte.
|
||||
6. Port `6a` and `6l`. Diff resulting `.o` and final binaries.
|
||||
6. Port `w6a` and `w6l`. Diff resulting `.o` and final binaries.
|
||||
7. Three-stage bootstrap:
|
||||
`Cstage (gcc-built tools) → ww1 → ww2 → ww3`, with
|
||||
`cmp ww2 ww3` OK for each tool.
|
||||
@@ -288,7 +288,7 @@ Steps:
|
||||
Exit criteria:
|
||||
|
||||
- `make bootstrap` runs the three-stage build green.
|
||||
- The C `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/` C trees are
|
||||
- The C `cmd/wcc/`, `cmd/w6c/`, `cmd/w6a/`, `cmd/w6l/` C trees are
|
||||
deleted in this phase's final commit.
|
||||
|
||||
### Status (2026-05)
|
||||
@@ -296,10 +296,10 @@ Exit criteria:
|
||||
Steps 1–6 done. The five ww-side tools live in `selfhost/cmd/`:
|
||||
|
||||
- `wwc/` — ww-native lex/parse/check/cgen (the frontend library)
|
||||
- `6c/` — ww-native compiler binary; thin driver over wwc's cgen
|
||||
- `6a/` — ww-native amd64 assembler
|
||||
- `6l/` — ww-native amd64 linker
|
||||
- `ww/` — ww-native driver, shells to `6c_ww/6a_ww/6l_ww`
|
||||
- `w6c/` — ww-native compiler binary; thin driver over wwc's cgen
|
||||
- `w6a/` — ww-native amd64 assembler
|
||||
- `w6l/` — ww-native amd64 linker
|
||||
- `ww/` — ww-native driver, shells to `w6c_ww/w6a_ww/w6l_ww`
|
||||
|
||||
Step 7 reaches its fixed point: `make bootstrap` produces ww1 → ww2 →
|
||||
ww3 with `cmp ww2 ww3` byte-identical. Tests 990–994 confirm each
|
||||
@@ -312,20 +312,20 @@ toolchain underneath).
|
||||
the wwstage tools end-to-end. Cstage is still required at first-
|
||||
checkout time (no checked-in stage-0 binary yet). ET_DYN dynamic
|
||||
linking is also fully ported to the ww side (test 996 confirms
|
||||
`6l_ww -L ... -l ...` is byte-identical to `6l` on snake); the one
|
||||
`w6l_ww -L ... -l ...` is byte-identical to `w6l` on snake); the one
|
||||
remaining gap is that `ww_ww build`'s argv parser does not yet
|
||||
forward `-L`/`-l` to the linker — `6l_ww` accepts them when invoked
|
||||
forward `-L`/`-l` to the linker — `w6l_ww` accepts them when invoked
|
||||
directly, just not through the ww-side driver. Snake's Makefile
|
||||
still drives the C `ww` for that reason; switching it to `6l_ww` +
|
||||
still drives the C `ww` for that reason; switching it to `w6l_ww` +
|
||||
explicit args works today.
|
||||
|
||||
Exit criterion 1 (bootstrap green) is met. Exit criterion 2 (delete
|
||||
the C trees) is **deferred to v1.0**: removing `cmd/wwc/`, `cmd/6c/`,
|
||||
`cmd/6a/`, `cmd/6l/`, `cmd/ww/` is a one-way door. Until the compiler
|
||||
the C trees) is **deferred to v1.0**: removing `cmd/wcc/`, `cmd/w6c/`,
|
||||
`cmd/w6a/`, `cmd/w6l/`, `cmd/ww/` is a one-way door. Until the compiler
|
||||
stops churning we keep Cstage as the canonical fresh-checkout entry
|
||||
point and treat `selfhost/cmd/*` as the path forward. At v1.0, the
|
||||
plan is to ship a checked-in stage-0 binary archive
|
||||
(`bootstrap/<arch>/{ww,6c,6a,6l}`), delete the C trees, and promote
|
||||
(`bootstrap/<arch>/{ww,w6c,w6a,w6l}`), delete the C trees, and promote
|
||||
`selfhost/cmd/*` to `cmd/*`.
|
||||
|
||||
See `BOOTSTRAP.md` for the build flow and `make cstage`/`make wwstage`
|
||||
@@ -365,7 +365,7 @@ Exit criteria:
|
||||
|
||||
## Test cadence
|
||||
|
||||
Every phase has its own test directory (`test/wwc/<phase>_*.c`,
|
||||
Every phase has its own test directory (`test/wcc/<phase>_*.c`,
|
||||
`test/lang/<phase-area>/*.ww`). `make test` runs them all in order
|
||||
of phase. CI is just `make test` in a clean tree.
|
||||
|
||||
@@ -387,7 +387,7 @@ Tag the tree at each phase boundary:
|
||||
you've copied.
|
||||
- **Writing a linker is hard.** ELF is documented, static linking
|
||||
is the small subset. Read `ref/plan9front/sys/src/cmd/8l/` and
|
||||
the ELF spec before writing `6l`. Start with hello-world and grow.
|
||||
the ELF spec before writing `w6l`. Start with hello-world and grow.
|
||||
- **Static linking against C libs.** OpenSSL is a pain to build
|
||||
static; BearSSL or LibreTLS are more pleasant. Pick early.
|
||||
- **Self-host bootstrap divergence.** Diff outputs at every stage,
|
||||
|
||||
Reference in New Issue
Block a user