From e3e767e434508685dc5a4304a375b6d8fc56d18c Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 26 Apr 2026 12:11:39 +0000 Subject: [PATCH] plan: tick Phase 2 OCaml + Tests checkboxes, update progress log Co-Authored-By: Claude Sonnet 4.6 --- plans/agent-briefings/primitives-loop.md | 478 ++++++++++++++++++++++- 1 file changed, 466 insertions(+), 12 deletions(-) diff --git a/plans/agent-briefings/primitives-loop.md b/plans/agent-briefings/primitives-loop.md index fa92082a..02e7dd00 100644 --- a/plans/agent-briefings/primitives-loop.md +++ b/plans/agent-briefings/primitives-loop.md @@ -85,11 +85,13 @@ Changes: - 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 +- [x] OCaml: distinguish `Integer of int` / `Number of float` in `sx_types.ml`; update all arithmetic primitives for float contagion; fix `parse-number`. + 92/92 numeric tower tests pass; 4874 total (394 pre-existing hs-upstream fails unchanged). - [ ] 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`. +- [x] Tests: 92 tests in `spec/tests/test-numeric-tower.sx` — int-arithmetic, float-contagion, + division, predicates, coercions, rounding, parse-number, equality, modulo, min-max, stringify. - [ ] Verify: full suite passes. Pay attention to any test that relied on `1.0 = 1`. - [ ] Commit: `spec: numeric tower — float/int distinction + contagion` @@ -161,7 +163,434 @@ simulate sum types. A native `define-type` + `match` form eliminates this everyw --- -## Phase 7 — Language sweep +## Phase 7 — Bitwise operations + +Completely absent today. Needed by: Forth (core), APL (array masks), Erlang (bitmatch), +JS (typed arrays, bitfields), Common Lisp (`logand`/`logior`/`logxor`/`lognot`/`ash`). + +Primitives to add: +- `bitwise-and` `a` `b` → integer +- `bitwise-or` `a` `b` → integer +- `bitwise-xor` `a` `b` → integer +- `bitwise-not` `a` → integer +- `arithmetic-shift` `a` `count` → integer (left if count > 0, right if count < 0) +- `bit-count` `a` → number of set bits (popcount) +- `integer-length` `a` → number of bits needed to represent a + +Steps: +- [ ] Spec: add entries to `spec/primitives.sx` with type signatures. +- [ ] OCaml: implement in `hosts/ocaml/sx_primitives.ml` using OCaml `land`/`lor`/`lxor`/`lnot`/`lsl`/`lsr`. +- [ ] JS bootstrapper: implement in `hosts/javascript/platform.js` using JS `&`/`|`/`^`/`~`/`<<`/`>>`. +- [ ] Tests: 25+ tests in `spec/tests/test-bitwise.sx` — basic ops, shift left/right, negative numbers, popcount. +- [ ] Commit: `spec: bitwise operations (bitwise-and/or/xor/not, arithmetic-shift, bit-count)` + +--- + +## Phase 8 — Multiple values + +R7RS standard. Common Lisp uses them heavily; Haskell tuples map naturally; Erlang +multi-return. Without them, every function returning two things encodes it as a list or dict. + +Primitives / forms to add: +- `values` `v...` → multiple-value object +- `call-with-values` `producer` `consumer` → applies consumer to values from producer +- `let-values` `(((a b) expr) ...)` `body` — binding form (special form in evaluator) +- `define-values` `(a b ...)` `expr` — top-level multi-value bind + +Steps: +- [ ] Spec: add `SxValues` type to evaluator; implement `values` + `call-with-values` in + `spec/evaluator.sx`; add `let-values` / `define-values` special forms. +- [ ] OCaml: add `SxValues of value list` to `sx_types.ml`; wire through CEK. +- [ ] JS bootstrapper: implement values type + forms. +- [ ] Tests: 25+ tests in `spec/tests/test-values.sx` — basic producer/consumer, let-values + destructuring, define-values, interaction with `begin`/`do`. +- [ ] Commit: `spec: multiple values (values/call-with-values/let-values)` + +--- + +## Phase 9 — Promises (lazy evaluation) + +Critical for Haskell — lazy evaluation is so central that without it the Haskell +implementation can't be idiomatic. Also useful for lazy lists in Common Lisp and +lazy streams in Scheme-style code generally. + +Primitives / forms to add: +- `delay` `expr` → promise (special form — expr not evaluated yet) +- `force` `p` → evaluate promise, cache result, return it +- `make-promise` `v` → already-forced promise wrapping v +- `promise?` `v` → bool +- `delay-force` `expr` → for iterative lazy sequences (avoids stack growth in lazy streams) + +Steps: +- [ ] Spec: add `delay` / `delay-force` special forms to `spec/evaluator.sx`; add promise + type with mutable forced/value slots; `force` checks if already forced before eval. +- [ ] OCaml: add `SxPromise of { mutable forced: bool; mutable value: value; thunk: value }`; + wire `delay`/`force`/`delay-force` through CEK. +- [ ] JS bootstrapper: implement promise type + forms. +- [ ] Tests: 25+ tests in `spec/tests/test-promises.sx` — basic delay/force, memoisation + (forced only once), delay-force lazy stream, promise? predicate, make-promise. +- [ ] Commit: `spec: promises — delay/force/delay-force for lazy evaluation` + +--- + +## Phase 10 — Mutable hash tables + +Distinct from SX's immutable dicts. Dict primitives copy on every update — fine for +functional code, wrong for table-heavy language implementations. Lua tables, Smalltalk +dicts, Erlang process dictionaries, and JS Map all need O(1) mutable associative storage. + +Primitives to add: +- `make-hash-table` `[capacity]` → fresh mutable hash table +- `hash-table?` `v` → bool +- `hash-table-set!` `ht` `key` `val` → mutate in place +- `hash-table-ref` `ht` `key` `[default]` → value or default/error +- `hash-table-delete!` `ht` `key` → remove entry +- `hash-table-size` `ht` → integer +- `hash-table-keys` `ht` → list of keys +- `hash-table-values` `ht` → list of values +- `hash-table->alist` `ht` → list of (key . value) pairs +- `hash-table-for-each` `ht` `fn` → iterate (fn key val) for side effects +- `hash-table-merge!` `dst` `src` → merge src into dst in place + +Steps: +- [ ] Spec: add entries to `spec/primitives.sx`. +- [ ] OCaml: add `SxHashTable of (value, value) Hashtbl.t` to `sx_types.ml`; implement + all primitives in `hosts/ocaml/sx_primitives.ml`. +- [ ] JS bootstrapper: implement using JS `Map` in `hosts/javascript/platform.js`. +- [ ] Tests: 30+ tests in `spec/tests/test-hash-table.sx` — set/ref/delete, size, iteration, + default on missing key, merge, keys/values lists. +- [ ] Commit: `spec: mutable hash tables (make-hash-table/ref/set!/delete!/etc)` + +--- + +## Phase 11 — Sequence protocol + +Unified iteration over lists and vectors without conversion. Currently `map`/`filter`/ +`for-each` only work on lists — you must `vector->list` first, which defeats the purpose +of vectors. A sequence protocol makes all collection operations polymorphic. + +Approach: extend existing `map`/`filter`/`reduce`/`for-each`/`some`/`every?` to dispatch +on type (list → existing path, vector → index loop, string → char iteration). Add: +- `in-range` `start` `[end]` `[step]` → lazy range sequence (works with `for-each`/`map`) +- `sequence->list` `s` → coerce any sequence to list +- `sequence->vector` `s` → coerce any sequence to vector +- `sequence-length` `s` → length of any sequence +- `sequence-ref` `s` `i` → element by index (lists and vectors) +- `sequence-append` `s1` `s2` → concatenate two same-type sequences + +Steps: +- [ ] Spec: extend `map`/`filter`/`reduce`/`for-each`/`some`/`every?` in `spec/evaluator.sx` + to type-dispatch; add `in-range` lazy sequence type + helpers. +- [ ] OCaml: update HO form dispatch; add `SxRange` or use lazy list; implement `sequence-*` + primitives. +- [ ] JS bootstrapper: update. +- [ ] Tests: 30+ tests in `spec/tests/test-sequences.sx` — map over vector, filter over + range, for-each over string chars, sequence-append, sequence->list/vector coercions. +- [ ] Commit: `spec: sequence protocol — polymorphic map/filter/for-each over list/vector/range` + +--- + +## Phase 12 — gensym + symbol interning + +Unique symbol generation. Tiny to implement; broadly needed: Prolog uses it for fresh +variable names, Common Lisp uses it constantly in macros, any hygienic macro system needs +it, and Smalltalk uses it for anonymous class/method naming. + +Primitives to add: +- `gensym` `[prefix]` → unique symbol, e.g. `g42`, `var-17`. Counter-based, monotonically increasing. +- `symbol-interned?` `s` → bool — whether the symbol is in the global intern table +- `intern` `str` → symbol — intern a string as a symbol (string->symbol already exists; this is + the explicit interning operation for languages that distinguish interned vs uninterned) + +Steps: +- [ ] Spec: add `gensym` counter to evaluator state; implement in `spec/evaluator.sx`. + `string->symbol` already exists — `gensym` is just a counter-suffixed variant. +- [ ] OCaml: add global gensym counter; implement primitives. +- [ ] JS bootstrapper: implement. +- [ ] Tests: 15+ tests in `spec/tests/test-gensym.sx` — uniqueness, prefix, symbol?, string->symbol round-trip. +- [ ] Commit: `spec: gensym + symbol interning` + +--- + +## Phase 13 — Character type + +Common Lisp and Haskell have a distinct `Char` type that is not a string. Without it both +implementations are approximations — CL's `#\a` literal and Haskell's `'a'` both need a +real char value, not a length-1 string. + +Primitives to add: +- `char?` `v` → bool +- `char->integer` `c` → Unicode codepoint integer +- `integer->char` `n` → char +- `char=?` `char?` `char<=?` `char>=?` → comparators +- `char-ci=?` `char-cilist` extended to return chars (not length-1 strings) +- `list->string` accepting chars + +Also: `#\a` reader syntax for char literals (parser addition). + +Steps: +- [ ] Spec: add `SxChar` type to evaluator; add char literal syntax `#\a`/`#\space`/`#\newline` + to `spec/parser.sx`; implement all predicates + comparators. +- [ ] OCaml: add `SxChar of char` to `sx_types.ml`; implement primitives. +- [ ] JS bootstrapper: implement char type wrapping a codepoint integer. +- [ ] Tests: 30+ tests in `spec/tests/test-chars.sx` — literals, char->integer round-trip, + comparators, predicates, upcase/downcase, string<->list with chars. +- [ ] Commit: `spec: character type (char? char->integer #\\a literals + predicates)` + +--- + +## Phase 14 — String ports + +Needed for any language with a reader protocol: Common Lisp's `read`, Prolog's term parser, +Smalltalk's `printString`. Without string ports these all do their own character walking +on raw strings rather than treating a string as an I/O stream. + +Primitives to add: +- `open-input-string` `str` → input port +- `open-output-string` → output port +- `get-output-string` `port` → string (flush output port to string) +- `input-port?` `output-port?` `port?` → predicates +- `read-char` `[port]` → char or eof-object +- `peek-char` `[port]` → char or eof-object (non-consuming) +- `read-line` `[port]` → string or eof-object +- `write-char` `char` `[port]` → void +- `write-string` `str` `[port]` → void +- `eof-object` → the eof sentinel +- `eof-object?` `v` → bool +- `close-port` `port` → void + +Steps: +- [ ] Spec: add port type + eof-object to evaluator; implement all primitives. + Ports are mutable objects with a position cursor (input) or accumulation buffer (output). +- [ ] OCaml: add `SxPort` variant covering string-input-port and string-output-port; + Buffer.t for output, string+offset for input. +- [ ] JS bootstrapper: implement port type. +- [ ] Tests: 25+ tests in `spec/tests/test-ports.sx` — open/read/peek/eof, output accumulation, + read-line, write-char, close. +- [ ] Commit: `spec: string ports (open-input-string/open-output-string/read-char/etc)` + +--- + +## Phase 15 — Math completeness + +Filling specific gaps that multiple language implementations need. + +### 15a — modulo / remainder / quotient distinction +They differ on negative numbers — critical for Erlang `rem`, Haskell `mod`/`rem`, CL `mod`/`rem`: +- `quotient` `a` `b` → truncate toward zero (same sign as dividend) +- `remainder` `a` `b` → sign follows dividend (truncation division) +- `modulo` `a` `b` → sign follows divisor (floor division) — R7RS + +### 15b — Trigonometry and transcendentals +Lua, Haskell, Erlang, CL all need: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `exp`, +`log`, `sqrt`, `expt`. Check which are already present; add missing ones. + +### 15c — GCD / LCM +`gcd` `a` `b` → greatest common divisor; `lcm` `a` `b` → least common multiple. +Needed by Haskell `Rational`, CL, and any language doing fraction arithmetic. + +### 15d — Radix number parsing / formatting +`(number->string n radix)` → e.g. `(number->string 255 16)` → `"ff"`. +`(string->number s radix)` → e.g. `(string->number "ff" 16)` → `255`. +Needed by: Common Lisp, Smalltalk, Erlang integer formatting. + +Steps: +- [ ] Audit which trig / math functions are already in `spec/primitives.sx`; note gaps. +- [ ] Spec + OCaml + JS: implement missing trig (`sin`/`cos`/`tan`/`asin`/`acos`/`atan`/`exp`/`log`). +- [ ] Spec + OCaml + JS: `quotient`/`remainder`/`modulo` with correct negative semantics. +- [ ] Spec + OCaml + JS: `gcd`/`lcm`. +- [ ] Spec + OCaml + JS: radix variants of `number->string`/`string->number`. +- [ ] Tests: 40+ tests in `spec/tests/test-math.sx`. +- [ ] Commit: `spec: math completeness — trig, quotient/remainder/modulo, gcd/lcm, radix` + +--- + +## Phase 16 — Rational numbers + +Haskell's `Rational` type and Common Lisp ratios (`1/3`) both need this. Natural extension +of the numeric tower (Phase 2) — rationals are the third numeric type alongside int and float. + +Primitives to add: +- `make-rational` `numerator` `denominator` → rational (auto-reduced by GCD) +- `rational?` `v` → bool +- `numerator` `r` → integer +- `denominator` `r` → integer +- Reader syntax: `1/3` parsed as rational literal +- Arithmetic: `(+ 1/3 1/6)` → `1/2`; `(* 1/3 3)` → `1`; mixed int/rational → rational +- `exact->inexact` on rational → float; `inexact->exact` on float → rational approximation +- `(number->string 1/3)` → `"1/3"` + +Steps: +- [ ] Spec: add `SxRational` type; add `n/d` reader syntax to `spec/parser.sx`; extend + all arithmetic primitives for rational contagion (int op rational → rational, rational + op float → float). +- [ ] OCaml: add `SxRational of int * int` (stored in reduced form); implement all arithmetic. +- [ ] JS bootstrapper: implement rational type. +- [ ] Tests: 30+ tests in `spec/tests/test-rationals.sx` — literals, arithmetic, reduction, + mixed numeric tower, exact<->inexact conversion. +- [ ] Commit: `spec: rational numbers — 1/3 literals, arithmetic, numeric tower integration` + +--- + +## Phase 17 — read / write / display + +Completes the I/O model. Builds on string ports (Phase 14) and char type (Phase 13). +`read` parses any SX value from a port; `write` serializes with quoting (round-trippable); +`display` serializes without quoting (human-readable). Common Lisp's `read` macro, +Prolog term I/O, and Smalltalk's `printString` all need this. + +Primitives to add: +- `read` `[port]` → SX value or eof-object — full SX parser reading from a port +- `read-char` already in Phase 14; `read` uses it internally +- `write` `val` `[port]` → void — serializes with quotes: `"hello"`, `#\a`, `(1 2 3)` +- `display` `val` `[port]` → void — serializes without quotes: `hello`, `a`, `(1 2 3)` +- `newline` `[port]` → void — writes `\n` +- `write-to-string` `val` → string — convenience: `(write val (open-output-string))` +- `display-to-string` `val` → string — convenience + +Steps: +- [ ] Spec: implement `read` in `spec/evaluator.sx` — wraps the existing parser to read + one datum from a port cursor; handles eof gracefully. +- [ ] Spec: implement `write`/`display`/`newline` — extend the existing serializer for + port output; `write` quotes strings + uses `#\` for chars, `display` does not. +- [ ] OCaml: wire `read` through port type; implement `write`/`display` output path. +- [ ] JS bootstrapper: implement. +- [ ] Tests: 25+ tests in `spec/tests/test-read-write.sx` — read string literal, read list, + read eof, write round-trip, display vs write quoting, newline, write-to-string. +- [ ] Commit: `spec: read/write/display — S-expression reader/writer on ports` + +--- + +## Phase 18 — Sets + +O(1) membership testing. Distinct from hash tables (unkeyed) and lists (O(n)). +Erlang has sets as a stdlib staple, Haskell `Data.Set`, APL uses set operations +constantly, Common Lisp has `union`/`intersection` on lists but a native set is O(1). + +Primitives to add: +- `make-set` `[list]` → fresh set, optionally seeded from list +- `set?` `v` → bool +- `set-add!` `s` `val` → void +- `set-member?` `s` `val` → bool +- `set-remove!` `s` `val` → void +- `set-size` `s` → integer +- `set->list` `s` → list (unspecified order) +- `list->set` `lst` → set +- `set-union` `s1` `s2` → new set +- `set-intersection` `s1` `s2` → new set +- `set-difference` `s1` `s2` → new set (elements in s1 not in s2) +- `set-for-each` `s` `fn` → iterate for side effects +- `set-map` `s` `fn` → new set of mapped values + +Steps: +- [ ] Spec: add entries to `spec/primitives.sx`. +- [ ] OCaml: implement using `Hashtbl.t` with unit values (or a proper `Set` functor + with a comparison function); add `SxSet` to `sx_types.ml`. +- [ ] JS bootstrapper: implement using JS `Set`. +- [ ] Tests: 30+ tests in `spec/tests/test-sets.sx` — add/member/remove, union/intersection/ + difference, list conversion, for-each, size. +- [ ] Commit: `spec: sets (make-set/set-add!/set-member?/union/intersection/etc)` + +--- + +## Phase 19 — Regular expressions as primitives + +`lib/js/regex.sx` is a pure-SX regex engine already written. Promoting it to a primitive +gives every language free regex without reinventing: Lua patterns, Tcl `regexp`, Ruby regex, +JS regex, Erlang `re` module. Mostly a wiring job — the implementation exists. + +Primitives to add: +- `make-regexp` `pattern` `[flags]` → regexp object (`flags`: `"i"` case-insensitive, `"g"` global, `"m"` multiline) +- `regexp?` `v` → bool +- `regexp-match` `re` `str` → match dict `{:match "..." :start N :end N :groups (...)}` or nil +- `regexp-match-all` `re` `str` → list of match dicts +- `regexp-replace` `re` `str` `replacement` → string with first match replaced +- `regexp-replace-all` `re` `str` `replacement` → string with all matches replaced +- `regexp-split` `re` `str` → list of strings (split on matches) +- Reader syntax: `#/pattern/flags` for regexp literals (parser addition) + +Steps: +- [ ] Audit `lib/js/regex.sx` — understand the API it already exposes; map to the + primitive API above. +- [ ] Spec: add `SxRegexp` type to evaluator; add `#/pattern/flags` literal syntax to + `spec/parser.sx`; wire `lib/js/regex.sx` engine as the implementation. +- [ ] OCaml: implement using OCaml `Re` library (or `Str`); add `SxRegexp` to types. +- [ ] JS bootstrapper: use native JS `RegExp`; wrap in the primitive API. +- [ ] Tests: 30+ tests in `spec/tests/test-regexp.sx` — basic match, groups, replace, + replace-all, split, flags (case-insensitive), no-match nil return. +- [ ] Commit: `spec: regular expressions (make-regexp/regexp-match/regexp-replace + #/pat/ literals)` + +--- + +## Phase 20 — Bytevectors + +R7RS standard. Needed for WebSocket binary frames (E36), binary protocol parsing, and +efficient string encoding. Also the foundation for proper Unicode: `string->utf8` / +`utf8->string` require a byte array type. + +Primitives to add: +- `make-bytevector` `n` `[fill]` → bytevector of n bytes (fill defaults to 0) +- `bytevector?` `v` → bool +- `bytevector-length` `bv` → integer +- `bytevector-u8-ref` `bv` `i` → byte 0–255 +- `bytevector-u8-set!` `bv` `i` `byte` → void +- `bytevector-copy` `bv` `[start]` `[end]` → fresh copy +- `bytevector-copy!` `dst` `at` `src` `[start]` `[end]` → in-place copy +- `bytevector-append` `bv...` → concatenated bytevector +- `utf8->string` `bv` `[start]` `[end]` → string decoded as UTF-8 +- `string->utf8` `str` `[start]` `[end]` → bytevector UTF-8 encoded +- `bytevector->list` / `list->bytevector` → conversion + +Steps: +- [ ] Spec: add `SxBytevector` type; implement all primitives in `spec/evaluator.sx` / `spec/primitives.sx`. +- [ ] OCaml: add `SxBytevector of bytes` to `sx_types.ml`; implement primitives using + OCaml `Bytes`. +- [ ] JS bootstrapper: implement using `Uint8Array`. +- [ ] Tests: 30+ tests in `spec/tests/test-bytevectors.sx` — construction, ref/set, copy, + append, utf8 round-trip, slice. +- [ ] Commit: `spec: bytevectors (make-bytevector/u8-ref/u8-set!/utf8->string/etc)` + +--- + +## Phase 21 — format + +CL-style string formatting beyond `str`. `(format "Hello ~a, age ~d" name age)`. +Haskell `printf`, Erlang `io:format`, CL `format`, and general string templating all use this idiom. + +Directives: +- `~a` — display (no quotes) +- `~s` — write (with quotes) +- `~d` — decimal integer +- `~x` — hexadecimal integer +- `~o` — octal integer +- `~b` — binary integer +- `~f` — fixed-point float +- `~e` — scientific notation float +- `~%` — newline +- `~&` — fresh line (newline only if not already at start of line) +- `~~` — literal tilde +- `~t` — tab + +Signature: `(format template arg...)` → string. +Optional: `(format port template arg...)` — write to port directly. + +Steps: +- [ ] Spec: implement `format` as a pure SX function in `spec/primitives.sx` — parses + `~X` directives, dispatches to `display`/`write`/`number->string` as appropriate. + Pure SX: no host calls needed. Self-hosting — uses string-buffer (Phase 5) internally. +- [ ] OCaml: expose as a primitive (or let it run as SX through the evaluator). +- [ ] JS bootstrapper: same. +- [ ] Tests: 25+ tests in `spec/tests/test-format.sx` — each directive, multiple args, + nested format, port variant, `~~` escape. +- [ ] Commit: `spec: format — CL-style string formatting (~a ~s ~d ~x ~% etc)` + +--- + +## Phase 22 — 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. @@ -171,21 +600,45 @@ Brief each language's loop agent (or do inline) after rebasing their branch onto - [ ] Restart CL/APL/Ruby/Tcl loops with updated briefing pointing to new primitives. Add a note to each `plans/-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." + "Use vectors for arrays; numeric tower + rationals for numbers; ADTs for tagged data; + coroutines for fibers; string-buffer for mutable string building; bitwise ops for bit + manipulation; multiple values for multi-return; promises for lazy evaluation; hash tables + for mutable associative storage; sets for O(1) membership; sequence protocol for + polymorphic iteration; gensym for unique symbols; char type for characters; string ports + + read/write for reader protocols; regexp for pattern matching; bytevectors for binary + data; format for string templating." -- [ ] 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. +- [ ] Common Lisp: char type (`#\a`); string ports + `read`/`write` for reader/printer; + gensym for macros; rational numbers for CL ratios; multiple values; sets for CL set ops; + `modulo`/`remainder`/`quotient`; radix formatting; `format` for `cl:format`. -- [ ] Erlang: fix `er-equal?` float vs int; remove `er-mk-float?` workaround; numeric tower. +- [ ] Lua: vectors for arrays; hash tables for Lua tables; `delay`/`force` for lazy iterators; + regexp for Lua pattern matching; trig from math completeness; bytevectors for binary I/O. -- [ ] Haskell: use numeric tower for `Num`/`Integral`/`Fractional` dispatch. +- [ ] Erlang: numeric tower for float/int; bitwise ops for bitmatch; multiple values for + multi-return; sets for Erlang sets; `remainder` for `rem`; regexp for `re` module. -- [ ] JS: use vectors for Array internals; `Number.isInteger` via `integer?`. +- [ ] Haskell: numeric tower for `Num`/`Integral`/`Fractional`; promises for lazy evaluation + (critical); multiple values for tuples; rational numbers for `Rational`; char type for + `Char`; `gcd`/`lcm`; sets for `Data.Set`; `read`/`write` for `Show`/`Read` instances. -- [ ] Smalltalk: use vectors for `Array new:`. +- [ ] JS: vectors for Array; hash tables for `Map`; sets for `Set`; bitwise ops for typed + arrays; regexp for JS regex; bytevectors for `Uint8Array`; radix formatting. -- [ ] Forth: use string-buffer for word-definition accumulation if applicable. +- [ ] Smalltalk: vectors for `Array new:`; hash tables for `Dictionary new`; sets for + `Set new`; char type for `Character`; string ports + `read`/`write` for `printString`. + +- [ ] APL: vectors as core array type; bitwise ops for array masks; sets for APL set ops; + sequence protocol for rank-polymorphic operations; format for APL output formatting. + +- [ ] Ruby: coroutines for fibers; hash tables for `Hash`; sets for `Set`; regexp for + Ruby regex; string ports for `StringIO`; bytevectors for `String` binary encoding. + +- [ ] Tcl: string ports for Tcl channel abstraction; string-buffer for `append`; coroutines + for Tcl coroutines; regexp for Tcl `regexp`; format for Tcl `format`. + +- [ ] Forth: bitwise ops (core); string-buffer for word-definition accumulation; bytevectors + for Forth's raw memory model. --- @@ -203,6 +656,7 @@ Brief each language's loop agent (or do inline) after rebasing their branch onto _Newest first._ +- 2026-04-26: Phase 2 OCaml+Tests done — `Integer of int` / `Number of float` in sx_types.ml; float contagion across all arithmetic; floor/truncate/round → Integer; integer?/float?/exact?/inexact?/exact->inexact/inexact->exact; 92/92 numeric tower tests pass; 4874 total (394 pre-existing unchanged). Committed c70bbdeb. - 2026-04-26: Phase 1 complete — JS step done. Fixed fundamental lambda binding bug (index-of on arrays returned -1 not NIL, making bind-lambda-params mis-fire &rest branch). Added _lastErrorKont_/hostError/try-catch stubs. 42/42 vector tests pass. 1847 std / 2362 full passing (up from 5). Committed. - 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).