Adds 9 regexp primitives to stdlib.regexp. OCaml: SxRegexp(src,flags,Re.re)
using Re.Pcre; $&/$1 capture expansion in replace. JS: native RegExp
with SxRegexp wrapper; regexp-match returns {:match :start :end :groups}.
32 tests in test-regexp.sx, all pass on both hosts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 13 set primitives to stdlib.sets. OCaml: SxSet as (string,value)
Hashtbl keyed by inspect(val); JS: SxSet wrapping Map keyed by
write-to-string. Structural equality — (make-set '(1 2)) contains 1.
Includes union, intersection, difference, for-each, map.
33 tests in test-sets.sx, all pass on both JS and OCaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds read, write, display, newline, write-to-string, display-to-string
and current-*-port primitives to both JS and OCaml hosts.
JS: sxReadNormalize (#t/#f→true/false), sxReadConvert (()→nil),
sxEq array comparison, sxWriteVal symbol/keyword name fix,
readerMacroGet/readerMacroSet registry in parser platform.
OCaml: sx_write_val/sx_display_val helpers, read/write/display/newline
primitives on port types; parser extended for #t/#f and N/D rationals.
42 new tests (test-read-write.sx), all passing on JS and OCaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SxRational type in OCaml (Rational of int * int, stored reduced, denom>0)
and JS (SxRational class with _rational marker). n/d reader syntax in
spec/parser.sx. Arithmetic contagion: int op rational → rational, rational
op float → float. JS keeps int/int → float for CSS backward compatibility.
OCaml as_number + safe_eq extended for cross-type rational equality so
(= 2.5 5/2) → true. 62 tests in test-rationals.sx, all pass.
JS: 2232 passed. OCaml: 4532 passed (+11 vs pre-fix baseline).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 14: port type + eof-object. Input ports track _pos cursor; output ports
accumulate _buffer. All 15 port primitives in spec/primitives.sx (stdlib.ports
module), platform.py (JS), and 39/39 tests in spec/tests/test-ports.sx.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OCaml: StringBuffer of Buffer.t in sx_types.ml; 5 primitives in
sx_primitives.ml (make-string-buffer, string-buffer?, string-buffer-append!,
string-buffer->string, string-buffer-length); inspect case added.
JS: SxStringBuffer with array+join backend; _string_buffer marker for
typeOf dispatch and dict? exclusion (also excludes _vector from dict?).
spec/primitives.sx: 5 define-primitive entries.
17/17 tests pass on both OCaml and JS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
spec/coroutines.sx: define-library with make-coroutine, coroutine-resume,
coroutine-yield, coroutine?, coroutine-alive?. Built on existing perform/
cek-step-loop/cek-resume suspension machinery.
spec/tests/test-coroutines.sx: 17 tests — multi-yield, final return,
arg passthrough, alive? predicate, nested coroutines, recursive iteration,
independent coroutine interleaving.
Key: coroutine body must use (define loop (fn…)) not named let — named let
transpiles to cek_call→cek_run which rejects IO suspension. All 17/17 pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
vector-ref and vector-set! now raise Eval_error on out-of-bounds index instead of
an OCaml array exception. vector-copy accepts optional start and end parameters for
slicing (R7RS §6.8). spec/primitives.sx doc updated to reflect slice params.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The irreducible primitive set drops from 79 to 33. Everything that can
be expressed in SX is now a library function in stdlib.sx, loaded after
evaluator.sx and before render.sx.
Moved to stdlib.sx (pure SX, no host dependency):
- Logic: not
- Comparison: != <= >= eq? eqv? equal?
- Predicates: nil? boolean? number? string? list? dict? continuation?
empty? odd? even? zero? contains?
- Arithmetic: inc dec abs ceil round min max clamp
- Collections: first last rest nth cons append reverse flatten range
chunk-every zip-pairs vals has-key? merge assoc dissoc into
- Strings: upcase downcase string-length substring string-contains?
starts-with? ends-with? split join replace
- Text: pluralize escape assert parse-datetime
Remaining irreducible primitives (33):
+ - * / mod floor pow sqrt = < > type-of symbol-name keyword-name
str slice index-of upper lower trim char-from-code list dict concat
get len keys dict-set! append! random-int json-encode format-date
parse-int format-decimal strip-tags sx-parse error apply
All hosts: JS 957+1080, Python 744, OCaml 952 — zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>