js-on-sx: Math.X.name / Number.X.name via SX→JS name unmap (+4)
Every built-in JS function on Math/Number/Array/Object had .name === "" because js-invoke-function-method/js-get-prop returned bare "" for the "name" slot. That breaks tests like Math.abs.name === "abs" and Array.isArray.name === "isArray". Fix: extract the SX symbol name from (inspect fn) which prints <js-math-abs(x)>, then unmap through a small string table that maps js-math-abs → "abs", js-array-is-array → "isArray" etc. Also strips the angle-bracket marker and stops at ( or space. Non-mapped lambdas (user fns) fall through to the raw "js-foo" form rather than "", which is slightly worse but only hit in debug prints. Unit 521/522, slice 148/148 unchanged. Scoreboard: Math 40/100 → 43/100 (+3); Number 74 → 75 (+1). Sample: Math/abs/name.js, Math/floor/name.js, Math/max/name.js, Number/isNaN/name.js — all flipped. length.js tests still fail for trig because the underlying fn isn't implemented.
This commit is contained in:
@@ -115,6 +115,95 @@
|
||||
;; sign — 1 or -1
|
||||
;; frac? — are we past the decimal point
|
||||
;; fdiv — divisor used to scale fraction digits (only if frac?)
|
||||
(define
|
||||
js-extract-fn-name
|
||||
(fn (f) (let ((raw (inspect f))) (js-strip-fn-name raw 0 (len raw)))))
|
||||
|
||||
(define
|
||||
js-strip-fn-name
|
||||
(fn
|
||||
(s i n)
|
||||
(let
|
||||
((start (if (and (< i n) (= (char-at s i) "<")) (+ i 1) i)))
|
||||
(js-strip-fn-name-end s start n))))
|
||||
|
||||
(define
|
||||
js-strip-fn-name-end
|
||||
(fn
|
||||
(s start n)
|
||||
(let
|
||||
((end (js-find-paren-or-space s start n)))
|
||||
(let ((name (js-string-slice s start end))) (js-unmap-fn-name name)))))
|
||||
|
||||
;; ── String coercion (ToString) ────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-find-paren-or-space
|
||||
(fn
|
||||
(s i n)
|
||||
(cond
|
||||
((>= i n) n)
|
||||
((or (= (char-at s i) "(") (= (char-at s i) " ")) i)
|
||||
(else (js-find-paren-or-space s (+ i 1) n)))))
|
||||
|
||||
(define
|
||||
js-unmap-fn-name
|
||||
(fn
|
||||
(name)
|
||||
(cond
|
||||
((= name "js-math-abs") "abs")
|
||||
((= name "js-math-floor") "floor")
|
||||
((= name "js-math-ceil") "ceil")
|
||||
((= name "js-math-round") "round")
|
||||
((= name "js-math-max") "max")
|
||||
((= name "js-math-min") "min")
|
||||
((= name "js-math-random") "random")
|
||||
((= name "js-math-sqrt") "sqrt")
|
||||
((= name "js-math-pow") "pow")
|
||||
((= name "js-math-trunc") "trunc")
|
||||
((= name "js-math-sign") "sign")
|
||||
((= name "js-math-cbrt") "cbrt")
|
||||
((= name "js-math-hypot") "hypot")
|
||||
((= name "js-number-is-finite") "isFinite")
|
||||
((= name "js-number-is-nan") "isNaN")
|
||||
((= name "js-number-is-integer") "isInteger")
|
||||
((= name "js-number-is-safe-integer") "isSafeInteger")
|
||||
((= name "js-global-is-finite") "isFinite")
|
||||
((= name "js-global-is-nan") "isNaN")
|
||||
((= name "js-string-from-char-code") "fromCharCode")
|
||||
((= name "js-array-is-array") "isArray")
|
||||
((= name "js-array-of") "of")
|
||||
((= name "js-array-from") "from")
|
||||
((= name "js-object-keys") "keys")
|
||||
((= name "js-object-values") "values")
|
||||
((= name "js-object-entries") "entries")
|
||||
((= name "js-object-assign") "assign")
|
||||
((= name "js-object-freeze") "freeze")
|
||||
((= name "js-object-get-prototype-of") "getPrototypeOf")
|
||||
((= name "js-object-set-prototype-of") "setPrototypeOf")
|
||||
((= name "js-object-create") "create")
|
||||
((= name "js-object-define-property") "defineProperty")
|
||||
((= name "js-object-define-properties") "defineProperties")
|
||||
((= name "js-object-get-own-property-names") "getOwnPropertyNames")
|
||||
((= name "js-object-get-own-property-descriptor")
|
||||
"getOwnPropertyDescriptor")
|
||||
((= name "js-object-get-own-property-descriptors")
|
||||
"getOwnPropertyDescriptors")
|
||||
((= name "js-object-is-extensible") "isExtensible")
|
||||
((= name "js-object-is-frozen") "isFrozen")
|
||||
((= name "js-object-is-sealed") "isSealed")
|
||||
((= name "js-object-seal") "seal")
|
||||
((= name "js-object-prevent-extensions") "preventExtensions")
|
||||
((= name "js-object-is") "is")
|
||||
((= name "js-object-from-entries") "fromEntries")
|
||||
((= name "js-object-has-own") "hasOwn")
|
||||
((= name "js-to-number") "Number")
|
||||
((= name "js-to-boolean") "Boolean")
|
||||
(else name))))
|
||||
|
||||
;; ── Arithmetic (JS rules) ─────────────────────────────────────────
|
||||
|
||||
;; JS `+`: if either operand is a string → string concat, else numeric.
|
||||
(define
|
||||
js-count-real-params
|
||||
(fn
|
||||
@@ -162,7 +251,7 @@
|
||||
(&rest more)
|
||||
(js-call-with-this this-arg recv (js-list-concat bound more)))))
|
||||
((= key "toString") "function () { [native code] }")
|
||||
((= key "name") "")
|
||||
((= key "name") (js-extract-fn-name recv))
|
||||
((= key "length") (js-fn-length recv))
|
||||
(else :js-undefined))))
|
||||
|
||||
@@ -179,8 +268,6 @@
|
||||
(fn (&rest args) (js-invoke-function-method recv "bind" args)))
|
||||
(else :js-undefined))))
|
||||
|
||||
;; ── String coercion (ToString) ────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-invoke-number-method
|
||||
(fn
|
||||
@@ -226,9 +313,6 @@
|
||||
((= key "toLocaleString") "function () { [native code] }")
|
||||
(else :js-undefined))))
|
||||
|
||||
;; ── Arithmetic (JS rules) ─────────────────────────────────────────
|
||||
|
||||
;; JS `+`: if either operand is a string → string concat, else numeric.
|
||||
(define
|
||||
js-invoke-boolean-method
|
||||
(fn
|
||||
@@ -272,6 +356,7 @@
|
||||
((d (mod n radix)) (rest (js-math-trunc (/ n radix))))
|
||||
(js-num-to-str-radix-rec rest radix (str (js-digit-char d) acc))))))
|
||||
|
||||
;; Bitwise + logical-not
|
||||
(define
|
||||
js-digit-char
|
||||
(fn
|
||||
@@ -315,6 +400,9 @@
|
||||
(js-to-string (js-math-trunc frac-part))
|
||||
d))))))))))))
|
||||
|
||||
;; ── Equality ──────────────────────────────────────────────────────
|
||||
|
||||
;; Strict equality (===): no coercion; js-undefined matches js-undefined.
|
||||
(define
|
||||
js-pow-int
|
||||
(fn (b e) (if (<= e 0) 1 (* b (js-pow-int b (- e 1))))))
|
||||
@@ -323,6 +411,9 @@
|
||||
js-pad-int-str
|
||||
(fn (s n) (if (>= (len s) n) s (js-pad-int-str (str "0" s) n))))
|
||||
|
||||
;; Abstract equality (==): type coercion rules.
|
||||
;; Simplified: number↔string coerce both to number; null == undefined;
|
||||
;; everything else falls back to strict equality.
|
||||
(define
|
||||
js-apply-fn
|
||||
(fn
|
||||
@@ -354,7 +445,6 @@
|
||||
(nth args 5)))
|
||||
(else (apply callable args))))))
|
||||
|
||||
;; Bitwise + logical-not
|
||||
(define
|
||||
js-invoke-method
|
||||
(fn
|
||||
@@ -380,6 +470,11 @@
|
||||
(error
|
||||
(str "TypeError: " (js-to-string key) " is not a function")))))))))
|
||||
|
||||
;; ── Relational comparisons ────────────────────────────────────────
|
||||
|
||||
;; Abstract relational comparison from ES5.
|
||||
;; Numbers compare numerically; two strings compare lexicographically;
|
||||
;; mixed types coerce both to numbers.
|
||||
(define
|
||||
js-object-builtin-method?
|
||||
(fn
|
||||
@@ -392,9 +487,6 @@
|
||||
(= name "valueOf")
|
||||
(= name "toLocaleString"))))
|
||||
|
||||
;; ── Equality ──────────────────────────────────────────────────────
|
||||
|
||||
;; Strict equality (===): no coercion; js-undefined matches js-undefined.
|
||||
(define
|
||||
js-invoke-object-method
|
||||
(fn
|
||||
@@ -418,9 +510,6 @@
|
||||
|
||||
(define js-upper-case (fn (s) (js-case-loop s 0 "" true)))
|
||||
|
||||
;; Abstract equality (==): type coercion rules.
|
||||
;; Simplified: number↔string coerce both to number; null == undefined;
|
||||
;; everything else falls back to strict equality.
|
||||
(define js-lower-case (fn (s) (js-case-loop s 0 "" false)))
|
||||
|
||||
(define
|
||||
@@ -438,11 +527,6 @@
|
||||
((cv (cond ((and to-upper? (>= cc 97) (<= cc 122)) (js-code-to-char (- cc 32))) ((and (not to-upper?) (>= cc 65) (<= cc 90)) (js-code-to-char (+ cc 32))) (else c))))
|
||||
(js-case-loop s (+ i 1) (str acc cv) to-upper?))))))))
|
||||
|
||||
;; ── Relational comparisons ────────────────────────────────────────
|
||||
|
||||
;; Abstract relational comparison from ES5.
|
||||
;; Numbers compare numerically; two strings compare lexicographically;
|
||||
;; mixed types coerce both to numbers.
|
||||
(define
|
||||
js-code-to-char
|
||||
(fn
|
||||
@@ -506,6 +590,13 @@
|
||||
js-invoke-method-dyn
|
||||
(fn (recv key args) (js-invoke-method recv key args)))
|
||||
|
||||
;; ── Property access ───────────────────────────────────────────────
|
||||
|
||||
;; obj[key] or obj.key in JS. Handles:
|
||||
;; • dicts keyed by string
|
||||
;; • lists indexed by number (incl. .length)
|
||||
;; • strings indexed by number (incl. .length)
|
||||
;; Returns js-undefined if the key is absent.
|
||||
(define
|
||||
js-call-plain
|
||||
(fn
|
||||
@@ -545,6 +636,7 @@
|
||||
((proto (js-get-ctor-proto ctor)))
|
||||
(js-instanceof-walk obj proto))))))
|
||||
|
||||
;; Setter — mutates the dict. Returns the new value (JS assignment yields rhs).
|
||||
(define
|
||||
js-instanceof-walk
|
||||
(fn
|
||||
@@ -560,6 +652,10 @@
|
||||
((not (= (type-of p) "dict")) false)
|
||||
(else (js-instanceof-walk p proto))))))))
|
||||
|
||||
;; ── Short-circuit logical ops ─────────────────────────────────────
|
||||
|
||||
;; `a && b` in JS: if a is truthy return b else return a. The thunk
|
||||
;; form defers evaluation of b — the transpiler passes (fn () b).
|
||||
(define
|
||||
js-in
|
||||
(fn
|
||||
@@ -568,13 +664,6 @@
|
||||
((not (= (type-of obj) "dict")) false)
|
||||
(else (js-in-walk obj (js-to-string key))))))
|
||||
|
||||
;; ── Property access ───────────────────────────────────────────────
|
||||
|
||||
;; obj[key] or obj.key in JS. Handles:
|
||||
;; • dicts keyed by string
|
||||
;; • lists indexed by number (incl. .length)
|
||||
;; • strings indexed by number (incl. .length)
|
||||
;; Returns js-undefined if the key is absent.
|
||||
(define
|
||||
js-in-walk
|
||||
(fn
|
||||
@@ -585,6 +674,9 @@
|
||||
((dict-has? obj "__proto__") (js-in-walk (get obj "__proto__") skey))
|
||||
(else false))))
|
||||
|
||||
;; ── console.log ───────────────────────────────────────────────────
|
||||
|
||||
;; Trivial bridge. `log-info` is available on OCaml; fall back to print.
|
||||
(define
|
||||
Error
|
||||
(fn
|
||||
@@ -621,7 +713,8 @@
|
||||
nil)
|
||||
this))))
|
||||
|
||||
;; Setter — mutates the dict. Returns the new value (JS assignment yields rhs).
|
||||
;; ── Math object ───────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
RangeError
|
||||
(fn
|
||||
@@ -639,11 +732,6 @@
|
||||
(dict-set! this "name" "RangeError"))
|
||||
nil)
|
||||
this))))
|
||||
|
||||
;; ── Short-circuit logical ops ─────────────────────────────────────
|
||||
|
||||
;; `a && b` in JS: if a is truthy return b else return a. The thunk
|
||||
;; form defers evaluation of b — the transpiler passes (fn () b).
|
||||
(define
|
||||
SyntaxError
|
||||
(fn
|
||||
@@ -661,7 +749,6 @@
|
||||
(dict-set! this "name" "SyntaxError"))
|
||||
nil)
|
||||
this))))
|
||||
|
||||
(define
|
||||
ReferenceError
|
||||
(fn
|
||||
@@ -679,10 +766,6 @@
|
||||
(dict-set! this "name" "ReferenceError"))
|
||||
nil)
|
||||
this))))
|
||||
|
||||
;; ── console.log ───────────────────────────────────────────────────
|
||||
|
||||
;; Trivial bridge. `log-info` is available on OCaml; fall back to print.
|
||||
(define
|
||||
URIError
|
||||
(fn
|
||||
@@ -700,7 +783,6 @@
|
||||
(if (empty? args) "" (js-to-string (nth args 0))))
|
||||
(dict-set! this "name" "URIError")))
|
||||
this))))
|
||||
|
||||
(define
|
||||
EvalError
|
||||
(fn
|
||||
@@ -718,9 +800,6 @@
|
||||
(if (empty? args) "" (js-to-string (nth args 0))))
|
||||
(dict-set! this "name" "EvalError")))
|
||||
this))))
|
||||
|
||||
;; ── Math object ───────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-function?
|
||||
(fn
|
||||
@@ -734,7 +813,8 @@
|
||||
(and (= t "dict") (contains? (keys v) "__callable__"))))))
|
||||
(define __js_proto_table__ (dict))
|
||||
(define __js_next_id__ (dict))
|
||||
(dict-set! __js_next_id__ "n" 0)
|
||||
(dict-set! __js_next_id__ "n" 0) ; deterministic placeholder for tests
|
||||
|
||||
(define
|
||||
js-get-ctor-proto
|
||||
(fn
|
||||
@@ -751,6 +831,11 @@
|
||||
(let
|
||||
((p (dict)))
|
||||
(begin (dict-set! __js_proto_table__ id p) p)))))))))
|
||||
|
||||
;; The global object — lookup table for JS names that aren't in the
|
||||
;; SX env. Transpiled idents look up locally first; globals here are a
|
||||
;; fallback, but most slice programs reference `console`, `Math`,
|
||||
;; `undefined` as plain symbols, which we bind as defines above.
|
||||
(define
|
||||
js-reset-ctor-proto!
|
||||
(fn
|
||||
@@ -758,11 +843,13 @@
|
||||
(let
|
||||
((id (js-ctor-id ctor)) (p (dict)))
|
||||
(begin (dict-set! __js_proto_table__ id p) p))))
|
||||
|
||||
(define
|
||||
js-set-ctor-proto!
|
||||
(fn
|
||||
(ctor proto)
|
||||
(let ((id (js-ctor-id ctor))) (dict-set! __js_proto_table__ id proto))))
|
||||
|
||||
(define
|
||||
js-ctor-id
|
||||
(fn
|
||||
@@ -771,6 +858,7 @@
|
||||
((and (= (type-of ctor) "dict") (dict-has? ctor "__ctor_id__"))
|
||||
(get ctor "__ctor_id__"))
|
||||
(else (inspect ctor)))))
|
||||
|
||||
(define
|
||||
js-typeof
|
||||
(fn
|
||||
@@ -786,7 +874,7 @@
|
||||
((= (type-of v) "component") "function")
|
||||
((and (= (type-of v) "dict") (contains? (keys v) "__callable__"))
|
||||
"function")
|
||||
(else "object")))) ; deterministic placeholder for tests
|
||||
(else "object"))))
|
||||
|
||||
(define
|
||||
js-to-boolean
|
||||
@@ -800,10 +888,6 @@
|
||||
((= v "") false)
|
||||
(else true))))
|
||||
|
||||
;; The global object — lookup table for JS names that aren't in the
|
||||
;; SX env. Transpiled idents look up locally first; globals here are a
|
||||
;; fallback, but most slice programs reference `console`, `Math`,
|
||||
;; `undefined` as plain symbols, which we bind as defines above.
|
||||
(define
|
||||
js-to-number
|
||||
(fn
|
||||
@@ -2180,7 +2264,7 @@
|
||||
((and (js-function? obj) (or (= key "prototype") (= key "name") (= key "length") (= key "call") (= key "apply") (= key "bind")))
|
||||
(cond
|
||||
((= key "prototype") (js-get-ctor-proto obj))
|
||||
((= key "name") "")
|
||||
((= key "name") (js-extract-fn-name obj))
|
||||
((= key "length") (js-fn-length obj))
|
||||
(else (js-invoke-function-bound obj key))))
|
||||
(else js-undefined))))
|
||||
|
||||
Reference in New Issue
Block a user