plans: briefings + roadmaps for lua, prolog, forth, erlang, haskell

Five new guest-language plans mirroring the js-on-sx / hs-loop pattern, each
with a phased roadmap (Progress log + Blockers), a self-contained agent
briefing for respawning a long-lived loop, and a shared restore-all.sh that
snapshots state across all seven language loops.

Briefings bake in the lessons from today's stall debugging: never call
sx_build (600s watchdog), only touch lib/<lang>/** + own plan file, commit
every feature, update Progress log on each commit, route shared-file
issues to Blockers rather than fixing them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 15:16:45 +00:00
parent 81022784bc
commit 0f67021aa3
11 changed files with 985 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
# erlang-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/erlang-on-sx.md` forever. Actors + mailboxes + selective receive on delimited continuations. The million-process ring benchmark is the headline showcase.
```
description: erlang-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prompt
You are the sole background agent working `/root/rose-ash/plans/erlang-on-sx.md`. Isolated worktree, forever, one commit per feature. Never push.
## Restart baseline — check before iterating
1. Read `plans/erlang-on-sx.md` — roadmap + Progress log.
2. `ls lib/erlang/` — pick up from the most advanced file.
3. If `lib/erlang/tests/*.sx` exist, run them. Green before new work.
4. If `lib/erlang/scoreboard.md` exists, that's your baseline.
## The queue
Phase order per `plans/erlang-on-sx.md`:
- **Phase 1** — tokenizer + parser (atoms, vars, tuples, lists, binaries, clauses with patterns + guards)
- **Phase 2** — sequential eval + pattern matching + guards + core BIFs (`length`, `hd`, `tl`, `element`, `lists:map`, `io:format`)
- **Phase 3** — **THE SHOWCASE**: scheduler + processes + mailboxes + `spawn`/`!`/`receive` via delimited continuations. 5 classic programs (ring, ping_pong, bank, echo, fib_server) green. Million-process ring benchmark works.
- **Phase 4** — links, monitors, exit signals, `try/catch`
- **Phase 5** — modules + `M:F(...)` cross-module calls + `gen_server` + `supervisor` + registered processes
- **Phase 6** — list comprehensions + binary pattern matching + ETS-lite + drive corpus to 200+
Within a phase, pick the checkbox that unlocks the most tests per effort.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → next.
## Ground rules (hard)
- **Scope:** only `lib/erlang/**` and `plans/erlang-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, or `lib/` root. Erlang primitives go in `lib/erlang/runtime.sx`.
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken → Blockers entry, stop.
- **Shared-file issues** → plan's Blockers with minimal repro.
- **Delimited continuations** are in `lib/callcc.sx` + `spec/evaluator.sx` Step 5. `sx_summarise` spec/evaluator.sx first — 2300+ lines.
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
- **Worktree:** commit locally. Never push. Never touch `main`.
- **Commit granularity:** one feature per commit.
- **Plan file:** update Progress log + tick boxes every commit.
## Erlang-specific gotchas
- **Process** = record with `{pid, mailbox, state, continuation, links, monitors, trap_exit?}`. Scheduler queue is a list of runnable pids.
- **`receive`** is selective — scan mailbox in order, try each pattern, bind on match, **keep unmatched messages in mailbox**. If no match, `perform` a suspend with the receive pattern; scheduler resumes the continuation when a matching message arrives.
- **`after N -> ...`** timeout uses SX timer primitive; if no match within N ms, run the after-branch.
- **Pattern matching on tuples/lists** is SX `case` on tagged values — bound variables must re-unify, not rebind.
- **Guards are pure** — compile-time check that no side-effecting calls appear.
- **Immutable data** — no in-place mutation; sends copy.
- Atoms are interned strings.
- Don't go near the preprocessor (`-define`, `-ifdef`). Out of scope.
- Test corpus is custom (not BEAM conformance). Write your own plus classic programs in `lib/erlang/tests/programs/`.
## General gotchas (all loops)
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr.
- `type-of` on user fn returns `"lambda"`.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious.
- No new planning docs — update `plans/erlang-on-sx.md` inline.
- Short, factual commit messages (`erlang: spawn/1 + basic scheduler (+5)`).
- One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first `[ ]`; implement.

View File

@@ -0,0 +1,71 @@
# forth-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/forth-on-sx.md` forever. ANS-Forth 1994, Hayes Core conformance is the north star.
```
description: forth-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prompt
You are the sole background agent working `/root/rose-ash/plans/forth-on-sx.md`. Isolated worktree, forever, one commit per feature. Never push.
## Restart baseline — check before iterating
1. Read `plans/forth-on-sx.md` — roadmap + Progress log.
2. `ls lib/forth/` — pick up from the most advanced file.
3. If `lib/forth/tests/*.sx` exist, run them. Green before new work.
4. If `lib/forth/scoreboard.md` exists, that's your baseline; attack worst failure mode.
## The queue
Phase order per `plans/forth-on-sx.md`:
- **Phase 1** — reader + interpret mode + core stack/arith/cmp/logic/IO words
- **Phase 2** — colon definitions + compile mode + `VARIABLE`/`CONSTANT`/`VALUE`/`@`/`!`
- **Phase 3** — control flow (`IF`/`ELSE`/`THEN`, `BEGIN`/`UNTIL`/`WHILE`/`REPEAT`/`AGAIN`, `DO`/`LOOP`, return stack) + vendor Hayes suite + first scoreboard
- **Phase 4** — strings, `BASE` manipulation, more Core words
- **Phase 5** — Core Extension, File/String word sets, drive to 100% Hayes
- **Phase 6** — inline primitive compile + TCO colon-def endings + JIT cooperation
Within a phase, pick the checkbox that gets the most Hayes tests passing per unit of effort.
Every iteration: implement → test → commit → tick `[ ]` → append Progress log → next.
## Ground rules (hard)
- **Scope:** only `lib/forth/**` and `plans/forth-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, or `lib/` root. Forth primitives go in `lib/forth/runtime.sx`.
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken → Blockers entry, stop.
- **Shared-file issues** → plan's Blockers with minimal repro.
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
- **Worktree:** commit locally. Never push. Never touch `main`.
- **Commit granularity:** one feature per commit.
- **Plan file:** update Progress log + tick boxes every commit.
## Forth-specific gotchas
- **Case-insensitive** — lowercase on lookup.
- Compile mode flag lives on interpreter state; `:` sets, `;` clears.
- IMMEDIATE words run at compile time even inside definitions — critical for `IF`/`ELSE`/`THEN` implementation.
- Return stack is separate from data stack. `>R`/`R>` move between them.
- `BASE` is a user-manipulable variable; number parsing must respect it.
- Colon-def body = SX lambda. Run on the CEK, inherit TCO.
- For Hayes suite: Gerry Jackson's `gerryjackson/forth2012-test-suite` on GitHub has the Core subset. Vendor `src/core.fth` + `src/tester.fr`. Document which source in Progress log.
## General gotchas (all loops)
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious.
- No new planning docs — update `plans/forth-on-sx.md` inline.
- Short, factual commit messages (`forth: DO/LOOP + return stack (+12)`).
- One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first `[ ]`; implement.

View File

@@ -0,0 +1,76 @@
# haskell-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/haskell-on-sx.md` forever. Mini-Haskell 98 with real laziness (SX thunks are first-class). Phases 1-3 are untyped — laziness + ADTs first; HM inference is phase 4.
```
description: haskell-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prompt
You are the sole background agent working `/root/rose-ash/plans/haskell-on-sx.md`. Isolated worktree, forever, one commit per feature. Never push.
**Note:** there's an existing `/root/rose-ash/sx-haskell/` directory (~25 M). Check whether it has prior work you should fold into `lib/haskell/` rather than starting from scratch. Summarise what you find in the first iteration's Progress log entry; do not edit `sx-haskell/` itself.
## Restart baseline — check before iterating
1. Read `plans/haskell-on-sx.md` — roadmap + Progress log.
2. First-run only: peek at `/root/rose-ash/sx-haskell/` — does any of it belong in `lib/haskell/`? Report in Progress log. Don't edit sx-haskell/.
3. `ls lib/haskell/` — pick up from the most advanced file.
4. Run `lib/haskell/tests/*.sx` if they exist. Green before new work.
5. If `lib/haskell/scoreboard.md` exists, that's your baseline.
## The queue
Phase order per `plans/haskell-on-sx.md`:
- **Phase 1** — tokenizer + parser + **layout rule** (indentation-sensitive, painful but required per Haskell 98 §10.3)
- **Phase 2** — desugar + eager eval + ADTs (`data` declarations, constructor tagging, pattern matching). Still untyped.
- **Phase 3** — **laziness**: thunk-wrap every application arg, `force` = WHNF, pattern match forces scrutinee. Classic programs (infinite Fibonacci, sieve of Eratosthenes, quicksort, n-queens, expression calculator) green.
- **Phase 4** — Hindley-Milner type inference (Algorithm W, let-polymorphism, type-sig checking)
- **Phase 5** — typeclasses (dictionary passing, Eq/Ord/Show/Num/Functor/Monad/Applicative, `deriving`)
- **Phase 6** — real `IO` monad backed by `perform`/`resume`, full Prelude, drive corpus to 150+
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → next.
## Ground rules (hard)
- **Scope:** only `lib/haskell/**` and `plans/haskell-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, `lib/` root, or `sx-haskell/`. Haskell primitives go in `lib/haskell/runtime.sx`.
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken → Blockers entry, stop.
- **Shared-file issues** → plan's Blockers with minimal repro.
- **SX thunks** (`make-thunk`, force on use) are already in the trampolining evaluator — reuse. Don't invent your own thunk type.
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
- **Worktree:** commit locally. Never push. Never touch `main`.
- **Commit granularity:** one feature per commit.
- **Plan file:** update Progress log + tick boxes every commit.
## Haskell-specific gotchas
- **Layout rule is the hard bit of parsing** — you need a lexer-parser feedback loop that inserts virtual `{`, `;`, `}` based on indentation. Budget proportionally.
- **Every application arg is a thunk** — compiling `f x y` to `(f (thunk x) (thunk y))` not `(f x y)`. Pattern-match forces.
- **ADT representation:** tagged list, e.g. `data Maybe a = Nothing | Just a` → constructors are `(:Nothing)` (0-ary) and `(:Just <thunk>)` (1-ary). Pattern match on the head symbol.
- **Let-polymorphism** (phase 4): generalise at let-binding boundaries only, not at lambda.
- **Typeclass dictionaries** (phase 5): each class is a record type; each instance builds the record; method call = project + apply.
- **`IO`** (phase 6): internally `World -> (a, World)` but in practice backed by `perform`/`resume` for real side effects. Desugar `do`-notation to `>>=`.
- **Out of scope:** GHC extensions. No `DataKinds`, `GADTs`, `TypeFamilies`, `TemplateHaskell`. Stick to Haskell 98.
## General gotchas (all loops)
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr.
- `type-of` on user fn returns `"lambda"`.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious.
- No new planning docs — update `plans/haskell-on-sx.md` inline.
- Short, factual commit messages (`haskell: layout rule + first parse (+10)`).
- One feature per iteration. Commit. Log. Next.
Go. Read the plan; (first run only) peek at sx-haskell/ and report; find first `[ ]`; implement.

View File

@@ -0,0 +1,75 @@
# lua-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/lua-on-sx.md` forever. One feature per commit. Scoreboard-driven once phase 3 is done: PUC-Rio 5.1.5 Core pass-rate is the north star.
```
description: lua-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prompt
You are the sole background agent working `/root/rose-ash/plans/lua-on-sx.md`. You run in an isolated git worktree. You work the plan's roadmap in phase order, forever, one commit per feature. You never push.
## Restart baseline — check before iterating
1. Read `plans/lua-on-sx.md` — the roadmap tells you what's done (`[x]`) and what's next (`[ ]`). The Progress log (newest-first) is your memory across crashes.
2. `ls lib/lua/` — pick up from the most advanced file that exists.
3. `bash lib/lua/conformance.sh` if it exists — current scoreboard is your starting number. If not yet created, that's phase 3 work.
4. If `lib/lua/tests/` exists, run the SX unit tests. They must be green before you start new work.
## The queue
Work in phase order per `plans/lua-on-sx.md`. At the top level:
- **Phase 1** — tokenizer + parser (small snippets green first)
- **Phase 2** — transpile arithmetic + control flow
- **Phase 3** — tables + functions + first PUC-Rio scoreboard
- **Phase 4** — metatables + `pcall`/`error` + generic `for`
- **Phase 5** — coroutines via `perform`/`cek-resume` (showcase)
- **Phase 6** — string/math/table/io stdlib
- **Phase 7** — `require`/modules + drive conformance to 100%
Within a phase, pick the checkbox that unlocks the most tests per effort. Once the scoreboard exists, re-read `lib/lua/scoreboard.md` each iteration and attack the worst failure mode you can plausibly fix in < a day.
Every iteration:
1. Implement
2. Add/update tests in `lib/lua/tests/`
3. Re-run conformance if it exists, regenerate scoreboard
4. Commit with a short factual message (e.g. `lua: table constructor hash-part (+17)`)
5. Tick the matching `[ ]``[x]` in `plans/lua-on-sx.md`
6. Append a one-line dated bullet to the plan's Progress log (newest first)
7. Move to next
## Ground rules (hard)
- **Scope:** only `lib/lua/**` and `plans/lua-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/js/**`, `lib/hyperscript/**`, `lib/prolog/**`, `lib/forth/**`, `lib/erlang/**`, `lib/haskell/**`, `lib/stdlib.sx`, or anything in `lib/` root. Lua primitives go in `lib/lua/runtime.sx`.
- **NEVER call `sx_build`.** The 600s agent watchdog will kill you before an OCaml build finishes. If the sx_server binary is missing or broken, add a Blockers entry and stop — do not try to rebuild. Use the binary at `hosts/ocaml/_build/default/bin/sx_server.exe` if present.
- **Shared-file issues** → plan's Blockers section with a minimal repro. Don't fix them.
- **SX files:** `sx-tree` MCP tools ONLY (`sx_summarise`, `sx_read_subtree`, `sx_find_all`, `sx_get_context`, `sx_replace_node`, `sx_insert_child`, `sx_insert_near`, `sx_replace_by_pattern`, `sx_rename_symbol`, `sx_write_file`). Run `sx_validate` after edits. Never `Edit`/`Read`/`Write` on `.sx` files — a hook blocks it.
- **Shell, Python, Markdown, JSON, Lua files:** edit normally.
- **Worktree:** commit locally. Never push. Never touch `main`.
- **Commit granularity:** one feature per commit. If partial, still commit — don't hoard.
- **Tests:** never regress. If a feature needs a larger refactor, split into commits each green.
- **Plan file:** update Progress log + tick boxes every commit. Don't rewrite history.
- **If blocked** for two iterations on the same issue, add to Blockers and move on.
## Gotchas already learned (all loops)
- SX `do` is R7RS iteration — **use `begin`** for multi-expr sequences.
- `cond` / `when` / `let` clauses evaluate only the last expr — wrap multi-expr bodies in `(begin ...)`.
- `type-of` on user fn returns `"lambda"`. `type-of` on builtin returns `"function"`.
- `&rest args` is SX varargs. `make-symbol` builds identifier symbols.
- Shell heredoc `||` gets eaten by bash — escape or use `case`.
- Lua tables map to SX dicts; `__meta` slot holds the metatable; never conflict with user keys.
## Style
- No comments in `.sx` unless non-obvious.
- No new planning docs — update `plans/lua-on-sx.md` inline.
- Short, factual commit messages.
- One feature per iteration. Commit. Log. Next.
Go. Start by reading the plan to find the first unchecked `[ ]`, then implement it.

View File

@@ -0,0 +1,74 @@
# prolog-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/prolog-on-sx.md` forever. Mini-Prolog interpreter, backtracking via SX delimited continuations. One feature per commit.
```
description: prolog-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prompt
You are the sole background agent working `/root/rose-ash/plans/prolog-on-sx.md`. You run in an isolated git worktree. You work the plan's roadmap forever, one commit per feature. You never push.
## Restart baseline — check before iterating
1. Read `plans/prolog-on-sx.md` — roadmap + Progress log tell you where you are.
2. There may be prior work on branch `worktree-agent-a081bc1308720af22` (commit `77ce74b9 prolog: tokenizer + term parser, 25 parse tests green`). If you're on that branch, phase 1 is done. If you're on a fresh branch, start phase 1.
3. `ls lib/prolog/` — pick up from the most advanced file that exists.
4. Run `lib/prolog/tests/*.sx` if they exist. Must be green before new work.
5. If `lib/prolog/scoreboard.md` exists, that's your starting number.
## The queue
Phase order per `plans/prolog-on-sx.md`:
- **Phase 1** — tokenizer + term parser (no operator table) — *may already be done*
- **Phase 2** — unification + trail (isolated module, 30+ tests)
- **Phase 3** — clause DB + DFS solver with delimited-continuation backtracking + cut + 5 classic programs green
- **Phase 4** — operator table + `assert`/`retract` + `findall`/`bagof`/`setof` + `copy_term`/`functor`/`arg`/`=..`
- **Phase 5** — Hyperscript integration (`prolog-query` primitive + DSL)
- **Phase 6** — vendor Hirst's ISO conformance tests, drive scoreboard to 200+
- **Phase 7** — compile clauses to SX (optional speed phase)
Within a phase, pick the checkbox with the best tests-per-effort ratio. Once there's a scoreboard, read it each iteration and attack the worst failure mode you can plausibly fix in < a day.
Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → next.
## Ground rules (hard)
- **Scope:** only `lib/prolog/**` and `plans/prolog-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, or `lib/` root. Prolog primitives go in `lib/prolog/runtime.sx`.
- **NEVER call `sx_build`.** 600s watchdog will kill you before OCaml finishes. If sx_server binary is broken, add Blockers entry and stop.
- **Shared-file issues** → plan's Blockers section with a minimal repro. Don't fix them.
- **Delimited continuations** are in `lib/callcc.sx` + `spec/evaluator.sx` Step 5 (IO suspension via `perform`/`cek-resume`). `sx_summarise` spec/evaluator.sx first — it's 2300+ lines.
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit locally. Never push. Never touch `main`.
- **Commit granularity:** one feature per commit.
- **Plan file:** update Progress log + tick boxes every commit.
- **If blocked** for two iterations on the same issue, add to Blockers and move on.
## Prolog-specific gotchas
- Variables must have mutable binding slots (SX dicts with a ref, or an explicit `make-var` primitive that returns something unifiable).
- Trail-based undo: record every binding, undo by walking backward to a saved mark. Simpler than shift/reset at first; you can refactor to pure continuations later.
- Cut (`!`) needs a cut barrier at the enclosing goal entry. Implement as a continuation prompt tag.
- Classic list cons: pick ONE representation (`(. H T)` vs pair-dict) and document it in the plan's architecture sketch. Don't mix.
- `is/2` evaluates arithmetic on already-ground terms — fail if unbound.
## General gotchas (all loops)
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr.
- `sx_validate` after every structural edit.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious.
- No new planning docs — update `plans/prolog-on-sx.md` inline.
- Short, factual commit messages (`prolog: assert/1 + retract/1 (+8)`).
- One feature per iteration. Commit. Log. Next.
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.

105
plans/erlang-on-sx.md Normal file
View File

@@ -0,0 +1,105 @@
# Erlang-on-SX: actors on delimited continuations
The headline showcase for the SX runtime. Erlang is built around the **one** primitive — lightweight processes with mailboxes and selective receive — that delimited continuations implement natively. Most Erlang implementations ship a whole VM (BEAM) for this; on SX each process is a pair of continuations and the scheduler is ~50 lines of SX.
End-state goal: spawn a million processes, run the classic **ring benchmark**, plus a mini gen_server OTP subset and a test corpus of ~150 programs.
## Scope decisions (defaults — override by editing before we spawn)
- **Syntax:** Erlang/OTP 26 subset. No preprocessor, no parse transforms.
- **Conformance:** not BEAM-compat. "Looks like Erlang, runs like Erlang, not byte-compatible." We care about semantics, not BEAM bug-for-bug.
- **Test corpus:** custom — ring, ping-pong, fibonacci-server, bank-account-server, echo-server, plus ~100 hand-written tests for patterns/guards/BIFs. No ISO Common Test.
- **Binaries:** basic bytes-lists only; full binary pattern matching deferred.
- **Hot code reload, distribution, NIFs:** out of scope entirely.
## Ground rules
- **Scope:** only touch `lib/erlang/**` and `plans/erlang-on-sx.md`. Don't edit `spec/`, `hosts/`, `shared/`, `lib/js/**`, `lib/hyperscript/**`, `lib/lua/**`, `lib/prolog/**`, `lib/forth/**`, `lib/haskell/**`, `lib/stdlib.sx`, or `lib/` root. Erlang primitives go in `lib/erlang/runtime.sx`.
- **SX files:** use `sx-tree` MCP tools only.
- **Commits:** one feature per commit. Keep `## Progress log` updated and tick roadmap boxes.
## Architecture sketch
```
Erlang source
lib/erlang/tokenizer.sx — atoms, vars, tuples, lists, binaries, operators
lib/erlang/parser.sx — AST: modules, functions with clauses, patterns, guards
lib/erlang/transpile.sx — AST → SX AST (entry: erlang-eval-ast)
lib/erlang/runtime.sx — scheduler, processes, mailboxes, BIFs
```
Core mapping:
- **Process** = pair of delimited continuations (`on-receive`, `on-resume`) + mailbox list + pid + links
- **Scheduler** = round-robin list of runnable processes; cooperative yield on `receive`
- **`spawn`** = push a new process record, return its pid
- **`send`** = append to target mailbox; if target is blocked on receive, resume its continuation
- **`receive`** = selective — scan mailbox for first matching clause; if none, `perform` a suspend with the receive pattern; scheduler resumes when a matching message arrives
- **Pattern matching** = SX `case` on tagged values; vars bind on match
- **Guards** = side-effect-free predicate evaluated after unification
- **Immutable data** = native
- **Links / monitors / exit signals** = additional process-record fields, scheduler fires exit signals on death
## Roadmap
### Phase 1 — tokenizer + parser
- [ ] Tokenizer: atoms (bare + single-quoted), variables (Uppercase/`_`-prefixed), numbers (int, float, `16#HEX`), strings `"..."`, chars `$c`, punct `( ) { } [ ] , ; . : :: ->`
- [ ] Parser: module declarations, `-module`/`-export`/`-import` attributes, function clauses with head patterns + guards + body
- [ ] Expressions: literals, vars, calls, tuples `{...}`, lists `[...|...]`, binaries `<<...>>`, `if`, `case`, `receive`, `fun`, `try/catch`, operators
- [ ] Unit tests in `lib/erlang/tests/parse.sx`
### Phase 2 — sequential eval + pattern matching + BIFs
- [ ] `erlang-eval-ast`: evaluate sequential expressions
- [ ] Pattern matching (atoms, numbers, vars, tuples, lists, `[H|T]`, underscore, bound-var re-match)
- [ ] Guards: `is_integer`, `is_atom`, `is_list`, `is_tuple`, comparisons, arithmetic
- [ ] BIFs: `length/1`, `hd/1`, `tl/1`, `element/2`, `tuple_size/1`, `atom_to_list/1`, `list_to_atom/1`, `lists:map/2`, `lists:foldl/3`, `lists:reverse/1`, `io:format/1-2`
- [ ] 30+ tests in `lib/erlang/tests/eval.sx`
### Phase 3 — processes + mailboxes + receive (THE SHOWCASE)
- [ ] Scheduler in `runtime.sx`: runnable queue, pid counter, per-process state record
- [ ] `spawn/1`, `spawn/3`, `self/0`
- [ ] `!` (send), `receive ... end` with selective pattern matching
- [ ] `receive ... after Ms -> ...` timeout clause (use SX timer primitive)
- [ ] `exit/1`, basic process termination
- [ ] Classic programs in `lib/erlang/tests/programs/`:
- [ ] `ring.erl` — N processes in a ring, pass a token around M times
- [ ] `ping_pong.erl` — two processes exchanging messages
- [ ] `bank.erl` — account server (deposit/withdraw/balance)
- [ ] `echo.erl` — minimal server
- [ ] `fib_server.erl` — compute fib on request
- [ ] `lib/erlang/conformance.sh` + runner, `scoreboard.json` + `scoreboard.md`
- [ ] Target: 5/5 classic programs + 1M-process ring benchmark runs
### Phase 4 — links, monitors, exit signals
- [ ] `link/1`, `unlink/1`, `monitor/2`, `demonitor/1`
- [ ] Exit-signal propagation; trap_exit flag
- [ ] `try/catch/of/end`
### Phase 5 — modules + OTP-lite
- [ ] `-module(M).` loading, `M:F(...)` calls across modules
- [ ] `gen_server` behaviour (the big OTP win)
- [ ] `supervisor` (simple one-for-one)
- [ ] Registered processes: `register/2`, `whereis/1`
### Phase 6 — the rest
- [ ] List comprehensions `[X*2 || X <- L]`
- [ ] Binary pattern matching `<<A:8, B:16>>`
- [ ] ETS-lite (in-memory tables via SX dicts)
- [ ] More BIFs — target 200+ test corpus green
## Progress log
_Newest first._
- _(not started)_
## Blockers
- _(none yet)_

106
plans/forth-on-sx.md Normal file
View File

@@ -0,0 +1,106 @@
# Forth-on-SX: stack language on the VM
The smallest serious second language — Forth's stack-based semantics map directly onto the SX bytecode VM (OP_DUP, OP_SWAP, OP_DROP already exist as arithmetic primitives or can be added trivially). Compile-mode / interpret-mode is the one genuinely novel piece, but it's a classic technique and small.
End-state goal: **passes John Hayes' ANS-Forth test suite** (the canonical Forth conformance harness — small, well-documented, targets the Core word set).
## Scope decisions (defaults — override)
- **Standard:** ANS-Forth 1994 Core word set + Core Extension. No ANS-Forth Optional word sets (File Access, Floating Point, Search Order, etc.) in the first run.
- **Test suite:** John Hayes' "Test Suite for ANS Forth" (~250 tests, public domain, widely used).
- **Case-sensitivity:** case-insensitive (ANS default).
- **Number base:** support `BASE` variable, defaults to 10. Hex and binary literals (`$FF`, `%1010`) per standard.
## Ground rules
- **Scope:** only touch `lib/forth/**` and `plans/forth-on-sx.md`. No edits to `spec/`, `hosts/`, `shared/`, or other language dirs.
- **SX files:** use `sx-tree` MCP tools only.
- **Architecture:** reader (not tokenizer — Forth is whitespace-delimited) → interpreter → dictionary-backed compiler. The compiler emits SX AST (not bytecode directly) so we inherit the VM.
- **Commits:** one feature per commit. Keep `## Progress log` updated.
## Architecture sketch
```
Forth source text
lib/forth/reader.sx — whitespace-split words (that's it — no real tokenizer)
lib/forth/interpreter.sx — interpret mode: look up word in dict, execute
lib/forth/compiler.sx — compile mode (`:` opens, `;` closes): emit SX AST
lib/forth/runtime.sx — stack ops, dictionary, BASE, I/O
existing CEK / VM — runs compiled definitions natively
```
Representation:
- **Stack** = SX list, push = cons, pop = uncons
- **Dictionary** = dict `word-name → {:kind :immediate? :body}` where kind is `:primitive` or `:colon-def`
- **A colon definition** compiles to a thunk `(lambda () <body-as-sx-sequence>)`
- **Compile-mode** is a flag on the interpreter state; `:` sets it, `;` clears and installs the new word
- **IMMEDIATE** words run at compile time
## Roadmap
### Phase 1 — reader + interpret mode
- [ ] `lib/forth/reader.sx`: whitespace-split, number parsing (base-aware)
- [ ] `lib/forth/runtime.sx`: stack as SX list, push/pop/peek helpers
- [ ] Core stack words: `DUP`, `DROP`, `SWAP`, `OVER`, `ROT`, `NIP`, `TUCK`, `PICK`, `ROLL`, `?DUP`, `2DUP`, `2DROP`, `2SWAP`, `2OVER`
- [ ] Arithmetic: `+`, `-`, `*`, `/`, `MOD`, `/MOD`, `NEGATE`, `ABS`, `MIN`, `MAX`, `1+`, `1-`, `2*`, `2/`
- [ ] Comparison: `=`, `<`, `>`, `<=`, `>=`, `0=`, `0<`, `0>`
- [ ] Logical: `AND`, `OR`, `XOR`, `INVERT`
- [ ] I/O: `.` (print), `.S` (show stack), `EMIT`, `CR`, `SPACE`, `SPACES`
- [ ] Interpreter loop: read word, look up, execute, repeat
- [ ] Unit tests in `lib/forth/tests/interp.sx`
### Phase 2 — colon definitions + compile mode
- [ ] `:` opens compile mode and starts a definition
- [ ] `;` closes it and installs into the dictionary
- [ ] Compile mode: non-IMMEDIATE words get appended as SX references; numbers get compiled as literals; IMMEDIATE words (like `IF`) run now
- [ ] `VARIABLE`, `CONSTANT`, `VALUE`, `TO`
- [ ] `@` (fetch), `!` (store), `+!`
- [ ] Compile a colon def into an SX lambda that the CEK runs directly
- [ ] Tests: define words, call them, nest definitions
### Phase 3 — control flow + first Hayes tests green
- [ ] `IF`, `ELSE`, `THEN` — compile to SX `if`
- [ ] `BEGIN`, `UNTIL`, `WHILE`, `REPEAT`, `AGAIN` — compile to loops
- [ ] `DO`, `LOOP`, `+LOOP`, `I`, `J`, `LEAVE` — counted loops (needs a return stack)
- [ ] Return stack: `>R`, `R>`, `R@`, `2>R`, `2R>`, `2R@`
- [ ] Vendor John Hayes' test suite to `lib/forth/ans-tests/`
- [ ] `lib/forth/conformance.sh` + runner; `scoreboard.json` + `scoreboard.md`
- [ ] Baseline: probably 30-50% Core passing after phase 3
### Phase 4 — strings + more Core
- [ ] `S"`, `C"`, `."`, `TYPE`, `COUNT`, `CMOVE`, `FILL`, `BLANK`
- [ ] `CHAR`, `[CHAR]`, `KEY`, `ACCEPT`
- [ ] `BASE` manipulation: `DECIMAL`, `HEX`
- [ ] `DEPTH`, `SP@`, `SP!`
- [ ] Drive Hayes Core pass-rate up
### Phase 5 — Core Extension + optional word sets
- [ ] Full Core + Core Extension
- [ ] File Access word set (via SX IO)
- [ ] String word set (`SLITERAL`, `COMPARE`, `SEARCH`)
- [ ] Target: 100% Hayes Core
### Phase 6 — speed
- [ ] Inline primitive calls during compile (skip dict lookup)
- [ ] Tail-call optimise colon-def endings
- [ ] JIT cooperation: mark compiled colon-defs as VM-eligible
## Progress log
_Newest first._
- _(not started)_
## Blockers
- _(none yet)_

114
plans/haskell-on-sx.md Normal file
View File

@@ -0,0 +1,114 @@
# Haskell-on-SX: mini-Haskell with real laziness
Mini-Haskell is the research-paper-worthy demo. Laziness is native to the SX runtime (thunks are already a first-class type); algebraic data types map onto tagged lists; typeclasses map onto dictionary passing; IO maps onto `perform`/`resume`. Hindley-Milner inference is the one real piece of new work.
End-state goal: a **Haskell 98 subset** that runs the small classic programs (sieve of Eratosthenes lazy stream, fibonacci as infinite list, naive quicksort, n-queens, expression evaluator) plus a ~150-test corpus.
## Scope decisions (defaults — override)
- **Standard:** Haskell 98 subset. No GHC extensions (no `DataKinds`, no `GADTs`, no `TypeFamilies`, no `TemplateHaskell`).
- **Phase 1-3 are untyped** — we get the evaluator right first with laziness + ADTs, then add HM inference in phase 4. This is deliberate: typing is the hard bit and will take a full phase on its own.
- **Typeclasses:** dictionary passing, no overlap, no orphan instances. Added in phase 5.
- **Layout rule:** yes — phase 1 implements Haskell's indentation-sensitive parsing (painful but required).
- **Test corpus:** custom. No GHC test suite. Bundle classic programs + ~100 hand-written expression-level tests + mini Prelude tests.
## Ground rules
- **Scope:** only `lib/haskell/**` and `plans/haskell-on-sx.md`. No edits to `spec/`, `hosts/`, `shared/`, or other language dirs.
- **SX files:** `sx-tree` MCP tools only.
- **Architecture:** Haskell source → AST → desugared-core → SX AST → CEK. Thunks on the SX side provide laziness natively.
- **Commits:** one feature per commit. Keep `## Progress log` updated.
## Architecture sketch
```
Haskell source
lib/haskell/tokenizer.sx — idents, operators, layout-sensitive indentation
lib/haskell/parser.sx — AST: modules, data decls, type sigs, fn clauses, expressions
lib/haskell/desugar.sx — surface → core: case-of-case, do-notation, list comp, guards
lib/haskell/transpile.sx — core → SX AST, wrapping everything in thunks for laziness
lib/haskell/runtime.sx — force, ADT constructors, Prelude, typeclass dicts (phase 5+)
existing CEK / VM
```
Key mappings:
- **Laziness** = every function argument is an SX thunk; `force` is WHNF reduction. SX already has `make-thunk` from the trampolining evaluator — we reuse it.
- **Pattern match** = forces the scrutinee to WHNF, then structural match on the tag
- **ADT** = `data Maybe a = Nothing | Just a` compiles to tagged lists: `(:Nothing)` and `(:Just <thunk>)`
- **Typeclass** = each class becomes a record type; each instance becomes a record value; each method becomes a projection; the elaborator inserts the dict at each call site (phase 5)
- **IO** = `IO a` is a function `World -> (a, World)` internally; in practice uses `perform`/`resume` for actual side effects
- **Layout** = offside rule; inserted virtual braces + semis during a lexer-parser feedback pass
## Roadmap
### Phase 1 — tokenizer + parser + layout rule
- [ ] Tokenizer: reserved words, qualified names, operators, numbers (int, float, Rational later), chars/strings, comments (`--` and `{-` nested)
- [ ] Layout algorithm: turn indentation into virtual `{`, `;`, `}` tokens per Haskell 98 §10.3
- [ ] Parser: modules, imports (stub), top-level decls, type sigs, function clauses with patterns + guards + where-clauses, expressions with operator precedence, lambdas, `let`, `if`, `case`, `do`, list comp, sections
- [ ] AST design modelled on GHC's HsSyn at a surface level
- [ ] Unit tests in `lib/haskell/tests/parse.sx`
### Phase 2 — desugar + eager-ish eval + ADTs (untyped)
- [ ] Desugar: guards → nested `if`s; `where``let`; list comp → `concatMap`-based; do-notation stays for now (desugared in phase 3)
- [ ] `data` declarations register constructors in runtime
- [ ] Pattern match (tag-based, value-level): atoms, vars, wildcards, constructor patterns, `as` patterns, nested
- [ ] Evaluator (still strict internally — laziness in phase 3): `let`, `lambda`, application, `case`, literals, constructors
- [ ] 30+ eval tests in `lib/haskell/tests/eval.sx`
### Phase 3 — laziness + classic programs
- [ ] Transpile to thunk-wrapped SX: every application arg becomes `(make-thunk (lambda () <arg>))`
- [ ] `force` = SX eval-thunk-to-WHNF primitive
- [ ] Pattern match forces scrutinee before matching
- [ ] Infinite structures: `repeat x`, `iterate f x`, `[1..]`, Fibonacci stream, sieve of Eratosthenes
- [ ] `seq`, `deepseq` from Prelude
- [ ] Do-notation for a stub `IO` monad (just threading, no real side effects yet)
- [ ] Classic programs in `lib/haskell/tests/programs/`:
- [ ] `fib.hs` — infinite Fibonacci stream
- [ ] `sieve.hs` — lazy sieve of Eratosthenes
- [ ] `quicksort.hs` — naive QS
- [ ] `nqueens.hs`
- [ ] `calculator.hs` — parser combinator style expression evaluator
- [ ] `lib/haskell/conformance.sh` + runner; `scoreboard.json` + `scoreboard.md`
- [ ] Target: 5/5 classic programs passing
### Phase 4 — Hindley-Milner inference
- [ ] Algorithm W: unification + type schemes + generalisation + instantiation
- [ ] Report type errors with meaningful positions
- [ ] Reject untypeable programs that phase 3 was accepting
- [ ] Type-sig checking: user writes `f :: Int -> Int`; verify
- [ ] Let-polymorphism
- [ ] Unit tests: inference for 50+ expressions
### Phase 5 — typeclasses (dictionary passing)
- [ ] `class` / `instance` declarations
- [ ] Dictionary-passing elaborator: inserts dict args at call sites
- [ ] Standard classes: `Eq`, `Ord`, `Show`, `Num`, `Functor`, `Monad`, `Applicative`
- [ ] `deriving (Eq, Show)` for ADTs
### Phase 6 — real IO + Prelude completion
- [ ] Real `IO` monad backed by `perform`/`resume`
- [ ] `putStrLn`, `getLine`, `readFile`, `writeFile`, `print`
- [ ] Full-ish Prelude: `Maybe`, `Either`, `List` functions, `Map`-lite
- [ ] Drive scoreboard toward 150+ passing
## Progress log
_Newest first._
- _(not started)_
## Blockers
- _(none yet)_

91
plans/lua-on-sx.md Normal file
View File

@@ -0,0 +1,91 @@
# Lua-on-SX: Lua 5.1 on the CEK/VM
Compile Lua 5.1 AST to SX AST; the existing CEK evaluator runs it. Same architecture as `plans/js-on-sx.md` — reuse SX semantics wherever they fit, only shim the Lua-specific parts (tables/metatables, `nil`/`false`-only-falsy, multi-return, coroutines via `perform`/resume).
End-state goal: **100% of PUC-Rio Lua 5.1.5 test suite.** Running as a long-lived background agent that drives the scoreboard up one failure-mode at a time, like `lib/js/`.
## Ground rules
- **Scope:** only touch `lib/lua/**` and `plans/lua-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/js/**`, `lib/hyperscript/**`, `lib/prolog/**`, `lib/stdlib.sx`, or anything in `lib/` root. Lua-specific primitives go in `lib/lua/runtime.sx`.
- **Shared-file issues** go under "Blockers" below with a minimal repro; do not fix from this loop.
- **SX files:** use `sx-tree` MCP tools only (never `Edit`/`Read`/`Write` on `.sx` files). `sx_write_file` for new files, path/pattern edits for changes.
- **Architecture:** Lua source → Lua AST → SX AST → CEK. No standalone Lua evaluator.
- **Commits:** one feature per commit. Keep `## Progress log` updated (dated entries, newest first) and tick boxes in the roadmap.
## Architecture sketch
```
Lua source text
lib/lua/tokenizer.sx — numbers, strings (short + long [[…]]), idents, ops, comments
lib/lua/parser.sx — Lua AST as SX trees, e.g. (lua-for-num i a b c body)
lib/lua/transpile.sx — Lua AST → SX AST (entry: lua-eval-ast)
existing CEK / VM
```
Runtime shims in `lib/lua/runtime.sx`: `lua-truthy?`, string coercion for `..`/arithmetic, table ops (array + hash part), metatable dispatch, `pcall`/`error` bridge, `string`/`math`/`table` libs.
## Roadmap
Each item: implement → tests → tick box → update progress log.
### Phase 1 — tokenizer + parser
- [ ] Tokenizer: numbers (int, float, hex), strings (short + long `[[…]]`), idents, keywords, operators, comments (`--`, `--[[…]]`)
- [ ] Parser: blocks, `local`, `if/elseif/else/end`, `while`, numeric `for`, `function`, `return`, expressions, table constructors, indexing (`.`, `[]`), calls (`f(…)`, `f:m(…)`)
- [ ] Skip for phase 1: generic `for … in …`, goto/labels, nested varargs `...`
- [ ] Unit tests in `lib/lua/tests/parse.sx`: source → expected AST
### Phase 2 — transpile: control flow + arithmetic
- [ ] `lua-eval-ast` entry
- [ ] Arithmetic (Lua 5.1 semantics — `/` is float)
- [ ] Comparison + logical (short-circuit, Lua truthy)
- [ ] `..` concat with string/number coercion
- [ ] `if`, `while`, numeric `for`, `local`, assignment, blocks
- [ ] 30+ eval tests in `lib/lua/tests/eval.sx`
### Phase 3 — tables + functions + first PUC-Rio slice
- [ ] `function` (anon, local, top-level), closures
- [ ] Multi-return: return as list, unpack at call sites
- [ ] Table constructors (array + hash + computed keys)
- [ ] Raw table access `t.k` / `t[k]` (no metatables yet)
- [ ] Vendor PUC-Rio 5.1.5 suite to `lib/lua/lua-tests/` (just `.lua` files)
- [ ] `lib/lua/conformance.sh` + Python runner (model on `lib/js/test262-runner.py`)
- [ ] `scoreboard.json` + `scoreboard.md` baseline
### Phase 4 — metatables + error handling (next run)
- [ ] Metatable dispatch: `__index`, `__newindex`, `__add`/`__sub`/…, `__eq`, `__lt`, `__call`, `__tostring`, `__len`
- [ ] `pcall`/`xpcall`/`error` via handler-bind
- [ ] Generic `for … in …`
### Phase 5 — coroutines (the showcase)
- [ ] `coroutine.create`/`.resume`/`.yield`/`.status`/`.wrap` via `perform`/`cek-resume`
### Phase 6 — standard library
- [ ] `string``format`, `sub`, `find`, `match`, `gmatch`, `gsub`, `len`, `rep`, `upper`, `lower`, `byte`, `char`
- [ ] `math` — full surface
- [ ] `table``insert`, `remove`, `concat`, `sort`, `unpack`
- [ ] `io` — minimal stub (read/write to SX IO surface)
- [ ] `os` — time/date subset
### Phase 7 — modules + full conformance
- [ ] `require` / `package` via SX `define-library`/`import`
- [ ] Drive PUC-Rio scoreboard to 100%
## Progress log
_Newest first. Agent appends on every commit._
- _(awaiting phase 1)_
## Blockers
_Shared-file issues that need someone else to fix. Minimal repro only._
- _(none yet)_

97
plans/prolog-on-sx.md Normal file
View File

@@ -0,0 +1,97 @@
# Prolog-on-SX: mini-Prolog interpreter on delimited continuations
Horn clauses + unification + cut + arithmetic, implemented as an **interpreter** (clauses live as SX data, a SX-implemented solver walks them). Backtracking is powered by the delimited-continuations machinery in `lib/callcc.sx` + `spec/evaluator.sx` Step 5 — this is the reason Prolog fits SX well.
End-state goal: **200+ tests passing** (classic programs + Hirst's ISO conformance subset + Hyperscript integration suite). Long-lived background agent driving the scoreboard up.
## Ground rules
- **Scope:** only touch `lib/prolog/**` and `plans/prolog-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/js/**`, `lib/hyperscript/**`, `lib/lua/**`, `lib/stdlib.sx`, or anything in `lib/` root. Prolog primitives go in `lib/prolog/runtime.sx`.
- **Shared-file issues** go under "Blockers" below with a minimal repro; do not fix from this loop.
- **SX files:** use `sx-tree` MCP tools only.
- **Architecture:** Prolog source → term AST → clause DB. Solver is SX code walking the DB; backtracking via delimited continuations, not a separate trail machine.
- **Commits:** one feature per commit. Keep `## Progress log` updated and tick boxes.
## Architecture sketch
```
Prolog source text
lib/prolog/tokenizer.sx — atoms, vars, numbers, punct, comments
lib/prolog/parser.sx — term AST; phase 1: f(a,b) syntax only, no operator table
lib/prolog/runtime.sx — clause DB, unify!, trail, solver (DFS + delimited-cont backtracking)
│ built-ins: =/2, \=/2, !/0, is/2, call/1, findall/3, …
solutions / side-effects
```
Representation choices (finalise in phase 1, document here):
- **Term:** nested SX list. Compound `(functor arg1 arg2)`. Atom = symbol. Number = number. Variable = `{:var "X" :binding <ref>}` with mutable binding slot.
- **List:** cons-cell compound `(. H T)` or similar. `[1,2,3]` sugar desugared at parse.
- **Clause:** `{:head <term> :body <term>}` where body is the conjunction goal.
- **Clause DB:** dict `"functor/arity" → list of clauses`.
## Roadmap
### Phase 1 — tokenizer + term parser (no operator table)
- [ ] Tokenizer: atoms (lowercase/quoted), variables (uppercase/`_`), numbers, strings, punct `( ) , . [ ] | ! :-`, comments (`%`, `/* */`)
- [ ] Parser: clauses `head :- body.` and facts `head.`; terms `atom | Var | number | compound(args) | [list,sugar]`
- [ ] **Skip for phase 1:** operator table. `X is Y + 1` must be written `is(X, '+'(Y, 1))`; `=` written `=(X, Y)`. Operators land in phase 4.
- [ ] Unit tests in `lib/prolog/tests/parse.sx`
### Phase 2 — unification + trail
- [ ] `make-var`, `walk` (follow binding chain), `prolog-unify!` (terms + trail → bool), `trail-undo-to!`
- [ ] Occurs-check off by default, exposed as flag
- [ ] 30+ unification tests in `lib/prolog/tests/unify.sx`: atoms, vars, compounds, lists, cyclic (no-occurs-check), mutual occurs
### Phase 3 — clause DB + DFS solver + cut + first classic programs
- [ ] Clause DB: `"functor/arity" → list-of-clauses`, loader inserts
- [ ] Solver: DFS with choice points backed by delimited continuations (`lib/callcc.sx`). On goal entry, capture; per matching clause, unify head + recurse body; on failure, undo trail, try next
- [ ] Cut (`!`): cut barrier at current choice-point frame; collapse all up to barrier
- [ ] Built-ins: `=/2`, `\\=/2`, `true/0`, `fail/0`, `!/0`, `,/2`, `;/2`, `->/2` inside `;`, `call/1`, `write/1`, `nl/0`
- [ ] Arithmetic `is/2` with `+ - * / mod abs`
- [ ] Classic programs in `lib/prolog/tests/programs/`:
- [ ] `append.pl` — list append (with backtracking)
- [ ] `reverse.pl` — naive reverse
- [ ] `member.pl` — generate all solutions via backtracking
- [ ] `nqueens.pl` — 8-queens
- [ ] `family.pl` — facts + rules (parent/ancestor)
- [ ] `lib/prolog/conformance.sh` + runner, `scoreboard.json` + `scoreboard.md`
- [ ] Target: all 5 classic programs passing
### Phase 4 — operator table + more built-ins (next run)
- [ ] Operator table parsing (prefix/infix/postfix, precedence, assoc)
- [ ] `assert/1`, `asserta/1`, `assertz/1`, `retract/1`
- [ ] `findall/3`, `bagof/3`, `setof/3`
- [ ] `copy_term/2`, `functor/3`, `arg/3`, `=../2`
- [ ] String/atom predicates
### Phase 5 — Hyperscript integration
- [ ] `prolog-query` primitive callable from SX/Hyperscript
- [ ] Hyperscript DSL: `when allowed(user, :edit) then …`
- [ ] Integration suite
### Phase 6 — ISO conformance
- [ ] Vendor Hirst's conformance tests
- [ ] Drive scoreboard to 200+
### Phase 7 — compiler (later, optional)
- [ ] Compile clauses to SX continuations for speed
- [ ] Keep interpreter as the reference
## Progress log
_Newest first. Agent appends on every commit._
- _(awaiting phase 1)_
## Blockers
_Shared-file issues that need someone else to fix. Minimal repro only._
- _(none yet)_

101
plans/restore-all.sh Executable file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# restore-all.sh — snapshot recovery state for every language loop.
#
# Shows: per-language branch, recent commits, test/scoreboard status, plan progress.
# Points at the briefing file for each language. To respawn a loop, paste the
# briefing into Claude via the Agent tool with run_in_background=true and
# isolation=worktree.
#
# Usage:
# bash plans/restore-all.sh # status for all languages
# bash plans/restore-all.sh <lang> # one language (js|hs|lua|prolog|forth|erlang|haskell)
# bash plans/restore-all.sh --print # also cat each briefing
#
set -uo pipefail
cd "$(dirname "$0")/.."
LANGS=(js hs lua prolog forth erlang haskell)
FILTER="${1:-}"
PRINT=""
if [ "$FILTER" = "--print" ]; then PRINT="yes"; FILTER=""; fi
snap_one() {
local lang="$1"
local plan
local dir
local scoreboard
local tests_cmd
local briefing
case "$lang" in
js) plan="plans/js-on-sx.md"; dir="lib/js"; scoreboard="lib/js/test262-scoreboard.md"; briefing="plans/agent-briefings/loop.md"; tests_cmd="bash lib/js/test.sh";;
hs) plan="plans/hs-conformance-to-100.md"; dir="lib/hyperscript"; scoreboard="plans/hs-conformance-scoreboard.md"; briefing="plans/agent-briefings/hs-loop.md"; tests_cmd="bash lib/hyperscript/test.sh";;
lua) plan="plans/lua-on-sx.md"; dir="lib/lua"; scoreboard="lib/lua/scoreboard.md"; briefing="plans/agent-briefings/lua-loop.md"; tests_cmd="bash lib/lua/conformance.sh";;
prolog) plan="plans/prolog-on-sx.md"; dir="lib/prolog"; scoreboard="lib/prolog/scoreboard.md"; briefing="plans/agent-briefings/prolog-loop.md"; tests_cmd="bash lib/prolog/conformance.sh";;
forth) plan="plans/forth-on-sx.md"; dir="lib/forth"; scoreboard="lib/forth/scoreboard.md"; briefing="plans/agent-briefings/forth-loop.md"; tests_cmd="bash lib/forth/conformance.sh";;
erlang) plan="plans/erlang-on-sx.md"; dir="lib/erlang"; scoreboard="lib/erlang/scoreboard.md"; briefing="plans/agent-briefings/erlang-loop.md"; tests_cmd="bash lib/erlang/conformance.sh";;
haskell) plan="plans/haskell-on-sx.md"; dir="lib/haskell"; scoreboard="lib/haskell/scoreboard.md"; briefing="plans/agent-briefings/haskell-loop.md"; tests_cmd="bash lib/haskell/conformance.sh";;
*) echo "unknown lang: $lang"; return 1;;
esac
echo "=== $lang ==="
if [ -f "$plan" ]; then
echo "plan: $plan"
else
echo "plan: (missing)"
fi
if [ -f "$briefing" ]; then
echo "briefing: $briefing"
else
echo "briefing: (missing — cannot respawn)"
fi
if [ -d "$dir" ]; then
echo "dir: $dir ($(find "$dir" -type f 2>/dev/null | wc -l) files)"
else
echo "dir: $dir (does not exist yet)"
fi
if [ -f "$scoreboard" ]; then
echo "scoreboard: $scoreboard"
head -3 "$scoreboard" | sed 's/^/ /'
else
echo "scoreboard: (not yet generated)"
fi
echo "recent commits (vs main):"
git log --oneline "main..HEAD" -- "$dir" "$plan" 2>/dev/null | head -5 | sed 's/^/ /' || echo " (none)"
echo
}
echo "=== environment ==="
echo "branch: $(git rev-parse --abbrev-ref HEAD)"
echo "HEAD: $(git log -1 --oneline)"
if [ -x hosts/ocaml/_build/default/bin/sx_server.exe ]; then
echo "sx_server: present"
else
echo "sx_server: MISSING — build with ./scripts/sx-build-all.sh before spawning any agent"
fi
echo "worktrees: $(git worktree list | wc -l)"
echo
if [ -n "$FILTER" ]; then
snap_one "$FILTER"
else
for l in "${LANGS[@]}"; do snap_one "$l"; done
fi
echo "=== to spawn a loop ==="
echo "Paste the target briefing into Claude via the Agent tool with"
echo " run_in_background: true"
echo " isolation: worktree"
echo "The briefing is self-contained; the agent reads the plan's Progress log"
echo "and picks up from wherever the last commit left off."
if [ "$PRINT" = "yes" ]; then
echo
echo "=== all briefings ==="
for f in plans/agent-briefings/*.md; do
echo
echo "---- $f ----"
cat "$f"
done
fi