- 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>
emit-set on call command re-applied so `it`/`the-result` bound after call.
A11 hide now 16/16 via count-filter unlock (was partial +3, now done +4).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parser.sx: detect bare ident "initial" after "to" in parse-one-transition,
emit string sentinel instead of (ref "initial") which evaluated to nil.
runtime.sx: hs-transition stores pre-first-transition style as
data-hs-init-{prop}; restores it when value=="initial".
Also commits E37 tokenizer and E40 fetch test implementations that
accumulated in the working tree but weren't staged in prior commits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements hs-tokens-of, hs-stream-token, hs-stream-consume,
hs-stream-has-more, hs-token-type, hs-token-value, hs-token-op?,
hs-raw->api-token, hs-eof-sentinel in runtime.sx.
Tokenizer emits whitespace tokens after the first content token;
stream functions skip them for look-ahead and consume. Parser
filters whitespace tokens at hs-parse entry. Dot/hash after close
brackets split into PERIOD/POUND + IDENTIFIER. Template escape \$
produces literal $.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge c36fd5b2 stripped the source-info dict unwrapping from hs-to-sx
(the (let ((ast (if (and (dict? ast) (:hs-ast)) ...) wrapper) and also
introduced E37 tokenizer whitespace-token changes that broke the parser.
Reverts tokenizer/runtime to pre-E37 HEAD~1 state, restores hs-to-sx
with AST unwrapping from 61c9697f, and adds back the hs-id= dispatch
clause. Baseline: 178/195.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add hs-raw->api-token, hs-eof-sentinel, hs-api-list, hs-tokens-of,
hs-stream-token, hs-stream-consume, hs-stream-has-more, hs-token-type,
hs-token-value, hs-token-op? to runtime. Fix tokenizer to emit whitespace
tokens and handle dot/hash after closing brackets. Fix hs-tokens-of to
accept bare :template keyword flag via &rest args + some() check.
Remaining failure (string interpolation isnt surprising) requires full
DOM activation infrastructure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lost when resolving E37 reformat conflicts — re-added:
- hs-id= function in runtime.sx (JS === for elements, = for scalars)
- hs-id= dispatch in compiler.sx (after = clause)
Parser already uses hs-id= for != operator (unchanged).
- hs-id= uses JS === for DOM elements (hs-ref-eq), = for scalars
- != operator now uses hs-id= for structural correctness
- compound tag[attr=val] selector matching in test runner
- dom-query-all replaces host-call querySelectorAll
- DOM tree structure corrected in 4 generated tests (elements were
appended to wrong parents)
Fix compiler: (block-literal () body) was emitting bare body instead of
(fn () body). Now always wraps in fn regardless of param count.
Generator: MANUAL_TEST_BODIES for all 4 blockLiteral tests using apply
and SX map rather than JS array.map.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parser: parse-js-block extracts raw JS source by character positions.
Compiler: js-block AST → hs-js-exec call, stores result in it.
Runtime: hs-js-exec creates JS Function, handles promise rejection.
Test runner: host-new-function/host-promise-state natives + promise monkey-patch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hs-make-object no longer appends _order to every HS object literal.
Generator emit_eval now uses assert-equal (equal?) for dict-containing
expected values instead of assert= (= reference equality).
Together these fix arrayLiteral "arrays containing objects work".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
empty .class compiled (empty-target (query ".class")) to
(hs-empty-target! (hs-query-first ".class")) via hs-to-sx — only
emptying the first match. Fix: detect (query ...) target in the
empty-target compiler case and emit (for-each (fn (_el)
(hs-empty-target! _el)) (hs-query-all sel)) instead, mirroring the
add-class pattern. Suite hs-upstream-empty: 12/13 → 13/13.
Smoke 0-195: 175/195 unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
${}{"val"} pattern in add {prop: ${}{"val"}} uses two consecutive brace
groups: empty ${} followed by {"val"} for the actual expression. The prior
fix called parse-expr when already at the brace-close of the empty group,
returning nil. New fix: detect empty ${} (brace-open then brace-close),
skip the close, then read the actual value from the following {…} block.
Also handles non-empty ${expr} directly as before.
Suite hs-upstream-add: 17/19 → 18/19. Smoke 0-195: 174/195 → 175/195.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
call X then put it into Y was emitting (hs-win-call ...) without
wrapping in emit-set, so it remained nil. Wrap call result in
emit-set(the-result) so it/the-result are updated. Fixes +1 test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents click events from bubbling into ancestor elements that also
have hs handlers (e.g. parent re-inserting HTML after child click).
Fixes put-reprocessing tests 1147/1149/1150 (+3 tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tell now rebinds beingTold/you/yourself without overwriting me.
Parser implicit targets use beingTold; handler wrapper seeds beingTold=me.
Fixes: attributes refer to the thing being told, does not overwrite me,
your symbol represents the thing being told.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _NO_STEP_LIMIT set exempts hypertrace tests from the 200k step cap
- globalThis.__hs_deadline exposed so cek_step_loop wall-clock check
(every 10k steps) can terminate runaway async loops without needing
to go through host-call or _driveAsync
- meta + _hs-on-caller added to hs-runtime.sx (both lib and bundled):
on-event handlers now set meta.caller to an object with
meta.feature.type = "onFeature" before calling the handler
Tests 196 (async hypertrace), 198 (meta.caller), 199, 200 now pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add hs-tokenize-template: scans " as single STRING token, ${ ... }
as dollar+brace+inner-tokens (inner tokenized with hs-tokenize), and
} as brace-close. Update hs-tokens-of to call hs-tokenize-template
when :template keyword arg is passed. Unlocks tests 1 and 15.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add \r \b \f \v and \xNN escape handling to read-string. Use
char-from-code for non-SX-literal chars. Throw "Unterminated string"
on EOF inside a string literal. Throw "Invalid hexadecimal escape: \x"
on bad \xNN. Add hs-hex-digit? and hs-hex-val helpers. Unlocks
tests 2, 6, 13, 14 once generator lands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add hs-eof-sentinel, hs-op-type, hs-raw->api-token, hs-tokens-of,
hs-stream-token, hs-stream-consume, hs-stream-has-more, and the
three token accessors (hs-token-type, hs-token-value, hs-token-op?).
No test delta yet — API-only, generator comes in step 6.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`1 in [1, 2, 3]` must return (list 1) not true. Root cause: in? compiled
to hs-contains? which returns boolean for scalar items. Fix: new hs-in?
returns filtered list; new in-bool? operator for is/am-in comparison
contexts so those still return boolean. Parser generates in-bool? for
`X is in Y` / `X am in Y`; plain `in` keeps in? → list return.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove guard wrapper from hs-win-call emit (direct call is sufficient now)
- def command also registers fn on window[name] so hs-win-call finds it
- Generator: fix \"-escaped quotes in hs-compile string literal (was splitting "here" into three SX nodes)
- Hand-rolled deftest for 'can refer to function in init blocks' now passes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>