All 10 vector primitives now have :as type annotations on every parameter, :returns types, and :doc strings. make-vector gains optional fill annotation; vector uses :rest for its variadic args; vector-ref/set! document bounds error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
204 lines
9.5 KiB
Markdown
204 lines
9.5 KiB
Markdown
# SX Primitives — Meta-Loop Briefing
|
|
|
|
Goal: add fundamental missing SX primitives in sequence, then sweep all language
|
|
implementations to replace their workarounds. Full rationale: vectors fix O(n) array
|
|
access across every language; numeric tower fixes float/int conflation; dynamic-wind
|
|
fixes cleanup semantics; coroutine primitive unifies Ruby/Lua/Tcl; string buffer fixes
|
|
O(n²) concat; algebraic data types eliminate the tagged-dict pattern everywhere.
|
|
|
|
**Each fire: find the first unchecked `[ ]`, do it, commit, tick it, stop.**
|
|
Sub-items within a Phase may span multiple fires — just commit progress and tick what's done.
|
|
|
|
---
|
|
|
|
## Phase 0 — Prep (gate)
|
|
|
|
- [x] Stop new-language loops: send `/exit` to sx-loops windows for the four blank-slate
|
|
languages that haven't committed workarounds yet:
|
|
```
|
|
tmux send-keys -t sx-loops:common-lisp "/exit" Enter
|
|
tmux send-keys -t sx-loops:apl "/exit" Enter
|
|
tmux send-keys -t sx-loops:ruby "/exit" Enter
|
|
tmux send-keys -t sx-loops:tcl "/exit" Enter
|
|
```
|
|
Verify all four windows are idle (claude prompt, no active task).
|
|
|
|
- [x] E38 + E39 landed: check both Bucket-E branches for implementation commits.
|
|
```
|
|
git log --oneline hs-e38-sourceinfo | head -5
|
|
git log --oneline hs-e39-webworker | head -5
|
|
```
|
|
If either branch has only its base commit (no impl work yet): note "pending" and stop —
|
|
next fire re-checks. Proceed only when both have at least one implementation commit.
|
|
|
|
---
|
|
|
|
## Phase 1 — Vectors
|
|
|
|
Native mutable integer-indexed arrays. Fix: Lua O(n) sort, APL rank polymorphism, Ruby
|
|
Array, Tcl lists, Common Lisp vectors, all using string-keyed dicts today.
|
|
|
|
Primitives to add:
|
|
- `make-vector` `n` `[fill]` → vector of length n
|
|
- `vector?` `v` → bool
|
|
- `vector-ref` `v` `i` → element at index i (0-based)
|
|
- `vector-set!` `v` `i` `x` → mutate in place
|
|
- `vector-length` `v` → integer
|
|
- `vector->list` `v` → list
|
|
- `list->vector` `lst` → vector
|
|
- `vector-fill!` `v` `x` → fill all elements
|
|
- `vector-copy` `v` `[start]` `[end]` → fresh copy of slice
|
|
|
|
Steps:
|
|
- [x] OCaml: add `SxVector of value array` to `hosts/ocaml/sx_types.ml`; implement all
|
|
primitives in `hosts/ocaml/sx_primitives.ml` (or equivalent); wire into evaluator.
|
|
Note: Vector type + most prims were already present; added bounds-checked vector-ref/set!
|
|
and optional start/end to vector-copy. 10/10 vector tests pass (r7rs suite).
|
|
- [x] Spec: add vector entries to `spec/primitives.sx` with type signatures and descriptions.
|
|
All 10 vector primitives now have :as type annotations, :returns, and :doc strings.
|
|
make-vector: optional fill param; vector-copy: optional start/end (done prev step).
|
|
- [ ] JS bootstrapper: implement vectors in `hosts/javascript/platform.js` (or equivalent);
|
|
ensure `sx-browser.js` rebuild picks them up.
|
|
- [ ] Tests: 40+ tests in `spec/tests/test-vectors.sx` covering construction, ref, set!,
|
|
length, conversions, fill, copy, bounds behaviour.
|
|
- [ ] Verify: full test suite still passes (`node hosts/javascript/run_tests.js --full`).
|
|
- [ ] Commit: `spec: vector primitive (make-vector/vector-ref/vector-set!/etc)`
|
|
|
|
---
|
|
|
|
## Phase 2 — Numeric tower
|
|
|
|
Float ≠ integer distinction. Fix: Erlang `=:=`, Lua `math.type()`, Haskell `Num`/`Integral`,
|
|
Common Lisp `integerp`/`floatp`/`ratio`, JS `Number.isInteger`.
|
|
|
|
Changes:
|
|
- `parse-number` preserves float identity: `"1.0"` → float 1.0, not integer 1
|
|
- New predicates: `integer?`, `float?`, `exact?`, `inexact?`
|
|
- New coercions: `exact->inexact`, `inexact->exact`
|
|
- Fix `floor`/`ceiling`/`truncate`/`round` to return integers when applied to floats
|
|
- `number->string` renders `1.0` as `"1.0"`, `1` as `"1"`
|
|
- Arithmetic: `(+ 1 1.0)` → `2.0` (float contagion), `(+ 1 1)` → `2` (integer)
|
|
|
|
Steps:
|
|
- [ ] OCaml: distinguish `SxInt of int` / `SxFloat of float` in `sx_types.ml`; update all
|
|
arithmetic primitives for float contagion; fix `parse-number`.
|
|
- [ ] Spec: update `spec/primitives.sx` with new predicates + coercions; document contagion rules.
|
|
- [ ] JS bootstrapper: update number representation and arithmetic.
|
|
- [ ] Tests: 40+ tests in `spec/tests/test-numeric-tower.sx`.
|
|
- [ ] Verify: full suite passes. Pay attention to any test that relied on `1.0 = 1`.
|
|
- [ ] Commit: `spec: numeric tower — float/int distinction + contagion`
|
|
|
|
---
|
|
|
|
## Phase 3 — Dynamic-wind
|
|
|
|
Fix: Common Lisp `unwind-protect`, Ruby `ensure`, JS `finally`, Tcl `catch`+cleanup,
|
|
Erlang `try...after` (currently uses double-nested guard workaround).
|
|
|
|
- [ ] Spec: implement `dynamic-wind` in `spec/evaluator.sx` such that the after-thunk fires
|
|
on both normal return AND non-local exit (raise/call-cc escape). Must compose with
|
|
`guard` — currently they don't interact.
|
|
- [ ] OCaml: wire `dynamic-wind` through the CEK machine with a `WindFrame` continuation.
|
|
- [ ] JS bootstrapper: update.
|
|
- [ ] Tests: 20+ tests covering normal return, raise, call/cc escape, nested dynamic-winds.
|
|
- [ ] Commit: `spec: dynamic-wind + guard integration`
|
|
|
|
---
|
|
|
|
## Phase 4 — Coroutine primitive
|
|
|
|
Unify Ruby fibers, Lua coroutines, Tcl coroutines — all currently reimplemented separately
|
|
using call/cc+perform/resume.
|
|
|
|
- [ ] Spec: add `make-coroutine`, `coroutine-resume`, `coroutine-yield`, `coroutine?`,
|
|
`coroutine-alive?` to `spec/primitives.sx`. Build on existing `perform`/`cek-resume`
|
|
machinery — coroutines ARE perform/resume with a stable identity.
|
|
- [ ] OCaml: implement coroutine type; wire resume/yield through CEK suspension.
|
|
- [ ] JS bootstrapper: update.
|
|
- [ ] Tests: 25+ tests — multi-yield, final return, arg passthrough, alive? predicate,
|
|
nested coroutines, "final return vs yield" distinction (the Lua gotcha).
|
|
- [ ] Commit: `spec: coroutine primitive (make-coroutine/resume/yield)`
|
|
|
|
---
|
|
|
|
## Phase 5 — String buffer
|
|
|
|
Fix O(n²) string concatenation in loops across Lua, Ruby, Common Lisp, Tcl.
|
|
|
|
- [ ] Spec + OCaml: add `make-string-buffer`, `string-buffer-append!`, `string-buffer->string`,
|
|
`string-buffer-length` to primitives. OCaml: `Buffer.t` wrapper. JS: array+join.
|
|
- [ ] Tests: 15+ tests.
|
|
- [ ] Commit: `spec: string-buffer primitive`
|
|
|
|
---
|
|
|
|
## Phase 6 — Algebraic data types
|
|
|
|
The deepest structural gap. Every language uses `{:tag "..." :field ...}` tagged dicts to
|
|
simulate sum types. A native `define-type` + `match` form eliminates this everywhere.
|
|
|
|
- [ ] Design: write `plans/designs/sx-adt.md` covering syntax, CEK dispatch, interaction with
|
|
existing `cond`/`case`, exhaustiveness checking, recursive types, pattern variables.
|
|
Draft, then stop — next fire reviews design before implementing.
|
|
|
|
- [ ] Spec: implement `define-type` special form in `spec/evaluator.sx`:
|
|
`(define-type Name (Ctor1 field...) (Ctor2 field...) ...)`
|
|
Creates constructor functions `Ctor1`, `Ctor2` + predicate `Name?`.
|
|
|
|
- [ ] Spec: implement `match` special form:
|
|
`(match expr ((Ctor1 a b) body) ((Ctor2 x) body) (else body))`
|
|
Exhaustiveness warning if not all constructors covered and no `else`.
|
|
|
|
- [ ] OCaml: add `SxAdt of string * value array` to types; implement constructors + match.
|
|
- [ ] JS bootstrapper: update.
|
|
- [ ] Tests: 40+ tests in `spec/tests/test-adt.sx`.
|
|
- [ ] Commit: `spec: algebraic data types (define-type + match)`
|
|
|
|
---
|
|
|
|
## Phase 7 — Language sweep
|
|
|
|
Replace workarounds with primitives. One language per fire (or per sub-item for big ones).
|
|
Start with blank slates (CL, APL, Ruby, Tcl) — they haven't committed to workarounds yet.
|
|
|
|
**Scope per language:** only `lib/<lang>/**`. Don't touch spec or other languages.
|
|
Brief each language's loop agent (or do inline) after rebasing their branch onto architecture.
|
|
|
|
- [ ] Restart CL/APL/Ruby/Tcl loops with updated briefing pointing to new primitives.
|
|
Add a note to each `plans/<lang>-on-sx.md` under a `## SX primitive baseline` section:
|
|
"Use vectors for arrays, numeric tower for numbers, ADTs for tagged data, coroutines
|
|
for fibers, string-buffer for mutable string building."
|
|
|
|
- [ ] Lua: replace string-keyed dict arrays → vectors in `lua-get`/`lua-set!`/`lua-len`;
|
|
remove `str` coercion from array paths; fix `lua-to-number` for float identity.
|
|
|
|
- [ ] Erlang: fix `er-equal?` float vs int; remove `er-mk-float?` workaround; numeric tower.
|
|
|
|
- [ ] Haskell: use numeric tower for `Num`/`Integral`/`Fractional` dispatch.
|
|
|
|
- [ ] JS: use vectors for Array internals; `Number.isInteger` via `integer?`.
|
|
|
|
- [ ] Smalltalk: use vectors for `Array new:`.
|
|
|
|
- [ ] Forth: use string-buffer for word-definition accumulation if applicable.
|
|
|
|
---
|
|
|
|
## Ground rules
|
|
|
|
- Work on the `architecture` branch in `/root/rose-ash` (main worktree).
|
|
- Use sx-tree MCP for all `.sx` file edits. Never use raw Edit/Write/Read on `.sx` files.
|
|
- Commit after each concrete unit of work. Never leave the branch broken.
|
|
- Never push to `main` — only push to `origin/architecture`.
|
|
- Update this checklist every fire: tick `[x]` done, add inline notes on blockers.
|
|
|
|
---
|
|
|
|
## Progress log
|
|
|
|
_Newest first._
|
|
|
|
- 2026-04-25: Phase 1 spec step done — all 10 vector primitives in spec/primitives.sx have full :as type annotations, :returns, :doc; make-vector optional fill param added.
|
|
- 2026-04-25: Phase 1 OCaml step done — bounds-checked vector-ref/set!, vector-copy now accepts optional start/end, spec/primitives.sx doc updated. 10/10 r7rs vector tests pass, 4747 total (394 pre-existing hs-upstream fails unchanged).
|
|
- 2026-04-25: Phase 0 complete — stopped CL/APL/Ruby/Tcl loops (all 4 idle at shell); confirmed E38 (tokenizer :end/:line) and E39 (WebWorker stub) both have implementation commits.
|