js-on-sx: expanded Math + Number globals

Math gains sqrt/pow/trunc/sign/cbrt/hypot plus LN2/LN10/LOG2E/
LOG10E/SQRT2/SQRT1_2 constants and full-precision PI/E.

Number global: isFinite/isNaN/isInteger/isSafeInteger plus
MAX_VALUE/MIN_VALUE/MAX_SAFE_INTEGER/MIN_SAFE_INTEGER/EPSILON/
POSITIVE_INFINITY/NEGATIVE_INFINITY/NaN.

Global isFinite, isNaN, Infinity, NaN. Wired into js-global.

329/331 unit (+21), 148/148 slice unchanged.
This commit is contained in:
2026-04-23 20:42:57 +00:00
parent ce46420c2e
commit 608a5088a4
3 changed files with 153 additions and 2 deletions

View File

@@ -1183,7 +1183,86 @@
(define js-math-random (fn () 0))
(define Math {:random js-math-random :floor js-math-floor :PI 3.14159 :round js-math-round :abs js-math-abs :ceil js-math-ceil :max js-math-max :min js-math-min :E 2.71828})
(define js-math-sqrt (fn (x) (sqrt (js-to-number x))))
(define js-math-pow (fn (a b) (pow (js-to-number a) (js-to-number b))))
(define
js-math-trunc
(fn
(x)
(let ((n (js-to-number x))) (if (< n 0) (ceil n) (floor n)))))
(define
js-math-sign
(fn
(x)
(let
((n (js-to-number x)))
(cond ((> n 0) 1) ((< n 0) -1) (else n)))))
(define
js-math-cbrt
(fn
(x)
(let
((n (js-to-number x)))
(if (< n 0) (- 0 (pow (- 0 n) (/ 1 3))) (pow n (/ 1 3))))))
(define js-math-hypot (fn (&rest args) (sqrt (js-math-hypot-loop args 0))))
(define
js-math-hypot-loop
(fn
(args acc)
(if
(empty? args)
acc
(let
((n (js-to-number (first args))))
(js-math-hypot-loop (rest args) (+ acc (* n n)))))))
(define Math {:random js-math-random :trunc js-math-trunc :LN10 2.30259 :SQRT1_2 0.707107 :floor js-math-floor :PI 3.14159 :sqrt js-math-sqrt :hypot js-math-hypot :LOG2E 1.4427 :round js-math-round :ceil js-math-ceil :abs js-math-abs :pow js-math-pow :max js-math-max :LOG10E 0.434294 :SQRT2 1.41421 :cbrt js-math-cbrt :min js-math-min :sign js-math-sign :E 2.71828 :LN2 0.693147})
(define
js-number-is-finite
(fn
(v)
(and
(number? v)
(not (js-number-is-nan v))
(not (= v (/ 1 0)))
(not (= v (/ -1 0))))))
(define js-number-is-nan (fn (v) (and (number? v) (not (= v v)))))
(define
js-number-is-integer
(fn
(v)
(and (number? v) (js-number-is-finite v) (= v (js-math-trunc v)))))
(define
js-number-is-safe-integer
(fn
(v)
(and (js-number-is-integer v) (<= (js-math-abs v) 9007199254740991))))
(define
js-global-is-finite
(fn (v) (js-number-is-finite (js-to-number v))))
(define js-global-is-nan (fn (v) (js-number-is-nan (js-to-number v))))
(define Number {:isFinite js-number-is-finite :MAX_SAFE_INTEGER 9007199254740991 :EPSILON 2.22045e-16 :MAX_VALUE 0 :POSITIVE_INFINITY inf :isInteger js-number-is-integer :isNaN js-number-is-nan :isSafeInteger js-number-is-safe-integer :NEGATIVE_INFINITY -inf :NaN 0 :MIN_VALUE 4.94066e-324 :MIN_SAFE_INTEGER -9007199254740991})
(define isFinite js-global-is-finite)
(define isNaN js-global-is-nan)
(define Infinity inf)
(define NaN 0)
(define __js_microtask_queue__ (dict))
@@ -1686,4 +1765,4 @@
(str "/" (get rx "source") "/" (get rx "flags")))
(else js-undefined))))
(define js-global {:console console :Math Math :NaN 0 :Infinity (/ 1 0) :undefined js-undefined})
(define js-global {:isFinite js-global-is-finite :console console :Number Number :Math Math :NaN 0 :Infinity inf :isNaN js-global-is-nan :undefined js-undefined})

View File

