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>
Eof sentinel and Port{PortInput/PortOutput} in sx_types.ml. All 15 port
primitives in sx_primitives.ml. type_of/inspect updated. 39/39 port tests
pass (4532 total, +39, zero regressions).
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>
gensym_counter ref + gensym/string->symbol/symbol->string/intern/symbol-interned?
primitives in sx_primitives.ml. Fix ListRef case in seq_to_list on both
sx_ref.ml and sx_primitives.ml. 19 new tests in test-gensym.sx.
OCaml 4450/1080, JS 2205/2497, zero regressions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- seq-to-list: coerce list/vector/string/nil to list
- ho-setup-dispatch: apply seq-to-list to all collection args so map/filter/
reduce/for-each/some/every? work over vectors and strings natively
- sequence->list, sequence->vector, sequence-length, sequence-ref,
sequence-append: full polymorphic sequence helpers
- in-range: list-returning range generator (eager, works with all HO forms)
- Restore 3 accidentally-deleted make-cek-state/make-cek-value/make-cek-suspended
- Fix 8 shorthand define forms (transpiler requires long form)
- Add vector->list/list->vector to transpiler js-renames + platform aliases
- JS: 2137 passing (+28 vs HEAD baseline)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
25 tests pass on both JS and OCaml hosts. Uses dict marker
{:_values true :_list [...]} for 0/2+ values; 1 value passes
through directly. step-sf-define extended to desugar shorthand
(define (name params) body) forms on both hosts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hand-write sf_define_type in bootstrap.py FIXUPS (skipped from transpile
because the spec uses &rest params and empty-dict literals the transpiler
can't emit). Registers define-type via register_special_form. Adds
step_limit/step_count to PREAMBLE (referenced by sx_vm.ml/run_tests.ml).
172 assertions pass (test-adt). Full suite: 4280/1080 (was 4243/1117).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends match-pattern in spec/evaluator.sx with an ADT case: when the
pattern is (CtorName var...) and the value is an ADT dict (:_adt true),
check :_ctor matches, arity matches, then recursively bind field patterns.
Supports nested patterns, wildcard _, variable binding, and zero-arg ctors.
Changes step-sf-match to route no-clause errors through raise-eval-frame
instead of direct error, allowing guard to catch non-exhaustive matches.
40/40 ADT tests pass (20 define-type + 20 match). Zero regressions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
17 tests written inline with the implementation step. All 17 pass
on OCaml and JS. Phase 5 fully done as d98b5fa2.
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>
10 new tests: state field transitions (ready/suspended/dead), yield from
nested helper function, initial resume arg ignored by ready coroutine,
mutable closure state via dict-set!, complex yield values (list/dict),
round-robin scheduling, factory creates independent coroutines, resuming
non-coroutine raises error.
27/27 pass on both OCaml and JS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All CEK primitives (cek-step-loop/cek-resume/make-cek-state/cek-suspended?/
cek-io-request/cek-terminal?/cek-value) were already registered in sx-browser.js.
Root cause of test failure: (import (sx coroutines)) creates an io-suspended
state when the library isn't pre-loaded; overridden cekRun throws on suspension.
Fix: pre-load spec/signals.sx + spec/coroutines.sx before test files run.
17/17 coroutine tests pass in JS. 1965/2500 total (+25 vs 1940 baseline),
zero new failures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No native SxCoroutine type needed. dict-based coroutine identity +
cek-step-loop/cek-resume/perform/make-cek-state primitives already in
run_tests.ml fully implement the coroutine contract. 284/284 pass
(coroutines+vectors+numeric-tower+dynamic-wind), zero regressions.
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>
- sx_types.ml: CallccContinuation gains winders depth int field
- sx_runtime.ml: make_callcc_continuation(captured, winders_len),
callcc_continuation_winders_len accessor; get_val maps after-thunk,
winders-len, body-result to cf_f/cf_extra/cf_name
- sx_ref.ml: step_limit/step_count restored; make_wind_after_frame and
make_wind_return_frame now store their args in the CekFrame fields
- transpiler.sx: after-thunk→cf_f, winders-len→cf_extra,
body-result→cf_name for future bootstrap runs
- 8 new dynamic-wind tests pass (OCaml), 235/235 no regressions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>