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>
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>
parse-on-feat now skips 'queue MODE' tokens before parsing the body,
so 'on foo queue first ...' and 'on foo queue last ...' parse correctly.
Compiler ignores queue mode (catch-all drops unknown parts).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parser: parse-set-cmd now emits (set-el! target value) when target is
a query node (e.g. #id, .class), keeping (set! ...) for all other
targets.
Compiler: add (set-el! ...) handler that calls hs-set-element!; revert
emit-set for query targets back to hs-set-inner-html! so that
put "x" into #target keeps setting innerHTML rather than replacing
the element.
Runtime: hs-set-element! new function — parses value as HTML into a
temp div; if it contains element children, replaces the target element
via replaceChild and boots hyperscript on the new element; otherwise
falls through to hs-set-inner-html!. Removes the spurious
host-to-list wrapper that was causing len() to always return 0.
Result: all 8 assignableElements tests pass (set #id / set .class /
set closest / swap, plus put-into-still-works-as-innerHTML).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- hs-value-of-node: use selectedIndex fallback when SELECT.value is
empty (mock DOM doesn't auto-compute it from selected options)
- generate-sx-tests: manual body for 'programmatically changed
selections' test — deselect dog, select cat before reading values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- parse-atom: unrecognized keywords (e.g. index) fall back to ref,
fixing 'set index to N' parse failure
- hs-set-inner-html!: join list values as "" so 'put [A,C] into el'
concatenates strings not [object Object]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add style case to the of-target compiler branch so
'set *color of #el to x' emits dom-set-style correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Store target element in meta.owner when hs-on fires;
hs-fetch-impl dispatches beforeFetch on it before the perform.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Track halt mode via __hs-no-stop flag; skip stopPropagation when
handler raised hs-halt-default (from 'halt default'). All other
halt variants (halt, halt the event, halt bubbling) unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- compiler: wrap catch body in nested guard so (raise e) inside a
catch handler defers the re-raise until after the guard exits,
avoiding the handler-stays-active infinite loop
- generator: MANUAL_TEST_BODIES for rethrown/uncaught exception events,
can-pick-detail/event-property, bootstrap bootstraps; remove from
skip-list; regenerate behavioral spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
collect-pairs! in parse-add-cmd now skips the semicolon op token
between CSS properties, so add {color: red; font-family: monospace}
compiles to two dom-set-style calls instead of three malformed ones.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parser: parse-cmd-list now skips a leading 'then' token so that
'on click from #bar then add .clicked' compiles correctly instead
of producing nil as the body.
Bootstrap tests: fix two broken tests whose assertions were
incomplete or contradictory:
- "cleanup removes event listeners" — deactivate + re-click to
verify listener is gone
- "reinitializes if script attribute changes" — actually change
the _ attribute before re-activating and re-clicking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SX uses nil (not null) as the null value; null is an undefined symbol
that caused _run-test-thunk to throw before the guard could catch it.
Also adds globalFunction mock for call-cluster tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>