From 00edae49e4e103c984effc0caecd8fb60262f91c Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 12:14:47 +0000 Subject: [PATCH] =?UTF-8?q?js-on-sx:=20hex-literal=20string=E2=86=92number?= =?UTF-8?q?=20coercion=20(+15=20Number)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ES spec: ToNumber("0x0") == 0, ToNumber("0xFF") == 255. Our parser returned NaN for anything with a 0x/0X prefix, failing S9.3.1_A16..A32 (every hex-literal assertion test case). Added: - js-hex-prefix? — is this an 0x/0X prefix? - js-is-hex-body? — all remaining chars are [0-9a-fA-F]? - js-parse-hex — walk chars, accumulate in base 16, NaN on bad char - js-hex-digit-value — char → 0..15 (or -1) js-is-numeric-string? short-circuits to the hex-body check on 0x* prefix; js-num-from-string dispatches to js-parse-hex on same. Unit 521/522, slice 148/148 unchanged. Number scoreboard: 58/100 → 73/100 (+15). Sample flipped: S9.3.1_A16 (0x0/0X0), A17..A31 (0x0..0xF and 0x10..0x1F range coverage). Many of the remaining 22 fails are (new Number()).x style prototype-chain introspection and MAX_VALUE precision. --- lib/js/runtime.sx | 63 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index b8bbf7b8..48a647d7 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -833,7 +833,22 @@ (define js-is-numeric-string? - (fn (s) (js-is-numeric-loop s 0 false false false))) + (fn + (s) + (if + (js-hex-prefix? s) + (js-is-hex-body? s 2 (len s)) + (js-is-numeric-loop s 0 false false false)))) + +(define + js-is-hex-body? + (fn + (s i n) + (cond + ((>= i n) (> n 2)) + ((>= (js-hex-digit-value (char-at s i)) 0) + (js-is-hex-body? s (+ i 1) n)) + (else false)))) (define js-is-numeric-loop @@ -886,6 +901,51 @@ ((> exp 0) (* base (js-pow-int base (- exp 1)))) (else (/ 1 (js-pow-int base (- 0 exp))))))) +(define + js-hex-prefix? + (fn + (s) + (and + (>= (len s) 2) + (= (char-at s 0) "0") + (or (= (char-at s 1) "x") (= (char-at s 1) "X"))))) + +(define + js-parse-hex + (fn + (s i acc) + (cond + ((>= i (len s)) acc) + (else + (let + ((c (char-at s i)) (d (js-hex-digit-value (char-at s i)))) + (cond + ((< d 0) (js-nan-value)) + (else (js-parse-hex s (+ i 1) (+ (* acc 16) d))))))))) + +(define + js-hex-digit-value + (fn + (c) + (cond + ((= c "0") 0) + ((= c "1") 1) + ((= c "2") 2) + ((= c "3") 3) + ((= c "4") 4) + ((= c "5") 5) + ((= c "6") 6) + ((= c "7") 7) + ((= c "8") 8) + ((= c "9") 9) + ((or (= c "a") (= c "A")) 10) + ((or (= c "b") (= c "B")) 11) + ((or (= c "c") (= c "C")) 12) + ((or (= c "d") (= c "D")) 13) + ((or (= c "e") (= c "E")) 14) + ((or (= c "f") (= c "F")) 15) + (else -1)))) + (define js-num-from-string (fn @@ -894,6 +954,7 @@ ((trimmed (js-trim s))) (cond ((= trimmed "") 0) + ((js-hex-prefix? trimmed) (js-parse-hex trimmed 2 0)) (else (let ((esplit (js-find-exp-char trimmed)))