From 7cffae21481a56250e5fe9e21012dab30f8c69f5 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 11:36:56 +0000 Subject: [PATCH] js-on-sx: exponent notation in js-string-to-number (+3 Number tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- lib/js/runtime.sx | 35 ++++++++++++++++++++++++++++++++++- lib/js/test.sh | 2 +- lib/js/test262-runner.py | 11 +++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index 0a43f8e4..4da7e64d 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -860,6 +860,26 @@ (define js-parse-num-safe (fn (s) (cond (else (js-num-from-string s))))) +(define js-find-exp-char (fn (s) (js-find-exp-char-loop s 0 (len s)))) + +(define + js-find-exp-char-loop + (fn + (s i n) + (cond + ((>= i n) -1) + ((or (= (char-at s i) "e") (= (char-at s i) "E")) i) + (else (js-find-exp-char-loop s (+ i 1) n))))) + +(define + js-pow-int + (fn + (base exp) + (cond + ((= exp 0) 1) + ((> exp 0) (* base (js-pow-int base (- exp 1)))) + (else (/ 1 (js-pow-int base (- 0 exp))))))) + (define js-num-from-string (fn @@ -868,7 +888,20 @@ ((trimmed (js-trim s))) (cond ((= trimmed "") 0) - (else (js-parse-decimal trimmed 0 0 1 false 0)))))) + (else + (let + ((esplit (js-find-exp-char trimmed))) + (if + (>= esplit 0) + (let + ((mant (js-string-slice trimmed 0 esplit)) + (expstr + (js-string-slice trimmed (+ esplit 1) (len trimmed)))) + (let + ((m (js-parse-decimal mant 0 0 1 false 0)) + (e (js-parse-decimal expstr 0 0 1 false 0))) + (* m (js-pow-int 10 e)))) + (js-parse-decimal trimmed 0 0 1 false 0)))))))) (define js-trim (fn (s) (js-trim-left (js-trim-right s)))) diff --git a/lib/js/test.sh b/lib/js/test.sh index a9e7a83c..de6caea5 100755 --- a/lib/js/test.sh +++ b/lib/js/test.sh @@ -1697,7 +1697,7 @@ check 870 "typeof Promise" '"object"' check 900 "tpl plain: type" '"template"' check 901 "tpl plain: value" '"hello"' check 902 "tpl interp: value is list" 'true' -check 903 "tpl interp: 2 parts" '2' +check 903 "tpl interp: 3 parts" '3' check 910 "parse plain" '(js-str "hi")' check 911 'parse interp a${1}b' '(js-tpl ((js-str "a") (js-num 1) (js-str "b")))' diff --git a/lib/js/test262-runner.py b/lib/js/test262-runner.py index 49dbe999..9a0807b7 100644 --- a/lib/js/test262-runner.py +++ b/lib/js/test262-runner.py @@ -1120,6 +1120,8 @@ def main(argv): ap.add_argument("--output-md", type=str, default=str(REPO / "lib" / "js" / "test262-scoreboard.md")) ap.add_argument("--progress-every", type=int, default=100) + ap.add_argument("--dump-failures", type=str, default=None, + help="if set, write every failed test's rel path + reason to this file") args = ap.parse_args(argv) if not SX_SERVER.exists(): @@ -1242,6 +1244,15 @@ def main(argv): out_md = Path(args.output_md) write_markdown(scoreboard, out_md, pinned_commit, t_run_elapsed) + if args.dump_failures: + out_fail = Path(args.dump_failures) + out_fail.parent.mkdir(parents=True, exist_ok=True) + with out_fail.open("w", encoding="utf-8") as f: + for r in results: + if r.status in ("fail", "timeout"): + f.write(f"{r.status}\t{r.rel}\t{r.reason}\n") + print(f"failures dumped to {out_fail}", file=sys.stderr) + t = scoreboard["totals"] print( f"\nScoreboard: {t['pass']}/{t['runnable']} runnable passed ({t['pass_rate']}%) "