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:
@@ -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})
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user