js-num-from-string now finds an e/E split, parses mantissa and exponent
separately, and combines via js-pow-int (positive-exp loop for >=0, 1/
reciprocal for negative). Previously `.12345e-3` parsed as 0.12345 and
"1e3" returned NaN — the parser walked decimals/dots only.
New helpers:
- js-find-exp-char / -loop : linear scan for e/E, returns -1 if absent
- js-pow-int base exp : integer-exp power, handles negative
Also fixed `js-string-trim` typo → `js-trim` in the rewritten num-from-
string, and corrected test 903's expected part count (3, not 2 — the
lexer has always split `hi ${x}!` into str+expr+str, the test just had
the wrong count).
Unit: 521/522 (was 520/522, 934 still blocked on SX \` escape).
Conformance: 148/148 unchanged.
Number scoreboard: 43/100 → 46/100 (+3).
Impacted test262 paths (sample): built-ins/Number/S9.3.1_A11.js and
A12/A16/A17 (".12345e-3", scientific notation round-trips).
Root cause: every sx_server worker session used js-eval on the 3.6KB
HARNESS_STUB, paying ~15s for tokenize+parse+transpile even though every
session does the same thing. Over a full scoreboard with periodic worker
restarts that's minutes of wasted work.
Fix: transpile once per Python process. Spin up a throwaway sx_server,
run (inspect (js-transpile (js-parse (js-tokenize HARNESS_STUB)))), write
the resulting SX source to lib/js/.harness-cache/stub.<fingerprint>.sx and
a stable-name symlink-ish copy stub.sx. Every worker session then does a
single (load .harness-cache/stub.sx) instead of re-running js-eval.
Fingerprint: sha256(HARNESS_STUB + lexer.sx + parser.sx + transpile.sx).
Transpiler edits invalidate the cache automatically. Runs back-to-back
reuse the cache — only the first run after a transpiler change pays the
~15s precompute.
Transpile had to gain a $-to-_js_dollar_ name-mangler: the SX tokenizer
rejects $ in identifiers, which broke round-tripping via inspect. JS
$DONOTEVALUATE → SX _js_dollar_DONOTEVALUATE. Internal JS-on-SX names are
unaffected (none contain $).
Measured: 300-test wide (Math+Number+String @ 100/cat, --per-test-timeout 5):
593.7s → 288.0s, 2.06x speedup. Scoreboard 114→115/300 (38.3%, noise band).
Math 40%, Number 44%, String 30% — same shape as prior.
Baselines: 520/522 unit, 148/148 slice — unchanged.
Many test262 tests write 'assert(condition, msg)' as a plain call, not
'assert.sameValue(...)'. The stub now sets assert.__callable__ to
__assert_call__ so the dispatch in js-call-plain finds a callable.
Also widens verifyNotEnumerable etc. to 5-arg signatures — some tests call
them with (o, name, value, writable, configurable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
classify_error now catches 'unexpected token', 'unexpected char',
'expected ident/punct/keyword' as SyntaxError variants.
classify_negative_result maps parser errors to SyntaxError for negative:parse
tests that expect a SyntaxError. Also maps 'undefined symbol' to
ReferenceError for negative:runtime tests. This reclassifies ~39+36 tests
per wide run from 'fail' to 'pass (negative)'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework test262-runner.py to support --workers N parallel shards, each running
a long-lived sx_server session. Replace thread-per-readline with a select-based
raw-fd line buffer.
On 2-core machines, 1 worker still beats 2 (OCaml eval is CPU-bound and starves
when shared). Auto-defaults n_workers=1 on <=2 CPU, nproc-1 (up to 8) otherwise.
Throughput baseline: ~1.1 Math tests/s serial on 2-core (unchanged; the
evaluator dominates). The runner framework is now ready to scale on bigger
machines without further code changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- runtime hs-add-to!/hs-append: dedupe on list targets (Set semantics)
- compiler emit-set: set result to X now syncs it too
- compiler append!: handle (local)/(ref) targets via emit-set so scoped
vars get rebound to the returned list
- parser add/remove: accept bare @attr (not just [@attr])
- parser add-attr: support when-clause → emits add-attr-when
- compiler add-class-when/add-attr-when: collect matched items into
the-result / it so subsequent "if the result is empty" works
+6 upstream tests in early range (add 13→17, append 10→12).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial commit of the lib/js/ tree and plans/ directory. A previous
session left template-string work in progress — 278/280 unit tests pass
(2 failing: tpl part-count off-by-one, escaped-backtick ident lookup).
test262-runner.py and scoreboard are placeholders (0/8 with 7 timeouts);
fixing the runner is the next queue item.