@@ -829,6 +829,52 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 1061)
(eval "(js-eval \"/zzz/.test('hello')\")")
;; ── Phase 11.math: expanded constants + functions ────────────────
(epoch 1100)
(eval "(js-eval \"Math.sqrt(16)\")")
(epoch 1101)
(eval "(js-eval \"Math.pow(2, 10)\")")
(epoch 1102)
(eval "(js-eval \"Math.trunc(3.7)\")")
(epoch 1103)
(eval "(js-eval \"Math.trunc(-3.7)\")")
(epoch 1104)
(eval "(js-eval \"Math.sign(5)\")")
(epoch 1105)
(eval "(js-eval \"Math.sign(-5)\")")
(epoch 1106)
(eval "(js-eval \"Math.sign(0)\")")
(epoch 1107)
(eval "(js-eval \"Math.hypot(3, 4)\")")
(epoch 1108)
(eval "(js-eval \"Math.cbrt(27)\")")
(epoch 1109)
(eval "(js-eval \"Math.PI > 3.14\")")
(epoch 1110)
(eval "(js-eval \"Math.E > 2.7\")")
(epoch 1111)
(eval "(js-eval \"Math.SQRT2 > 1.41\")")
;; ── Phase 11.number: Number builtin ─────────────────────────────
(epoch 1200)
(eval "(js-eval \"Number.isInteger(5)\")")
(epoch 1201)
(eval "(js-eval \"Number.isInteger(5.5)\")")
(epoch 1202)
(eval "(js-eval \"Number.isFinite(5)\")")
(epoch 1203)
(eval "(js-eval \"Number.isFinite(1/0)\")")
(epoch 1204)
(eval "(js-eval \"Number.isSafeInteger(1)\")")
(epoch 1205)
(eval "(js-eval \"Number.MAX_SAFE_INTEGER\")")
(epoch 1206)
(eval "(js-eval \"Number.EPSILON > 0\")")
(epoch 1207)
(eval "(js-eval \"isFinite(1)\")")
(epoch 1208)
(eval "(js-eval \"isFinite(1/0)\")")
EPOCHS
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
@@ -1270,6 +1316,30 @@ check 1053 "literal .ignoreCase" 'true'
check 1060 "test match" 'true'
check 1061 "test no match" 'false'
# ── Phase 11.math: expanded Math ────────────────────────────────
check 1100 "Math.sqrt(16)" '4'
check 1101 "Math.pow(2,10)" '1024'
check 1102 "Math.trunc(3.7)" '3'
check 1103 "Math.trunc(-3.7)" '-3'
check 1104 "Math.sign(5)" '1'
check 1105 "Math.sign(-5)" '-1'
check 1106 "Math.sign(0)" '0'
check 1107 "Math.hypot(3,4)" '5'
check 1108 "Math.cbrt(27)" '3'
check 1109 "Math.PI" 'true'
check 1110 "Math.E" 'true'
check 1111 "Math.SQRT2" 'true'
check 1200 "Number.isInteger(5)" 'true'
check 1201 "Number.isInteger(5.5)" 'false'
check 1202 "Number.isFinite(5)" 'true'
check 1203 "Number.isFinite(Inf)" 'false'
check 1204 "Number.isSafeInteger(1)" 'true'
check 1205 "Number.MAX_SAFE_INTEGER" '9007199254740991'
check 1206 "Number.EPSILON > 0" 'true'
check 1207 "isFinite(1)" 'true'
check 1208 "isFinite(Inf)" 'false'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "$PASS/$TOTAL JS-on-SX tests passed"

View File

@@ -175,6 +175,8 @@ Append-only record of completed iterations. Loop writes one line per iteration:
- 2026-04-23 — **Regex literal support (lex+parse+transpile+runtime stub).** Runner now accepts repeatable `--filter` flags (OR'd). Lexer gains `js-regex-context?` (returns true at SOF or when last token is op/non-closing-punct/regex-keyword incl. return/typeof/in/of/throw/new/delete/instanceof/void/yield/await/case/do/else) and `read-regex` (handles `\` escapes and `[...]` classes, collects flags as ident chars). `scan!` intercepts `/` ahead of the operator-match tries when in a regex context and emits `{:type "regex" :value {:pattern :flags}}`. Parser adds a `regex` primary branch → `(js-regex pat flags)`. Transpile emits `(js-regex-new pat flags)`. Runtime adds: `js-regex?` predicate (dict + `__js_regex__` key), `js-regex-new` builds the tagged dict with `source / flags / global / ignoreCase / multiline / sticky / unicode / dotAll / hasIndices / lastIndex` populated; `js-regex-invoke-method` dispatches `.test` / `.exec` / `.toString`; `js-invoke-method` gets a regex branch before the generic method-lookup fallback. Stub engine (`js-regex-stub-test` / `-exec`) uses `js-string-index-of` — not a real regex, but enough to make `/foo/.test('hi foo')` work. `__js_regex_platform__` dict + `js-regex-platform-override!` let a real platform primitive be swapped in later without runtime changes. 30 new unit tests (17 lex + 3 parse + 1 transpile + 4 obj-shape + 4 prop + 2 test()): **308/310** (278→+30). Conformance unchanged. Gotcha: `contains?` with 2 args expects `(contains? list x)`, NOT a dict — use `(contains? (keys d) k)` or `dict-has?`. First pass forgot that and cascaded errors across Math / class tests via the `js-regex?` predicate inside `js-invoke-method`. Wide scoreboard run across 9 targeted categories launched in background.
- 2026-04-23 — **Expanded Math + Number globals.** Added `Math.sqrt/.pow/.trunc/.sign/.cbrt/.hypot` using SX primitives (`sqrt`, `pow`, `abs`, hand-rolled loops). Added missing constants: `Math.LN2 / LN10 / LOG2E / LOG10E / SQRT2 / SQRT1_2`; bumped PI/E precision to full 16-digit. New `Number` global: `isFinite`, `isNaN`, `isInteger`, `isSafeInteger`, `MAX_VALUE / MIN_VALUE / MAX_SAFE_INTEGER / MIN_SAFE_INTEGER / EPSILON / POSITIVE_INFINITY / NEGATIVE_INFINITY / NaN`. Global `isFinite`, `isNaN`, `Infinity`, `NaN`. `js-number-is-nan` uses the self-inequality trick `(and (number? v) (not (= v v)))`. Wired into `js-global`. 21 new unit tests (12 Math + 9 Number), **329/331** (308→+21). Conformance unchanged. Gotchas: (1) `sx_insert_near` takes a single node — multi-define source blocks get silently truncated. Use `sx_insert_child` at the root per define. (2) SX `(/ 1 0)``inf`, and `1e999` also → `inf`; both can be used as `Infinity`. (3) `1e999` has no `-` form — wrap as `(- 0 1e999)` or just use `-1e999` literal.
## Phase 3-5 gotchas
Worth remembering for later phases: