diff --git a/plans/agent-briefings/erlang-loop.md b/plans/agent-briefings/erlang-loop.md new file mode 100644 index 00000000..8b5d8b24 --- /dev/null +++ b/plans/agent-briefings/erlang-loop.md @@ -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//` 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. diff --git a/plans/agent-briefings/forth-loop.md b/plans/agent-briefings/forth-loop.md new file mode 100644 index 00000000..2106bb2e --- /dev/null +++ b/plans/agent-briefings/forth-loop.md @@ -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//` 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. diff --git a/plans/agent-briefings/haskell-loop.md b/plans/agent-briefings/haskell-loop.md new file mode 100644 index 00000000..66e46c18 --- /dev/null +++ b/plans/agent-briefings/haskell-loop.md @@ -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//` 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 )` (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. diff --git a/plans/agent-briefings/lua-loop.md b/plans/agent-briefings/lua-loop.md new file mode 100644 index 00000000..662e3ac6 --- /dev/null +++ b/plans/agent-briefings/lua-loop.md @@ -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. diff --git a/plans/agent-briefings/prolog-loop.md b/plans/agent-briefings/prolog-loop.md new file mode 100644 index 00000000..8a72157f --- /dev/null +++ b/plans/agent-briefings/prolog-loop.md @@ -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//` 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. diff --git a/plans/erlang-on-sx.md b/plans/erlang-on-sx.md new file mode 100644 index 00000000..f3faacf2 --- /dev/null +++ b/plans/erlang-on-sx.md @@ -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 `<>` +- [ ] ETS-lite (in-memory tables via SX dicts) +- [ ] More BIFs — target 200+ test corpus green + +## Progress log + +_Newest first._ + +- _(not started)_ + +## Blockers + +- _(none yet)_ diff --git a/plans/forth-on-sx.md b/plans/forth-on-sx.md new file mode 100644 index 00000000..9bdeface --- /dev/null +++ b/plans/forth-on-sx.md @@ -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 () )` +- **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)_ diff --git a/plans/haskell-on-sx.md b/plans/haskell-on-sx.md new file mode 100644 index 00000000..8396d527 --- /dev/null +++ b/plans/haskell-on-sx.md @@ -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 )` +- **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 () ))` +- [ ] `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)_ diff --git a/plans/lua-on-sx.md b/plans/lua-on-sx.md new file mode 100644 index 00000000..04a95755 --- /dev/null +++ b/plans/lua-on-sx.md @@ -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)_ diff --git a/plans/prolog-on-sx.md b/plans/prolog-on-sx.md new file mode 100644 index 00000000..45b0cf59 --- /dev/null +++ b/plans/prolog-on-sx.md @@ -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 }` with mutable binding slot. +- **List:** cons-cell compound `(. H T)` or similar. `[1,2,3]` sugar desugared at parse. +- **Clause:** `{:head :body }` 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)_ diff --git a/plans/restore-all.sh b/plans/restore-all.sh new file mode 100755 index 00000000..8333ed9d --- /dev/null +++ b/plans/restore-all.sh @@ -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 # 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