From 141795449aca64514d23c303bd82162d240b5062 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 03:42:47 +0000 Subject: [PATCH] js-on-sx: parseFloat/parseInt return NaN for digitless prefix --- lib/js/runtime.sx | 11 +++++++++++ plans/js-on-sx.md | 2 ++ 2 files changed, 13 insertions(+) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index 59d77ad6..5fa5abdc 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -4523,8 +4523,19 @@ ((end (js-float-prefix-end s 0 false false false))) (cond ((= end 0) (js-nan-value)) + ((not (js-str-has-digit? s 0 end)) (js-nan-value)) (else (js-parse-num-safe (js-string-slice s 0 end))))))))) +(define + js-str-has-digit? + (fn + (s i n) + (cond + ((>= i n) false) + ((let ((c (char-at s i))) (and (>= (char-code c) 48) (<= (char-code c) 57))) + true) + (else (js-str-has-digit? s (+ i 1) n))))) + (define js-float-has-infinity-prefix? (fn diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index f7951a57..15b95568 100644 --- a/plans/js-on-sx.md +++ b/plans/js-on-sx.md @@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green. Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta. +- 2026-05-09 — **`parseFloat("+")` / `parseFloat("-")` / `parseFloat(".")` return NaN (were returning 0).** `js-float-prefix-end` happily consumed leading `+`/`-` and dot characters even with no digits — and `js-parse-num-safe` of those characters returned 0. Per spec, the prefix must contain at least one digit. Added a `js-str-has-digit?` walker called between `js-float-prefix-end` and `js-parse-num-safe`; if no digit is present in the consumed slice, return NaN. Result: built-ins/parseFloat 20/30 → 23/30, built-ins/parseInt 22/30 → 24/30. Number unchanged. conformance.sh: 148/148. + - 2026-05-09 — **`parseFloat` recognises `"Infinity"` / `"±Infinity"` prefixes (not just exact matches).** Per spec, parseFloat parses the longest StrDecimalLiteral prefix — `Infinity` is one — so `parseFloat("Infinity1")`, `parseFloat("Infinityx")`, `parseFloat("Infinity+1")` should all return `Infinity`. Was only matching `s === "Infinity"` / `"+Infinity"` / `"-Infinity"` exactly. Added `js-float-has-infinity-prefix?` helper and three new branches at the top of `js-parse-float-prefix`. Result: built-ins/parseFloat 17/30 → 20/30. conformance.sh: 148/148. - 2026-05-09 — **JS lexer rejects bare `\` in source (e.g. `{` outside an identifier-escape context).** Was silently advancing past unknown chars in the punctuator-fallback branch, so `{` became `\` (skipped) + ident `u007B`, and `((1))` parsed as something close to `(1)` after our SX-string layer pre-converted half of them. Now `(else (advance! 1))` is a `(error "Unexpected char '\\' in source")` for `\` specifically (other unknown chars still advance — keeps multi-byte UTF-8 idents working at the byte level). Result: language/punctuators 1/11 → 11/11 (full pass), language/literals 25/30 → 28/30, language/identifiers 11/30 → 13/30. Object/Map unchanged. conformance.sh: 148/148.