parse-int "2.0" returns nil in SX (strict integer parse); fixed by adding
tcl-num-float? (char scan for ./e/E) and tcl-parse-num (routes to
parse-float when float-shaped). Applied in tcl-apply-binop (all arith +
comparisons), tcl-apply-func (parse-float for all math args), unary minus,
and tcl-expr-parse-power (**). Real sqrt/floor/ceil/round/pow/sin/cos/tan/
exp/log now used instead of integer stubs. Integer division still truncates
when both operands are integer-shaped. 329/329 tests green.
Parser: limit `from SOURCE` to parse-collection/cmp/arith/poss/atom
(stops before parse-logical so `or` is not consumed as binary op),
then collect `or EVENT from SOURCE` pairs via recursive collect-ors!.
Adds :or-sources key to the on-feature parts list.
Compiler: scan-on gains or-sources param (11th); new :or-sources cond
clause extracts the list; terminal `true` branch wraps on-call in
(do on-call (hs-on target event handler) ...) for each extra source.
Test: "can handle an or after a from clause" moved from skip-list to
MANUAL_TEST_BODIES and now passes (1478/1496).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When `from #doesntExist` resolves to nil, hs-on silently skips
listener registration instead of crashing on dom-listen nil.
Removes "can ignore when target doesn't exist" from skip-list.
Also adds host-make-js-thrower native utility (plain JS throwing
function, no K.callFn re-entry) — investigated for the js-exceptions
catch test but that test stays skipped: native JS throws from host
calls escape OCaml WASM try-with guards.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parser: bracket-open in obj-collect key cond → (computed-key expr).
Compiler: detect computed-key list at object-literal pair key and compile
the inner expression instead of emitting a literal string.
Generator: special case for 'expressions work in object literal field names'
using eval-hs-locals with host-callback so hs-win-call can find the fn.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JIT compilation on first call to many functions incurs a step cost of
200–600k CEK steps. The 200k default was silently failing ~70 tests
across suites like hs-upstream-default, hs-upstream-on, comparisonOp,
and others that work correctly but need JIT warmup headroom. Raising
to 1M reveals all of these as passing. The hypertrace/repeat-forever
tests that are genuinely unbounded remain in _NO_STEP_LIMIT.
Full suite scan (all ranges) now shows 1475/1496 (21 pre-existing
SKIP/untranslated failures, 0 actual failures).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parser parses optional deriving clause; only appended to AST when non-empty.
hk-bind-decls! data arm generates dictShow_Con / dictEq_Con per constructor.
hk-binop == and /= now deep-force both sides (SX dict equality is by
reference — two thunks wrapping the same value compared as not-equal without
this). Three token-type fixes in the deriving parser (lparen/rparen/comma,
not "special").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hs-upstream-core/sourceInfo tests "get source works for expressions"
and "get line works for statements" each call hs-parse-ast which runs
the full parser with span-mode enabled, creating ~15 wrapped AST nodes
and linking :next fields. The total CEK step count exceeds the 200k
default but terminates correctly around 400-500k steps. Adding the
suite to _NO_STEP_LIMIT_SUITES (no cap) lets both tests pass.
The other two sourceInfo tests were already passing. 4/4 now.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements namespace eval, current, which, exists, delete, export,
import, forget, path, and ensemble create (auto-map + -map). Procs
defined inside namespace eval are stored as fully-qualified names
(::ns::proc), resolved relative to the calling namespace at lookup
time. Proc bodies execute in their defining namespace so sibling
calls work without qualification.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds lib/tcl/conformance.sh: runs .tcl programs through the epoch
protocol, compares against # expected: annotations, writes
scoreboard.json and scoreboard.md. All 3 classic programs pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 3 headline feature: everything falls out of SX's first-class env chain.
- make-tcl-interp extended with :frame-stack and :procs fields
- proc: user-defined commands with param binding, rest args, isolated scope
- uplevel: run script in ancestor frame with correct frame propagation
- upvar: alias local name to remote frame variable (get/set follow alias)
- global/variable: sugar for upvar #0
- info: level, vars, locals, globals, commands, procs, args, body
- tcl-call-proc propagates updated frames back to caller after proc returns
- test.sh timeout bumped to 90s for larger runtime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pl-hs-query, pl-hs-predicate/1,2,3, pl-hs-install in hs-bridge.sx.
No parser/compiler changes: Hyperscript already compiles
`when allowed(user, action)` to (allowed user action).
Total 590/590.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bind: verify $nope stays nil when binding to a plain div (compile→nil).
when: verify myVar produces when-feat-no-op (parse-error detected).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hs-coerce HTML list case: use outerHTML for element items, not str.
hs-coerce Fragment case: actually build a DocumentFragment — element
items are appended directly; strings are parsed via a temp div.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JIT saturation after multiple compilations in the 13-test suite
causes tests 818-819 to time out at 10s.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests that call eval-expr-cek twice before the assertion take 7–12 s
cold on the WASM kernel. The 10 s wall-clock deadline fires during the
second warmup call, leaving the kernel in a partially-compiled state
that silently broke adjacent tests (e.g. "loop continue works" started
producing empty output rather than the expected string).
Add 60 s entries to _SLOW_DEADLINE for:
- behavior scoping is isolated from other/core element scope (×2)
- repeat suite preheat tests: can nest loops, only executes init once,
repeat forever (w/ and w/o keyword), until keyword works,
while keyword works (×6)
All eight suites now pass 100 %:
hs-upstream-core/scoping 20/20
hs-upstream-repeat 29/29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>