Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 22s
js-unmap-fn-name had mappings for older Math methods but not the trig/hyperbolic/log family added later. Added 22 mappings for sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, exp, log, log2, log10, expm1, log1p, clz32, imul, fround. built-ins/Math: 42/45 → 45/45 (100%). conformance.sh: 148/148.
4593 lines
131 KiB
Plaintext
4593 lines
131 KiB
Plaintext
;; lib/js/runtime.sx — JS semantics shims
|
|
;;
|
|
;; Coercions, abstract equality, arithmetic with JS rules, and the
|
|
;; global object live here. Transpiled code (from lib/js/transpile.sx)
|
|
;; compiles to calls into these helpers.
|
|
;;
|
|
;; Phase 4 status: core coercions, arithmetic, comparison, equality,
|
|
;; property access, `console.log`, Math shim wired up. Enough to make
|
|
;; the expression-level test262 slice green.
|
|
|
|
;; ── JS value sentinels ────────────────────────────────────────────
|
|
|
|
;; JS `undefined` — we represent it as a distinct keyword so it
|
|
;; survives round-trips through the evaluator without colliding with
|
|
;; SX `nil` (which maps to JS `null`).
|
|
(define js-nan-value (fn () nan))
|
|
|
|
(define js-infinity-value (fn () inf))
|
|
|
|
;; ── Type predicates ───────────────────────────────────────────────
|
|
|
|
(define js-max-value-approx (fn () 1.7976931348623157e+308))
|
|
|
|
;; ── Boolean coercion (ToBoolean) ──────────────────────────────────
|
|
|
|
(define js-function-global {:__callable__ (fn (&rest args) (error "TypeError: Function constructor not supported")) :prototype {:call (fn (&rest args) :js-undefined) :length 0 :bind (fn (&rest args) (fn () :js-undefined)) :toString (fn () "function () { [native code] }") :apply (fn (&rest args) :js-undefined) :name ""}})
|
|
|
|
;; ── Numeric coercion (ToNumber) ───────────────────────────────────
|
|
|
|
(dict-set!
|
|
(get js-function-global "prototype")
|
|
"isPrototypeOf"
|
|
(fn (x) (js-function? x)))
|
|
|
|
;; Parse a JS-style string to a number. For the slice we just delegate
|
|
;; to SX's number parser via `str->num`/`parse-number`. Empty string → 0
|
|
;; per JS (technically ToNumber("") === 0).
|
|
(define
|
|
js-global-eval
|
|
(fn (&rest args) (if (empty? args) :js-undefined (nth args 0))))
|
|
|
|
;; Safe number-parser. Tries to call an SX primitive that can parse
|
|
;; strings to numbers; on failure returns 0 (stand-in for NaN so the
|
|
;; slice doesn't crash).
|
|
(define
|
|
js-max-value-loop
|
|
(fn
|
|
(cur steps)
|
|
(if
|
|
(<= steps 0)
|
|
cur
|
|
(let
|
|
((next (* cur 2)))
|
|
(if
|
|
(= next (js-infinity-value))
|
|
cur
|
|
(js-max-value-loop next (- steps 1)))))))
|
|
|
|
;; Minimal string->number for the slice. Handles integers, negatives,
|
|
;; and simple decimals. Returns 0 on malformed input.
|
|
(define js-undefined :js-undefined)
|
|
|
|
(define js-undefined? (fn (v) (= v :js-undefined)))
|
|
|
|
(define __js_this_cell__ (dict))
|
|
|
|
(define
|
|
js-this
|
|
(fn
|
|
()
|
|
(if
|
|
(dict-has? __js_this_cell__ "this")
|
|
(get __js_this_cell__ "this")
|
|
:js-undefined)))
|
|
|
|
(define js-this-set! (fn (v) (dict-set! __js_this_cell__ "this" v)))
|
|
|
|
(define
|
|
js-call-with-this
|
|
(fn
|
|
(recv fn-val args)
|
|
(let
|
|
((saved (js-this)))
|
|
(begin
|
|
(js-this-set! recv)
|
|
(let
|
|
((result (js-apply-fn fn-val args)))
|
|
(begin (js-this-set! saved) result))))))
|
|
|
|
(define
|
|
js-function-method?
|
|
(fn
|
|
(name)
|
|
(or
|
|
(= name "call")
|
|
(= name "apply")
|
|
(= name "bind")
|
|
(= name "toString")
|
|
(= name "name")
|
|
(= name "length"))))
|
|
|
|
;; parse a decimal number from a trimmed non-empty string.
|
|
;; s — source
|
|
;; i — cursor
|
|
;; acc — integer part so far (or total for decimals)
|
|
;; sign — 1 or -1
|
|
;; frac? — are we past the decimal point
|
|
;; fdiv — divisor used to scale fraction digits (only if frac?)
|
|
(define
|
|
js-fn-length
|
|
(fn
|
|
(f)
|
|
(let
|
|
((t (type-of f)))
|
|
(cond
|
|
((= t "lambda") (js-count-real-params (lambda-params f)))
|
|
((= t "function") 0)
|
|
((= t "component") 0)
|
|
((and (= t "dict") (contains? (keys f) "__callable__"))
|
|
(js-fn-length (get f "__callable__")))
|
|
(else 0)))))
|
|
|
|
(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))))
|
|
|
|
;; ── String coercion (ToString) ────────────────────────────────────
|
|
|
|
(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)))))
|
|
|
|
(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)))))
|
|
|
|
;; ── Arithmetic (JS rules) ─────────────────────────────────────────
|
|
|
|
;; JS `+`: if either operand is a string → string concat, else numeric.
|
|
(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-math-sin") "sin")
|
|
((= name "js-math-cos") "cos")
|
|
((= name "js-math-tan") "tan")
|
|
((= name "js-math-asin") "asin")
|
|
((= name "js-math-acos") "acos")
|
|
((= name "js-math-atan") "atan")
|
|
((= name "js-math-atan2") "atan2")
|
|
((= name "js-math-sinh") "sinh")
|
|
((= name "js-math-cosh") "cosh")
|
|
((= name "js-math-tanh") "tanh")
|
|
((= name "js-math-asinh") "asinh")
|
|
((= name "js-math-acosh") "acosh")
|
|
((= name "js-math-atanh") "atanh")
|
|
((= name "js-math-exp") "exp")
|
|
((= name "js-math-log") "log")
|
|
((= name "js-math-log2") "log2")
|
|
((= name "js-math-log10") "log10")
|
|
((= name "js-math-expm1") "expm1")
|
|
((= name "js-math-log1p") "log1p")
|
|
((= name "js-math-clz32") "clz32")
|
|
((= name "js-math-imul") "imul")
|
|
((= name "js-math-fround") "fround")
|
|
((= 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))))
|
|
|
|
(define
|
|
js-count-real-params
|
|
(fn
|
|
(params)
|
|
(cond
|
|
((empty? params) 0)
|
|
(else
|
|
(let
|
|
((first (first params)))
|
|
(if
|
|
(= first "&rest")
|
|
0
|
|
(+ 1 (js-count-real-params (rest params)))))))))
|
|
|
|
(define
|
|
js-invoke-function-method
|
|
(fn
|
|
(recv key args)
|
|
(cond
|
|
((= key "call")
|
|
(let
|
|
((this-arg (if (< (len args) 1) :js-undefined (nth args 0)))
|
|
(rest
|
|
(if
|
|
(< (len args) 1)
|
|
(list)
|
|
(js-list-slice args 1 (len args)))))
|
|
(js-call-with-this this-arg recv rest)))
|
|
((= key "apply")
|
|
(let
|
|
((this-arg (if (< (len args) 1) :js-undefined (nth args 0)))
|
|
(arr
|
|
(if (< (len args) 2) (list) (nth args 1))))
|
|
(let
|
|
((rest (cond ((= arr nil) (list)) ((js-undefined? arr) (list)) ((list? arr) arr) (else (js-iterable-to-list arr)))))
|
|
(js-call-with-this this-arg recv rest))))
|
|
((= key "bind")
|
|
(let
|
|
((this-arg (if (< (len args) 1) :js-undefined (nth args 0)))
|
|
(bound
|
|
(if
|
|
(< (len args) 1)
|
|
(list)
|
|
(js-list-slice args 1 (len args)))))
|
|
(fn
|
|
(&rest more)
|
|
(js-call-with-this this-arg recv (js-list-concat bound more)))))
|
|
((= key "toString") "function () { [native code] }")
|
|
((= key "name") (js-extract-fn-name recv))
|
|
((= key "length") (js-fn-length recv))
|
|
(else :js-undefined))))
|
|
|
|
(define
|
|
js-invoke-function-bound
|
|
(fn
|
|
(recv key)
|
|
(cond
|
|
((= key "call")
|
|
(fn (&rest args) (js-invoke-function-method recv "call" args)))
|
|
((= key "apply")
|
|
(fn (&rest args) (js-invoke-function-method recv "apply" args)))
|
|
((= key "bind")
|
|
(fn (&rest args) (js-invoke-function-method recv "bind" args)))
|
|
(else :js-undefined))))
|
|
|
|
(define
|
|
js-invoke-number-method
|
|
(fn
|
|
(recv key args)
|
|
(cond
|
|
((= key "toString")
|
|
(let
|
|
((radix (if (empty? args) 10 (nth args 0))))
|
|
(js-num-to-str-radix
|
|
recv
|
|
(if
|
|
(or (= radix nil) (js-undefined? radix))
|
|
10
|
|
(js-to-number radix)))))
|
|
((= key "toFixed")
|
|
(js-number-to-fixed
|
|
recv
|
|
(if (empty? args) 0 (js-to-number (nth args 0)))))
|
|
((= key "valueOf") recv)
|
|
((= key "toLocaleString") (js-to-string recv))
|
|
((= key "toPrecision") (js-to-string recv))
|
|
((= key "toExponential") (js-to-string recv))
|
|
(else
|
|
(error
|
|
(str
|
|
"TypeError: "
|
|
(js-to-string key)
|
|
" is not a function (on number)"))))))
|
|
|
|
(define
|
|
js-invoke-function-objproto
|
|
(fn
|
|
(recv key args)
|
|
(cond
|
|
((= key "hasOwnProperty")
|
|
(let
|
|
((k (if (empty? args) "" (js-to-string (nth args 0)))))
|
|
(or (= k "name") (= k "length") (= k "prototype"))))
|
|
((= key "toString") "function () { [native code] }")
|
|
((= key "valueOf") recv)
|
|
((= key "isPrototypeOf") false)
|
|
((= key "propertyIsEnumerable") false)
|
|
((= key "toLocaleString") "function () { [native code] }")
|
|
(else :js-undefined))))
|
|
|
|
(define
|
|
js-invoke-boolean-method
|
|
(fn
|
|
(recv key args)
|
|
(cond
|
|
((= key "toString") (if recv "true" "false"))
|
|
((= key "valueOf") recv)
|
|
(else
|
|
(error
|
|
(str
|
|
"TypeError: "
|
|
(js-to-string key)
|
|
" is not a function (on boolean)"))))))
|
|
|
|
(define
|
|
js-num-to-str-radix
|
|
(fn
|
|
(n radix)
|
|
(cond
|
|
((and (number? n) (not (= n n))) "NaN")
|
|
((= n (/ 1 0)) "Infinity")
|
|
((= n (/ -1 0)) "-Infinity")
|
|
((or (= radix 10) (= radix nil) (js-undefined? radix))
|
|
(js-to-string n))
|
|
(else
|
|
(let
|
|
((int-n (js-math-trunc n)))
|
|
(if
|
|
(< int-n 0)
|
|
(str "-" (js-num-to-str-radix-rec (- 0 int-n) radix ""))
|
|
(js-num-to-str-radix-rec int-n radix "")))))))
|
|
|
|
;; Bitwise + logical-not
|
|
(define
|
|
js-num-to-str-radix-rec
|
|
(fn
|
|
(n radix acc)
|
|
(if
|
|
(= n 0)
|
|
(if (= acc "") "0" acc)
|
|
(let
|
|
((d (mod n radix)) (rest (js-math-trunc (/ n radix))))
|
|
(js-num-to-str-radix-rec rest radix (str (js-digit-char d) acc))))))
|
|
|
|
(define
|
|
js-digit-char
|
|
(fn
|
|
(d)
|
|
(cond
|
|
((< d 10) (js-to-string d))
|
|
(else
|
|
(let
|
|
((offset (+ 97 (- d 10))))
|
|
(js-code-to-char offset))))))
|
|
|
|
;; ── Equality ──────────────────────────────────────────────────────
|
|
|
|
;; Strict equality (===): no coercion; js-undefined matches js-undefined.
|
|
(define
|
|
js-number-to-fixed
|
|
(fn
|
|
(n digits)
|
|
(cond
|
|
((js-number-is-nan n) "NaN")
|
|
((= n (js-infinity-value)) "Infinity")
|
|
((= n (- 0 (js-infinity-value))) "-Infinity")
|
|
(else
|
|
(let
|
|
((d (js-math-trunc digits)))
|
|
(if
|
|
(< d 1)
|
|
(js-to-string (js-math-round n))
|
|
(let
|
|
((scale (js-pow-int 10 d)))
|
|
(let
|
|
((scaled (js-math-round (* n scale))))
|
|
(let
|
|
((abs-scaled (if (< scaled 0) (- 0 scaled) scaled))
|
|
(sign (if (< scaled 0) "-" "")))
|
|
(let
|
|
((int-part (js-math-trunc (/ abs-scaled scale)))
|
|
(frac-part
|
|
(-
|
|
abs-scaled
|
|
(* (js-math-trunc (/ abs-scaled scale)) scale))))
|
|
(str
|
|
sign
|
|
(js-to-string int-part)
|
|
"."
|
|
(js-pad-int-str
|
|
(js-to-string (js-math-trunc frac-part))
|
|
d))))))))))))
|
|
|
|
(define
|
|
js-pow-int
|
|
(fn
|
|
(b e)
|
|
(if (<= e 0) 1 (* b (js-pow-int b (- e 1))))))
|
|
|
|
;; Abstract equality (==): type coercion rules.
|
|
;; Simplified: number↔string coerce both to number; null == undefined;
|
|
;; everything else falls back to strict equality.
|
|
(define
|
|
js-pad-int-str
|
|
(fn (s n) (if (>= (len s) n) s (js-pad-int-str (str "0" s) n))))
|
|
|
|
(define
|
|
js-apply-fn
|
|
(fn
|
|
(fn-val args)
|
|
(let
|
|
((callable (if (and (dict? fn-val) (contains? (keys fn-val) "__callable__")) (get fn-val "__callable__") fn-val)))
|
|
(if
|
|
(not (js-function? callable))
|
|
(raise
|
|
(js-new-call
|
|
TypeError
|
|
(list (str (type-of fn-val) " is not a function"))))
|
|
(cond
|
|
((= (len args) 0) (callable))
|
|
((= (len args) 1) (callable (nth args 0)))
|
|
((= (len args) 2)
|
|
(callable (nth args 0) (nth args 1)))
|
|
((= (len args) 3)
|
|
(callable
|
|
(nth args 0)
|
|
(nth args 1)
|
|
(nth args 2)))
|
|
((= (len args) 4)
|
|
(callable
|
|
(nth args 0)
|
|
(nth args 1)
|
|
(nth args 2)
|
|
(nth args 3)))
|
|
((= (len args) 5)
|
|
(callable
|
|
(nth args 0)
|
|
(nth args 1)
|
|
(nth args 2)
|
|
(nth args 3)
|
|
(nth args 4)))
|
|
((= (len args) 6)
|
|
(callable
|
|
(nth args 0)
|
|
(nth args 1)
|
|
(nth args 2)
|
|
(nth args 3)
|
|
(nth args 4)
|
|
(nth args 5)))
|
|
(else (apply callable args)))))))
|
|
|
|
;; ── Relational comparisons ────────────────────────────────────────
|
|
|
|
;; Abstract relational comparison from ES5.
|
|
;; Numbers compare numerically; two strings compare lexicographically;
|
|
;; mixed types coerce both to numbers.
|
|
(define
|
|
js-invoke-method
|
|
(fn
|
|
(recv key args)
|
|
(cond
|
|
((and (js-promise? recv) (js-promise-builtin-method? key))
|
|
(js-invoke-promise-method recv key args))
|
|
((js-regex? recv) (js-regex-invoke-method recv key args))
|
|
((number? recv) (js-invoke-number-method recv key args))
|
|
((boolean? recv) (js-invoke-boolean-method recv key args))
|
|
(else
|
|
(let
|
|
((m (js-get-prop recv key)))
|
|
(cond
|
|
((not (js-undefined? m)) (js-call-with-this recv m args))
|
|
((and (js-function? recv) (js-function-method? key))
|
|
(js-invoke-function-method recv key args))
|
|
((and (js-function? recv) (not (= (type-of recv) "dict")) (js-object-builtin-method? key))
|
|
(js-invoke-function-objproto recv key args))
|
|
((and (= (type-of recv) "dict") (js-object-builtin-method? key))
|
|
(js-invoke-object-method recv key args))
|
|
(else
|
|
(error
|
|
(str "TypeError: " (js-to-string key) " is not a function")))))))))
|
|
|
|
(define
|
|
js-object-builtin-method?
|
|
(fn
|
|
(name)
|
|
(or
|
|
(= name "hasOwnProperty")
|
|
(= name "isPrototypeOf")
|
|
(= name "propertyIsEnumerable")
|
|
(= name "toString")
|
|
(= name "valueOf")
|
|
(= name "toLocaleString"))))
|
|
|
|
(define
|
|
js-invoke-object-method
|
|
(fn
|
|
(recv name args)
|
|
(cond
|
|
((= name "hasOwnProperty")
|
|
(if
|
|
(= (len args) 0)
|
|
false
|
|
(contains? (keys recv) (js-to-string (nth args 0)))))
|
|
((= name "isPrototypeOf") false)
|
|
((= name "propertyIsEnumerable")
|
|
(if
|
|
(= (len args) 0)
|
|
false
|
|
(contains? (keys recv) (js-to-string (nth args 0)))))
|
|
((= name "toString") "[object Object]")
|
|
((= name "valueOf") recv)
|
|
((= name "toLocaleString") "[object Object]")
|
|
(else js-undefined))))
|
|
|
|
(define js-upper-case (fn (s) (js-case-loop s 0 "" true)))
|
|
|
|
(define js-lower-case (fn (s) (js-case-loop s 0 "" false)))
|
|
|
|
(define
|
|
js-case-loop
|
|
(fn
|
|
(s i acc to-upper?)
|
|
(cond
|
|
((>= i (len s)) acc)
|
|
(else
|
|
(let
|
|
((c (char-at s i)))
|
|
(let
|
|
((cc (char-code c)))
|
|
(let
|
|
((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?))))))))
|
|
|
|
(define
|
|
js-code-to-char
|
|
(fn
|
|
(code)
|
|
(cond
|
|
((= code 65) "A")
|
|
((= code 66) "B")
|
|
((= code 67) "C")
|
|
((= code 68) "D")
|
|
((= code 69) "E")
|
|
((= code 70) "F")
|
|
((= code 71) "G")
|
|
((= code 72) "H")
|
|
((= code 73) "I")
|
|
((= code 74) "J")
|
|
((= code 75) "K")
|
|
((= code 76) "L")
|
|
((= code 77) "M")
|
|
((= code 78) "N")
|
|
((= code 79) "O")
|
|
((= code 80) "P")
|
|
((= code 81) "Q")
|
|
((= code 82) "R")
|
|
((= code 83) "S")
|
|
((= code 84) "T")
|
|
((= code 85) "U")
|
|
((= code 86) "V")
|
|
((= code 87) "W")
|
|
((= code 88) "X")
|
|
((= code 89) "Y")
|
|
((= code 90) "Z")
|
|
((= code 97) "a")
|
|
((= code 98) "b")
|
|
((= code 99) "c")
|
|
((= code 100) "d")
|
|
((= code 101) "e")
|
|
((= code 102) "f")
|
|
((= code 103) "g")
|
|
((= code 104) "h")
|
|
((= code 105) "i")
|
|
((= code 106) "j")
|
|
((= code 107) "k")
|
|
((= code 108) "l")
|
|
((= code 109) "m")
|
|
((= code 110) "n")
|
|
((= code 111) "o")
|
|
((= code 112) "p")
|
|
((= code 113) "q")
|
|
((= code 114) "r")
|
|
((= code 115) "s")
|
|
((= code 116) "t")
|
|
((= code 117) "u")
|
|
((= code 118) "v")
|
|
((= code 119) "w")
|
|
((= code 120) "x")
|
|
((= code 121) "y")
|
|
((= code 122) "z")
|
|
(else ""))))
|
|
|
|
;; ── 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-invoke-method-dyn
|
|
(fn (recv key args) (js-invoke-method recv key args)))
|
|
|
|
(define
|
|
js-call-plain
|
|
(fn
|
|
(fn-val args)
|
|
(cond
|
|
((js-undefined? fn-val)
|
|
(raise (js-new-call TypeError (list "undefined is not a function"))))
|
|
((and (dict? fn-val) (contains? (keys fn-val) "__callable__"))
|
|
(js-call-with-this :js-undefined (get fn-val "__callable__") args))
|
|
(else (js-call-with-this :js-undefined fn-val args)))))
|
|
|
|
(define
|
|
js-new-call
|
|
(fn
|
|
(ctor args)
|
|
(let
|
|
((obj (dict)))
|
|
(begin
|
|
(dict-set! obj "__proto__" (js-get-ctor-proto ctor))
|
|
(let
|
|
((ret (js-call-with-this obj ctor args)))
|
|
(if
|
|
(and
|
|
(not (js-undefined? ret))
|
|
(or (= (type-of ret) "dict") (= (type-of ret) "list") (js-function? ret)))
|
|
ret
|
|
obj))))))
|
|
|
|
;; Setter — mutates the dict. Returns the new value (JS assignment yields rhs).
|
|
(define
|
|
js-instanceof
|
|
(fn
|
|
(obj ctor)
|
|
(cond
|
|
((not (= (type-of obj) "dict")) false)
|
|
((not (js-function? ctor))
|
|
(error "TypeError: Right-hand side of instanceof is not callable"))
|
|
(else
|
|
(let
|
|
((proto (js-get-ctor-proto ctor)))
|
|
(js-instanceof-walk obj 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-instanceof-walk
|
|
(fn
|
|
(obj proto)
|
|
(cond
|
|
((not (= (type-of obj) "dict")) false)
|
|
((not (dict-has? obj "__proto__")) false)
|
|
(else
|
|
(let
|
|
((p (get obj "__proto__")))
|
|
(cond
|
|
((= p proto) true)
|
|
((not (= (type-of p) "dict")) false)
|
|
(else (js-instanceof-walk p proto))))))))
|
|
|
|
(define
|
|
js-in
|
|
(fn
|
|
(key obj)
|
|
(cond
|
|
((not (= (type-of obj) "dict")) false)
|
|
(else (js-in-walk obj (js-to-string key))))))
|
|
|
|
;; ── console.log ───────────────────────────────────────────────────
|
|
|
|
;; Trivial bridge. `log-info` is available on OCaml; fall back to print.
|
|
(define
|
|
js-in-walk
|
|
(fn
|
|
(obj skey)
|
|
(cond
|
|
((not (= (type-of obj) "dict")) false)
|
|
((dict-has? obj skey) true)
|
|
((dict-has? obj "__proto__") (js-in-walk (get obj "__proto__") skey))
|
|
(else false))))
|
|
|
|
(define
|
|
Error
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= (type-of this) "dict")
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0))))
|
|
(dict-set! this "name" "Error"))
|
|
nil)
|
|
this))))
|
|
|
|
;; ── Math object ───────────────────────────────────────────────────
|
|
|
|
(define
|
|
TypeError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= (type-of this) "dict")
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0))))
|
|
(dict-set! this "name" "TypeError"))
|
|
nil)
|
|
this))))
|
|
(define
|
|
RangeError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= (type-of this) "dict")
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0))))
|
|
(dict-set! this "name" "RangeError"))
|
|
nil)
|
|
this))))
|
|
(define
|
|
SyntaxError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= (type-of this) "dict")
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0))))
|
|
(dict-set! this "name" "SyntaxError"))
|
|
nil)
|
|
this))))
|
|
(define
|
|
ReferenceError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= (type-of this) "dict")
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0))))
|
|
(dict-set! this "name" "ReferenceError"))
|
|
nil)
|
|
this))))
|
|
(define
|
|
URIError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= this :js-undefined)
|
|
nil
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if (empty? args) "" (js-to-string (nth args 0))))
|
|
(dict-set! this "name" "URIError")))
|
|
this))))
|
|
(define
|
|
EvalError
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this (js-this)))
|
|
(begin
|
|
(if
|
|
(= this :js-undefined)
|
|
nil
|
|
(do
|
|
(dict-set!
|
|
this
|
|
"message"
|
|
(if (empty? args) "" (js-to-string (nth args 0))))
|
|
(dict-set! this "name" "EvalError")))
|
|
this))))
|
|
(define
|
|
js-function?
|
|
(fn
|
|
(v)
|
|
(let
|
|
((t (type-of v)))
|
|
(or
|
|
(= t "lambda")
|
|
(= t "function")
|
|
(= t "component")
|
|
(and (= t "dict") (contains? (keys v) "__callable__"))))))
|
|
(define __js_proto_table__ (dict))
|
|
(define __js_next_id__ (dict)) ; deterministic placeholder for tests
|
|
|
|
(dict-set! __js_next_id__ "n" 0)
|
|
|
|
;; 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-get-ctor-proto
|
|
(fn
|
|
(ctor)
|
|
(cond
|
|
((and (= (type-of ctor) "dict") (contains? (keys ctor) "prototype"))
|
|
(get ctor "prototype"))
|
|
(else
|
|
(let
|
|
((id (js-ctor-id ctor)))
|
|
(cond
|
|
((dict-has? __js_proto_table__ id) (get __js_proto_table__ id))
|
|
(else
|
|
(let
|
|
((p (dict)))
|
|
(begin (dict-set! __js_proto_table__ id p) p)))))))))
|
|
|
|
(define
|
|
js-reset-ctor-proto!
|
|
(fn
|
|
(ctor)
|
|
(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
|
|
(ctor)
|
|
(cond
|
|
((and (= (type-of ctor) "dict") (dict-has? ctor "__ctor_id__"))
|
|
(get ctor "__ctor_id__"))
|
|
(else (inspect ctor)))))
|
|
|
|
(define
|
|
js-typeof
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((js-undefined? v) "undefined")
|
|
((= v nil) "object")
|
|
((= (type-of v) "boolean") "boolean")
|
|
((= (type-of v) "number") "number")
|
|
((= (type-of v) "string") "string")
|
|
((= (type-of v) "lambda") "function")
|
|
((= (type-of v) "function") "function")
|
|
((= (type-of v) "component") "function")
|
|
((and (= (type-of v) "dict") (contains? (keys v) "__callable__"))
|
|
"function")
|
|
(else "object"))))
|
|
|
|
(define
|
|
js-to-boolean
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((js-undefined? v) false)
|
|
((= v nil) false)
|
|
((= v false) false)
|
|
((= v 0) false)
|
|
((= v "") false)
|
|
(else true))))
|
|
|
|
(define
|
|
js-to-number
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((js-undefined? v) (js-nan-value))
|
|
((= v nil) 0)
|
|
((= v true) 1)
|
|
((= v false) 0)
|
|
((= (type-of v) "number") v)
|
|
((= (type-of v) "string") (js-string-to-number v))
|
|
((= (type-of v) "dict")
|
|
(cond
|
|
((contains? (keys v) "__js_number_value__")
|
|
(get v "__js_number_value__"))
|
|
((contains? (keys v) "__js_boolean_value__")
|
|
(if (get v "__js_boolean_value__") 1 0))
|
|
((contains? (keys v) "__js_string_value__")
|
|
(js-string-to-number (get v "__js_string_value__")))
|
|
(else
|
|
(let
|
|
((valueof-fn (js-get-prop v "valueOf")))
|
|
(if
|
|
(= (type-of valueof-fn) "lambda")
|
|
(let
|
|
((result (js-call-with-this v valueof-fn ())))
|
|
(if
|
|
(not (= (type-of result) "dict"))
|
|
(js-to-number result)
|
|
(let
|
|
((tostr-fn (js-get-prop v "toString")))
|
|
(if
|
|
(= (type-of tostr-fn) "lambda")
|
|
(let
|
|
((result2 (js-call-with-this v tostr-fn ())))
|
|
(if
|
|
(not (= (type-of result2) "dict"))
|
|
(js-to-number result2)
|
|
(js-nan-value)))
|
|
(js-nan-value)))))
|
|
(js-nan-value))))))
|
|
(else 0))))
|
|
|
|
(define
|
|
js-string-to-number
|
|
(fn
|
|
(s)
|
|
(let
|
|
((trimmed (js-trim s)))
|
|
(cond
|
|
((= trimmed "") 0)
|
|
((= trimmed "Infinity") (js-infinity-value))
|
|
((= trimmed "+Infinity") (js-infinity-value))
|
|
((= trimmed "-Infinity") (- 0 (js-infinity-value)))
|
|
((js-is-numeric-string? trimmed) (js-parse-num-safe trimmed))
|
|
(else (js-nan-value))))))
|
|
|
|
(define
|
|
js-is-numeric-string?
|
|
(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
|
|
(fn
|
|
(s i sawdigit sawdot sawe)
|
|
(cond
|
|
((>= i (len s)) sawdigit)
|
|
(else
|
|
(let
|
|
((c (char-at s i)))
|
|
(cond
|
|
((or (= c "0") (= c "1") (= c "2") (= c "3") (= c "4") (= c "5") (= c "6") (= c "7") (= c "8") (= c "9"))
|
|
(js-is-numeric-loop s (+ i 1) true sawdot sawe))
|
|
((and (= c ".") (not sawdot) (not sawe))
|
|
(js-is-numeric-loop s (+ i 1) sawdigit true sawe))
|
|
((and (or (= c "e") (= c "E")) sawdigit (not sawe))
|
|
(js-is-numeric-loop s (+ i 1) false sawdot true))
|
|
((and (or (= c "+") (= c "-")) (= i 0))
|
|
(js-is-numeric-loop s (+ i 1) sawdigit sawdot sawe))
|
|
((and (or (= c "+") (= c "-")) sawe (= i (- (len s) 1)))
|
|
false)
|
|
((and (or (= c "+") (= c "-")) sawe)
|
|
(let
|
|
((prev (char-at s (- i 1))))
|
|
(if
|
|
(or (= prev "e") (= prev "E"))
|
|
(js-is-numeric-loop s (+ i 1) sawdigit sawdot sawe)
|
|
false)))
|
|
(else false)))))))
|
|
|
|
(define js-parse-num-safe (fn (s) (cond (else (js-num-from-string s)))))
|
|
|
|
(define
|
|
js-find-exp-char
|
|
(fn (s) (js-find-exp-char-loop s 0 (len s))))
|
|
|
|
(define
|
|
js-find-exp-char-loop
|
|
(fn
|
|
(s i n)
|
|
(cond
|
|
((>= i n) -1)
|
|
((or (= (char-at s i) "e") (= (char-at s i) "E")) i)
|
|
(else (js-find-exp-char-loop s (+ i 1) n)))))
|
|
|
|
(define
|
|
js-pow-int
|
|
(fn
|
|
(base exp)
|
|
(cond
|
|
((= exp 0) 1)
|
|
((> 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
|
|
(s)
|
|
(let
|
|
((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)))
|
|
(if
|
|
(>= esplit 0)
|
|
(let
|
|
((parsed (string->number trimmed)))
|
|
(if
|
|
(= parsed nil)
|
|
(let
|
|
((mant (js-string-slice trimmed 0 esplit))
|
|
(expstr
|
|
(js-string-slice
|
|
trimmed
|
|
(+ esplit 1)
|
|
(len trimmed))))
|
|
(let
|
|
((m (js-parse-decimal mant 0 0 1 false 0))
|
|
(e
|
|
(js-parse-decimal
|
|
expstr
|
|
0
|
|
0
|
|
1
|
|
false
|
|
0)))
|
|
(* m (pow 10 e))))
|
|
parsed))
|
|
(js-parse-decimal
|
|
trimmed
|
|
0
|
|
0
|
|
1
|
|
false
|
|
0))))))))
|
|
|
|
(define js-args (fn (&rest args) args))
|
|
|
|
(define js-trim (fn (s) (js-trim-left (js-trim-right s))))
|
|
|
|
(define
|
|
js-trim-left
|
|
(fn (s) (let ((n (len s))) (js-trim-left-at s n 0))))
|
|
|
|
(define
|
|
js-trim-left-at
|
|
(fn
|
|
(s n i)
|
|
(cond
|
|
((>= i n) "")
|
|
((js-is-space? (char-at s i)) (js-trim-left-at s n (+ i 1)))
|
|
(else (substr s i n)))))
|
|
|
|
(define
|
|
js-trim-right
|
|
(fn (s) (let ((n (len s))) (js-trim-right-at s n))))
|
|
|
|
(define
|
|
js-trim-right-at
|
|
(fn
|
|
(s n)
|
|
(cond
|
|
((<= n 0) "")
|
|
((js-is-space? (char-at s (- n 1)))
|
|
(js-trim-right-at s (- n 1)))
|
|
(else (substr s 0 n)))))
|
|
|
|
(define
|
|
js-is-space?
|
|
(fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
|
|
|
(define
|
|
js-parse-decimal
|
|
(fn
|
|
(s i acc sign frac? fdiv)
|
|
(let
|
|
((n (len s)))
|
|
(cond
|
|
((>= i n) (* sign (if frac? (/ acc fdiv) acc)))
|
|
((and (= i 0) (= (char-at s 0) "-"))
|
|
(js-parse-decimal
|
|
s
|
|
1
|
|
0
|
|
-1
|
|
false
|
|
0))
|
|
((and (= i 0) (= (char-at s 0) "+"))
|
|
(js-parse-decimal
|
|
s
|
|
1
|
|
0
|
|
1
|
|
false
|
|
0))
|
|
((= (char-at s i) ".")
|
|
(js-parse-decimal s (+ i 1) acc sign true 1))
|
|
((js-is-digit? (char-at s i))
|
|
(if
|
|
frac?
|
|
(js-parse-decimal
|
|
s
|
|
(+ i 1)
|
|
(+ (* acc 10) (js-digit-val (char-at s i)))
|
|
sign
|
|
true
|
|
(* fdiv 10))
|
|
(js-parse-decimal
|
|
s
|
|
(+ i 1)
|
|
(+ (* acc 10) (js-digit-val (char-at s i)))
|
|
sign
|
|
false
|
|
0)))
|
|
(else (* sign (if frac? (/ acc fdiv) acc)))))))
|
|
|
|
(define
|
|
js-is-digit?
|
|
(fn
|
|
(c)
|
|
(and
|
|
(or
|
|
(= c "0")
|
|
(= c "1")
|
|
(= c "2")
|
|
(= c "3")
|
|
(= c "4")
|
|
(= c "5")
|
|
(= c "6")
|
|
(= c "7")
|
|
(= c "8")
|
|
(= c "9")))))
|
|
|
|
(define
|
|
js-digit-val
|
|
(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)
|
|
(else 0))))
|
|
|
|
(define
|
|
js-to-string
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((js-undefined? v) "undefined")
|
|
((= v nil) "null")
|
|
((= v true) "true")
|
|
((= v false) "false")
|
|
((= (type-of v) "string") v)
|
|
((= (type-of v) "number") (js-number-to-string v))
|
|
(else
|
|
(if
|
|
(= (type-of v) "dict")
|
|
(cond
|
|
((contains? (keys v) "__js_string_value__")
|
|
(get v "__js_string_value__"))
|
|
((contains? (keys v) "__js_number_value__")
|
|
(js-number-to-string (get v "__js_number_value__")))
|
|
((contains? (keys v) "__js_boolean_value__")
|
|
(if (get v "__js_boolean_value__") "true" "false"))
|
|
(else
|
|
(let
|
|
((tostr-fn (js-get-prop v "toString")))
|
|
(if
|
|
(= (type-of tostr-fn) "lambda")
|
|
(let
|
|
((result (js-call-with-this v tostr-fn ())))
|
|
(if
|
|
(= (type-of result) "dict")
|
|
(let
|
|
((valueof-fn (js-get-prop v "valueOf")))
|
|
(if
|
|
(= (type-of valueof-fn) "lambda")
|
|
(let
|
|
((result2 (js-call-with-this v valueof-fn ())))
|
|
(if
|
|
(= (type-of result2) "dict")
|
|
(raise
|
|
(js-new-call
|
|
TypeError
|
|
(list
|
|
"Cannot convert object to primitive value")))
|
|
(js-to-string result2)))
|
|
"[object Object]"))
|
|
(js-to-string result)))
|
|
"[object Object]"))))
|
|
(if (= (type-of v) "list") (js-list-join v ",") (str v)))))))
|
|
|
|
(define
|
|
js-template-concat
|
|
(fn (&rest parts) (js-template-concat-loop parts 0 "")))
|
|
|
|
(define
|
|
js-template-concat-loop
|
|
(fn
|
|
(parts i acc)
|
|
(if
|
|
(>= i (len parts))
|
|
acc
|
|
(js-template-concat-loop
|
|
parts
|
|
(+ i 1)
|
|
(str acc (js-to-string (nth parts i)))))))
|
|
|
|
(define
|
|
js-big-int-str-loop
|
|
(fn
|
|
(n acc)
|
|
(if
|
|
(< n 1)
|
|
(if (= acc "") "0" acc)
|
|
(let
|
|
((d (floor (- n (* 10 (floor (/ n 10)))))))
|
|
(js-big-int-str-loop
|
|
(floor (/ n 10))
|
|
(str (js-string-slice "0123456789" d (+ d 1)) acc))))))
|
|
|
|
(define
|
|
js-find-decimal-k
|
|
(fn
|
|
(n k)
|
|
(if
|
|
(> k 17)
|
|
17
|
|
(let
|
|
((big-int (round (* n (js-pow-int 10 k)))))
|
|
(if
|
|
(= (/ big-int (js-pow-int 10 k)) n)
|
|
k
|
|
(js-find-decimal-k n (+ k 1)))))))
|
|
|
|
(define
|
|
js-format-decimal-digits
|
|
(fn
|
|
(digits k)
|
|
(if
|
|
(= k 0)
|
|
digits
|
|
(let
|
|
((dlen (len digits)))
|
|
(if
|
|
(> dlen k)
|
|
(str
|
|
(js-string-slice digits 0 (- dlen k))
|
|
"."
|
|
(js-string-slice digits (- dlen k) dlen))
|
|
(if
|
|
(= dlen k)
|
|
(str "0." digits)
|
|
(str "0." (js-string-repeat "0" (- k dlen)) digits)))))))
|
|
|
|
(define
|
|
js-expand-sci-notation
|
|
(fn
|
|
(mant exp-n)
|
|
(let
|
|
((di (js-string-index-of mant "." 0)))
|
|
(let
|
|
((int-part (if (< di 0) mant (js-string-slice mant 0 di)))
|
|
(frac-part
|
|
(if
|
|
(< di 0)
|
|
""
|
|
(js-string-slice mant (+ di 1) (len mant)))))
|
|
(let
|
|
((all-digits (str int-part frac-part))
|
|
(frac-len
|
|
(if
|
|
(< di 0)
|
|
0
|
|
(- (- (len mant) di) 1))))
|
|
(if
|
|
(>= exp-n 0)
|
|
(if
|
|
(>= exp-n frac-len)
|
|
(str all-digits (js-string-repeat "0" (- exp-n frac-len)))
|
|
(let
|
|
((dot-pos (+ (len int-part) exp-n)))
|
|
(str
|
|
(js-string-slice all-digits 0 dot-pos)
|
|
"."
|
|
(js-string-slice all-digits dot-pos (len all-digits)))))
|
|
(str
|
|
"0."
|
|
(js-string-repeat "0" (- (- 0 exp-n) 1))
|
|
all-digits)))))))
|
|
|
|
(define
|
|
js-number-to-string
|
|
(fn
|
|
(n)
|
|
(cond
|
|
((js-number-is-nan n) "NaN")
|
|
((= n (js-infinity-value)) "Infinity")
|
|
((= n (- 0 (js-infinity-value))) "-Infinity")
|
|
(else
|
|
(let
|
|
((pos-n (if (< n 0) (- 0 n) n)))
|
|
(let
|
|
((s0 (js-normalize-num-str (str pos-n))))
|
|
(let
|
|
((n2 (js-to-number s0)))
|
|
(let
|
|
((precise (if (= n2 pos-n) (let ((ei (js-string-index-of s0 "e" 0))) (if (< ei 0) s0 (let ((exp-n (js-to-number (js-string-slice s0 (+ ei 1) (len s0))))) (if (and (>= exp-n -6) (<= exp-n 20)) (js-expand-sci-notation (js-string-slice s0 0 ei) exp-n) (if (>= exp-n 0) (str (js-string-slice s0 0 (+ ei 1)) "+" (str exp-n)) s0))))) (if (and (>= pos-n 1e-06) (< pos-n 1e+21)) (let ((k (js-find-decimal-k pos-n 0))) (let ((big-int (round (* pos-n (js-pow-int 10 k))))) (js-format-decimal-digits (js-big-int-str-loop big-int "") k))) (let ((ei (js-string-index-of s0 "e" 0))) (if (< ei 0) s0 (let ((exp-n (js-to-number (js-string-slice s0 (+ ei 1) (len s0))))) (if (>= exp-n 0) (str (js-string-slice s0 0 (+ ei 1)) "+" (str exp-n)) s0))))))))
|
|
(if (< n 0) (str "-" precise) precise)))))))))
|
|
|
|
(define
|
|
js-normalize-num-str
|
|
(fn
|
|
(s)
|
|
(let
|
|
((ei (js-find-exp-char s)))
|
|
(if
|
|
(< ei 0)
|
|
s
|
|
(let
|
|
((mant (js-string-slice s 0 ei))
|
|
(expraw (js-string-slice s (+ ei 1) (len s))))
|
|
(str mant "e" (js-strip-leading-zeros expraw)))))))
|
|
|
|
(define
|
|
js-strip-leading-zeros
|
|
(fn
|
|
(s)
|
|
(let
|
|
((sign-and-body (js-split-sign s)))
|
|
(let
|
|
((sign (nth sign-and-body 0))
|
|
(body (nth sign-and-body 1)))
|
|
(let
|
|
((stripped (js-strip-zeros-loop body 0 (len body))))
|
|
(if (= stripped "") (str sign "0") (str sign stripped)))))))
|
|
|
|
(define
|
|
js-split-sign
|
|
(fn
|
|
(s)
|
|
(cond
|
|
((= s "") (list "" ""))
|
|
((= (char-at s 0) "-")
|
|
(list "-" (js-string-slice s 1 (len s))))
|
|
((= (char-at s 0) "+")
|
|
(list "" (js-string-slice s 1 (len s))))
|
|
(else (list "" s)))))
|
|
|
|
(define
|
|
js-strip-zeros-loop
|
|
(fn
|
|
(s i n)
|
|
(cond
|
|
((>= i n) "")
|
|
((= (char-at s i) "0") (js-strip-zeros-loop s (+ i 1) n))
|
|
(else (js-string-slice s i n)))))
|
|
|
|
(define
|
|
js-add
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((or (= (type-of a) "string") (= (type-of b) "string"))
|
|
(str (js-to-string a) (js-to-string b)))
|
|
(else (+ (js-to-number a) (js-to-number b))))))
|
|
|
|
(define js-sub (fn (a b) (- (js-to-number a) (js-to-number b))))
|
|
|
|
(define js-mul (fn (a b) (* (js-to-number a) (js-to-number b))))
|
|
|
|
(define
|
|
js-div
|
|
(fn (a b) (/ (js-to-number a) (exact->inexact (js-to-number b)))))
|
|
|
|
(define js-mod (fn (a b) (mod (js-to-number a) (js-to-number b))))
|
|
|
|
(define
|
|
js-unsigned-rshift
|
|
(fn
|
|
(l r)
|
|
(let
|
|
((lu32 (modulo (js-math-trunc (js-to-number l)) 4294967296))
|
|
(shift (modulo (js-math-trunc (js-to-number r)) 32)))
|
|
(floor (/ lu32 (js-math-pow 2 shift))))))
|
|
|
|
(define js-pow (fn (a b) (pow (js-to-number a) (js-to-number b))))
|
|
|
|
(define js-neg (fn (a) (* -1 (exact->inexact (js-to-number a)))))
|
|
|
|
(define js-pos (fn (a) (js-to-number a)))
|
|
|
|
(define js-not (fn (a) (not (js-to-boolean a))))
|
|
|
|
(define
|
|
js-bitnot
|
|
(fn (a) (- 0 (+ (js-num-to-int (js-to-number a)) 1))))
|
|
|
|
(define
|
|
js-strict-eq
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((and (js-undefined? a) (js-undefined? b)) true)
|
|
((or (js-undefined? a) (js-undefined? b)) false)
|
|
((not (= (type-of a) (type-of b))) false)
|
|
(else
|
|
(if (or (js-number-is-nan a) (js-number-is-nan b)) false (= a b))))))
|
|
|
|
(define js-strict-neq (fn (a b) (not (js-strict-eq a b))))
|
|
|
|
(define
|
|
js-loose-eq
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((js-strict-eq a b) true)
|
|
((and (= a nil) (js-undefined? b)) true)
|
|
((and (js-undefined? a) (= b nil)) true)
|
|
((and (= (type-of a) "number") (= (type-of b) "string"))
|
|
(= a (js-to-number b)))
|
|
((and (= (type-of a) "string") (= (type-of b) "number"))
|
|
(= (js-to-number a) b))
|
|
((= (type-of a) "boolean") (js-loose-eq (js-to-number a) b))
|
|
((= (type-of b) "boolean") (js-loose-eq a (js-to-number b)))
|
|
((and (dict? a) (contains? (keys a) "__js_string_value__"))
|
|
(js-loose-eq (get a "__js_string_value__") b))
|
|
((and (dict? b) (contains? (keys b) "__js_string_value__"))
|
|
(js-loose-eq a (get b "__js_string_value__")))
|
|
((and (dict? a) (contains? (keys a) "__js_number_value__"))
|
|
(js-loose-eq (get a "__js_number_value__") b))
|
|
((and (dict? b) (contains? (keys b) "__js_number_value__"))
|
|
(js-loose-eq a (get b "__js_number_value__")))
|
|
((and (dict? a) (contains? (keys a) "__js_boolean_value__"))
|
|
(js-loose-eq (get a "__js_boolean_value__") b))
|
|
((and (dict? b) (contains? (keys b) "__js_boolean_value__"))
|
|
(js-loose-eq a (get b "__js_boolean_value__")))
|
|
(else false))))
|
|
|
|
(define js-loose-neq (fn (a b) (not (js-loose-eq a b))))
|
|
|
|
(define
|
|
js-lt
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((and (= (type-of a) "string") (= (type-of b) "string"))
|
|
(js-str-lt a b))
|
|
(else (< (js-to-number a) (js-to-number b))))))
|
|
|
|
(define js-gt (fn (a b) (js-lt b a)))
|
|
|
|
(define js-le (fn (a b) (not (js-lt b a))))
|
|
|
|
(define js-ge (fn (a b) (not (js-lt a b))))
|
|
|
|
(define
|
|
js-str-lt
|
|
(fn (a b) (js-str-lt-at a b 0 (len a) (len b))))
|
|
|
|
(define
|
|
js-str-lt-at
|
|
(fn
|
|
(a b i la lb)
|
|
(cond
|
|
((and (>= i la) (>= i lb)) false)
|
|
((>= i la) true)
|
|
((>= i lb) false)
|
|
((< (char-code-at a i) (char-code-at b i)) true)
|
|
((> (char-code-at a i) (char-code-at b i)) false)
|
|
(else (js-str-lt-at a b (+ i 1) la lb)))))
|
|
|
|
(define char-code-at (fn (s i) (char-code (char-at s i))))
|
|
|
|
(define
|
|
js-array-method
|
|
(fn
|
|
(arr name)
|
|
(cond
|
|
((= name "push")
|
|
(fn
|
|
(&rest args)
|
|
(begin (for-each (fn (x) (append! arr x)) args) (len arr))))
|
|
((= name "pop")
|
|
(fn
|
|
()
|
|
(if
|
|
(= (len arr) 0)
|
|
js-undefined
|
|
(let
|
|
((v (nth arr (- (len arr) 1))))
|
|
(begin (pop-last! arr) v)))))
|
|
((= name "shift")
|
|
(fn
|
|
()
|
|
(if
|
|
(= (len arr) 0)
|
|
js-undefined
|
|
(let ((v (nth arr 0))) (begin (pop-first! arr) v)))))
|
|
((= name "slice")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((start (if (= (len args) 0) 0 (js-num-to-int (nth args 0))))
|
|
(stop
|
|
(if
|
|
(< (len args) 2)
|
|
(len arr)
|
|
(js-num-to-int (nth args 1)))))
|
|
(js-list-slice arr start stop))))
|
|
((= name "indexOf")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
-1
|
|
(js-list-index-of
|
|
arr
|
|
(nth args 0)
|
|
(if
|
|
(< (len args) 2)
|
|
0
|
|
(js-num-to-int (nth args 1)))))))
|
|
((= name "join")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((sep (if (= (len args) 0) "," (js-to-string (nth args 0)))))
|
|
(js-list-join arr sep))))
|
|
((= name "concat") (fn (&rest args) (js-list-concat arr args)))
|
|
((= name "map") (fn (f) (js-list-map-loop f arr 0 (list))))
|
|
((= name "filter")
|
|
(fn (f) (js-list-filter-loop f arr 0 (list))))
|
|
((= name "forEach")
|
|
(fn
|
|
(f)
|
|
(begin (js-list-foreach-loop f arr 0) js-undefined)))
|
|
((= name "reduce")
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 1)
|
|
(if
|
|
(= (len arr) 0)
|
|
(error "Reduce of empty array with no initial value")
|
|
(js-list-reduce-loop
|
|
(nth args 0)
|
|
(nth arr 0)
|
|
arr
|
|
1)))
|
|
(else
|
|
(js-list-reduce-loop
|
|
(nth args 0)
|
|
(nth args 1)
|
|
arr
|
|
0)))))
|
|
((= name "includes")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
false
|
|
(>=
|
|
(js-list-index-of arr (nth args 0) 0)
|
|
0))))
|
|
((= name "find") (fn (f) (js-list-find-loop f arr 0)))
|
|
((= name "findIndex")
|
|
(fn (f) (js-list-find-index-loop f arr 0)))
|
|
((= name "some") (fn (f) (js-list-some-loop f arr 0)))
|
|
((= name "every") (fn (f) (js-list-every-loop f arr 0)))
|
|
((= name "reverse")
|
|
(fn () (js-list-reverse-loop arr (- (len arr) 1) (list))))
|
|
((= name "flat")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((depth (if (= (len args) 0) 1 (js-num-to-int (nth args 0)))))
|
|
(js-list-flat-loop arr depth (list)))))
|
|
((= name "fill")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((v (if (= (len args) 0) js-undefined (nth args 0)))
|
|
(s
|
|
(if
|
|
(< (len args) 2)
|
|
0
|
|
(js-num-to-int (nth args 1))))
|
|
(e
|
|
(if
|
|
(< (len args) 3)
|
|
(len arr)
|
|
(js-num-to-int (nth args 2)))))
|
|
(js-list-fill-loop arr v s e)
|
|
arr)))
|
|
((= name "sort")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((cmp (if (= (len args) 0) nil (nth args 0))))
|
|
(js-list-sort! arr cmp)
|
|
arr)))
|
|
((= name "lastIndexOf")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
-1
|
|
(js-list-last-index-of
|
|
arr
|
|
(nth args 0)
|
|
(if
|
|
(< (len args) 2)
|
|
(- (len arr) 1)
|
|
(js-num-to-int (nth args 1)))))))
|
|
((= name "at")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((i (if (empty? args) 0 (js-num-to-int (nth args 0)))))
|
|
(let
|
|
((idx (if (< i 0) (+ (len arr) i) i)))
|
|
(if
|
|
(or (< idx 0) (>= idx (len arr)))
|
|
:js-undefined (nth arr idx))))))
|
|
((= name "unshift") (fn (&rest args) (+ (len arr) (len args))))
|
|
((= name "splice")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((n (len arr))
|
|
(start-raw
|
|
(if
|
|
(empty? args)
|
|
0
|
|
(js-num-to-int (nth args 0)))))
|
|
(let
|
|
((start (cond ((< start-raw 0) (max 0 (+ n start-raw))) ((> start-raw n) n) (else start-raw))))
|
|
(let
|
|
((delete-count (if (< (len args) 2) (- n start) (max 0 (min (- n start) (js-num-to-int (nth args 1)))))))
|
|
(js-list-slice arr start (+ start delete-count)))))))
|
|
((= name "flatMap")
|
|
(fn
|
|
(f)
|
|
(let
|
|
((mapped (js-list-map-loop f arr 0 (list))))
|
|
(js-list-flat-loop mapped 1 (list)))))
|
|
((= name "findLast")
|
|
(fn (f) (js-list-find-last-loop f arr (- (len arr) 1))))
|
|
((= name "findLastIndex")
|
|
(fn
|
|
(f)
|
|
(js-list-find-last-index-loop f arr (- (len arr) 1))))
|
|
((= name "reduceRight")
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 1)
|
|
(if
|
|
(= (len arr) 0)
|
|
(error "Reduce of empty array with no initial value")
|
|
(js-list-reduce-right-loop
|
|
(nth args 0)
|
|
(nth arr (- (len arr) 1))
|
|
arr
|
|
(- (len arr) 2))))
|
|
(else
|
|
(js-list-reduce-right-loop
|
|
(nth args 0)
|
|
(nth args 1)
|
|
arr
|
|
(- (len arr) 1))))))
|
|
((= name "toString") (fn () (js-list-join arr ",")))
|
|
((= name "toLocaleString") (fn () (js-list-join arr ",")))
|
|
((= name "keys")
|
|
(fn
|
|
()
|
|
(let
|
|
((result (list)))
|
|
(begin (js-list-keys-loop arr 0 result) result))))
|
|
((= name "values") (fn () (js-list-slice arr 0 (len arr))))
|
|
((= name "entries")
|
|
(fn
|
|
()
|
|
(let
|
|
((result (list)))
|
|
(begin (js-list-entries-loop arr 0 result) result))))
|
|
((= name "copyWithin")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((n (len arr))
|
|
(target-raw
|
|
(if
|
|
(empty? args)
|
|
0
|
|
(js-num-to-int (nth args 0))))
|
|
(start-raw
|
|
(if
|
|
(< (len args) 2)
|
|
0
|
|
(js-num-to-int (nth args 1))))
|
|
(end-raw
|
|
(if
|
|
(< (len args) 3)
|
|
(len arr)
|
|
(js-num-to-int (nth args 2)))))
|
|
(let
|
|
((target (cond ((< target-raw 0) (max 0 (+ n target-raw))) (else (min n target-raw))))
|
|
(start
|
|
(cond
|
|
((< start-raw 0)
|
|
(max 0 (+ n start-raw)))
|
|
(else (min n start-raw))))
|
|
(end
|
|
(cond
|
|
((< end-raw 0) (max 0 (+ n end-raw)))
|
|
(else (min n end-raw)))))
|
|
(begin (js-list-copy-within! arr target start end) arr)))))
|
|
((= name "toReversed")
|
|
(fn () (js-list-reverse-loop arr (- (len arr) 1) (list))))
|
|
((= name "toSorted")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((cmp (if (empty? args) nil (nth args 0)))
|
|
(copy (js-list-slice arr 0 (len arr))))
|
|
(begin (js-list-sort! copy cmp) copy))))
|
|
(else js-undefined))))
|
|
|
|
(define pop-last! (fn (lst) nil))
|
|
|
|
(define pop-first! (fn (lst) nil))
|
|
|
|
(define
|
|
js-list-slice
|
|
(fn
|
|
(arr start stop)
|
|
(let
|
|
((n (len arr)))
|
|
(let
|
|
((s (if (< start 0) (max 0 (+ n start)) (min start n)))
|
|
(e
|
|
(if
|
|
(< stop 0)
|
|
(max 0 (+ n stop))
|
|
(min stop n))))
|
|
(js-list-slice-loop arr s e (list))))))
|
|
|
|
(define
|
|
js-list-slice-loop
|
|
(fn
|
|
(arr i e acc)
|
|
(cond
|
|
((>= i e) acc)
|
|
(else
|
|
(do
|
|
(append! acc (nth arr i))
|
|
(js-list-slice-loop arr (+ i 1) e acc))))))
|
|
|
|
(define
|
|
js-list-index-of
|
|
(fn
|
|
(arr v i)
|
|
(cond
|
|
((>= i (len arr)) -1)
|
|
((js-strict-eq (nth arr i) v) i)
|
|
(else (js-list-index-of arr v (+ i 1))))))
|
|
|
|
(define
|
|
js-list-join
|
|
(fn
|
|
(arr sep)
|
|
(cond
|
|
((= (len arr) 0) "")
|
|
(else
|
|
(js-list-join-loop
|
|
arr
|
|
sep
|
|
1
|
|
(js-to-string-for-join (nth arr 0)))))))
|
|
|
|
(define
|
|
js-to-string-for-join
|
|
(fn
|
|
(v)
|
|
(cond ((js-undefined? v) "") ((= v nil) "") (else (js-to-string v)))))
|
|
|
|
(define
|
|
js-list-join-loop
|
|
(fn
|
|
(arr sep i acc)
|
|
(cond
|
|
((>= i (len arr)) acc)
|
|
(else
|
|
(js-list-join-loop
|
|
arr
|
|
sep
|
|
(+ i 1)
|
|
(str acc sep (js-to-string-for-join (nth arr i))))))))
|
|
|
|
(define
|
|
js-list-concat
|
|
(fn
|
|
(arr tail)
|
|
(let
|
|
((result (list)))
|
|
(begin
|
|
(for-each (fn (x) (append! result x)) arr)
|
|
(for-each
|
|
(fn
|
|
(other)
|
|
(cond
|
|
((= (type-of other) "list")
|
|
(for-each (fn (x) (append! result x)) other))
|
|
(else (append! result other))))
|
|
tail)
|
|
result))))
|
|
|
|
(define
|
|
js-list-map-loop
|
|
(fn
|
|
(f arr i acc)
|
|
(cond
|
|
((>= i (len arr)) acc)
|
|
(else
|
|
(do
|
|
(append! acc (f (nth arr i)))
|
|
(js-list-map-loop f arr (+ i 1) acc))))))
|
|
|
|
(define
|
|
js-list-filter-loop
|
|
(fn
|
|
(f arr i acc)
|
|
(cond
|
|
((>= i (len arr)) acc)
|
|
(else
|
|
(do
|
|
(let
|
|
((v (nth arr i)))
|
|
(if (js-to-boolean (f v)) (append! acc v) nil))
|
|
(js-list-filter-loop f arr (+ i 1) acc))))))
|
|
|
|
(define
|
|
js-list-foreach-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((>= i (len arr)) nil)
|
|
(else
|
|
(do (f (nth arr i)) (js-list-foreach-loop f arr (+ i 1)))))))
|
|
|
|
(define
|
|
js-list-reduce-loop
|
|
(fn
|
|
(f acc arr i)
|
|
(cond
|
|
((>= i (len arr)) acc)
|
|
(else
|
|
(js-list-reduce-loop f (f acc (nth arr i)) arr (+ i 1))))))
|
|
|
|
(define
|
|
js-list-find-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((>= i (len arr)) js-undefined)
|
|
((js-to-boolean (f (nth arr i))) (nth arr i))
|
|
(else (js-list-find-loop f arr (+ i 1))))))
|
|
|
|
(define
|
|
js-list-find-index-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((>= i (len arr)) -1)
|
|
((js-to-boolean (f (nth arr i))) i)
|
|
(else (js-list-find-index-loop f arr (+ i 1))))))
|
|
|
|
(define
|
|
js-list-some-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((>= i (len arr)) false)
|
|
((js-to-boolean (f (nth arr i))) true)
|
|
(else (js-list-some-loop f arr (+ i 1))))))
|
|
|
|
(define
|
|
js-list-flat-loop
|
|
(fn
|
|
(arr depth acc)
|
|
(for-each
|
|
(fn
|
|
(x)
|
|
(if
|
|
(and (list? x) (> depth 0))
|
|
(js-list-flat-loop x (- depth 1) acc)
|
|
(append! acc x)))
|
|
arr)
|
|
acc))
|
|
|
|
(define
|
|
js-list-fill-loop
|
|
(fn
|
|
(arr v s e)
|
|
(cond
|
|
((>= s e) nil)
|
|
((>= s (len arr)) nil)
|
|
(else
|
|
(begin
|
|
(js-list-set! arr s v)
|
|
(js-list-fill-loop arr v (+ s 1) e))))))
|
|
|
|
(define
|
|
js-list-sort!
|
|
(fn
|
|
(arr cmp)
|
|
(let ((n (len arr))) (js-list-sort-outer! arr cmp 0 n))))
|
|
|
|
(define
|
|
js-list-sort-outer!
|
|
(fn
|
|
(arr cmp i n)
|
|
(cond
|
|
((>= i n) nil)
|
|
(else
|
|
(begin
|
|
(js-list-sort-inner! arr cmp 0 (- n i 1))
|
|
(js-list-sort-outer! arr cmp (+ i 1) n))))))
|
|
|
|
(define
|
|
js-list-sort-inner!
|
|
(fn
|
|
(arr cmp i end)
|
|
(cond
|
|
((>= i end) nil)
|
|
(else
|
|
(begin
|
|
(let
|
|
((a (nth arr i)) (b (nth arr (+ i 1))))
|
|
(let
|
|
((result (if (= cmp nil) (if (js-str-lt (js-to-string b) (js-to-string a)) 1 -1) (js-to-number (cmp a b)))))
|
|
(when
|
|
(> result 0)
|
|
(begin
|
|
(js-list-set! arr i b)
|
|
(js-list-set! arr (+ i 1) a)))))
|
|
(js-list-sort-inner! arr cmp (+ i 1) end))))))
|
|
|
|
(define
|
|
js-list-last-index-of
|
|
(fn
|
|
(arr x i)
|
|
(cond
|
|
((< i 0) -1)
|
|
((= (nth arr i) x) i)
|
|
(else (js-list-last-index-of arr x (- i 1))))))
|
|
|
|
(define
|
|
js-list-every-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((>= i (len arr)) true)
|
|
((not (js-to-boolean (f (nth arr i)))) false)
|
|
(else (js-list-every-loop f arr (+ i 1))))))
|
|
|
|
(define
|
|
js-list-reverse-loop
|
|
(fn
|
|
(arr i acc)
|
|
(cond
|
|
((< i 0) acc)
|
|
(else
|
|
(begin
|
|
(append! acc (nth arr i))
|
|
(js-list-reverse-loop arr (- i 1) acc))))))
|
|
|
|
(define
|
|
js-list-find-last-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((< i 0) :js-undefined)
|
|
((js-to-boolean (f (nth arr i))) (nth arr i))
|
|
(else (js-list-find-last-loop f arr (- i 1))))))
|
|
|
|
(define
|
|
js-list-find-last-index-loop
|
|
(fn
|
|
(f arr i)
|
|
(cond
|
|
((< i 0) -1)
|
|
((js-to-boolean (f (nth arr i))) i)
|
|
(else (js-list-find-last-index-loop f arr (- i 1))))))
|
|
|
|
(define
|
|
js-list-reduce-right-loop
|
|
(fn
|
|
(f acc arr i)
|
|
(if
|
|
(< i 0)
|
|
acc
|
|
(js-list-reduce-right-loop f (f acc (nth arr i)) arr (- i 1)))))
|
|
|
|
(define
|
|
js-list-keys-loop
|
|
(fn
|
|
(arr i result)
|
|
(if
|
|
(>= i (len arr))
|
|
result
|
|
(begin
|
|
(append! result i)
|
|
(js-list-keys-loop arr (+ i 1) result)))))
|
|
|
|
(define
|
|
js-list-entries-loop
|
|
(fn
|
|
(arr i result)
|
|
(if
|
|
(>= i (len arr))
|
|
result
|
|
(begin
|
|
(append! result (list i (nth arr i)))
|
|
(js-list-entries-loop arr (+ i 1) result)))))
|
|
|
|
(define
|
|
js-list-copy-within!
|
|
(fn
|
|
(arr target start end)
|
|
(let
|
|
((snap (js-list-slice arr start end)))
|
|
(js-list-copy-within-loop! arr target snap 0))))
|
|
|
|
(define
|
|
js-list-copy-within-loop!
|
|
(fn
|
|
(arr target snap i)
|
|
(cond
|
|
((>= i (len snap)) arr)
|
|
((>= (+ target i) (len arr)) arr)
|
|
(else
|
|
(begin
|
|
(set-nth! arr (+ target i) (nth snap i))
|
|
(js-list-copy-within-loop! arr target snap (+ i 1)))))))
|
|
|
|
(define
|
|
js-string-repeat
|
|
(fn
|
|
(s n acc)
|
|
(if
|
|
(<= n 0)
|
|
acc
|
|
(js-string-repeat s (- n 1) (str acc s)))))
|
|
|
|
(define
|
|
js-string-pad
|
|
(fn
|
|
(s target pad at-start)
|
|
(let
|
|
((slen (len s)))
|
|
(if
|
|
(or (<= target slen) (= (len pad) 0))
|
|
s
|
|
(let
|
|
((needed (- target slen)))
|
|
(let
|
|
((padding (js-string-pad-build pad needed "")))
|
|
(if at-start (str padding s) (str s padding))))))))
|
|
|
|
(define
|
|
js-string-pad-build
|
|
(fn
|
|
(pad needed acc)
|
|
(cond
|
|
((<= needed 0) acc)
|
|
((>= (len acc) needed) (js-string-slice acc 0 needed))
|
|
(else (js-string-pad-build pad needed (str acc pad))))))
|
|
|
|
(define
|
|
js-string-method
|
|
(fn
|
|
(s name)
|
|
(cond
|
|
((= name "charAt")
|
|
(fn
|
|
(i)
|
|
(let
|
|
((idx (js-num-to-int i)))
|
|
(if
|
|
(and (>= idx 0) (< idx (len s)))
|
|
(char-at s idx)
|
|
""))))
|
|
((= name "charCodeAt")
|
|
(fn
|
|
(i)
|
|
(let
|
|
((idx (js-num-to-int (js-to-number i))))
|
|
(if
|
|
(and (>= idx 0) (< idx (len s)))
|
|
(char-code (char-at s idx))
|
|
(js-nan-value)))))
|
|
((= name "indexOf")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(empty? args)
|
|
-1
|
|
(js-string-index-of
|
|
s
|
|
(js-to-string (nth args 0))
|
|
(if
|
|
(< (len args) 2)
|
|
0
|
|
(max 0 (js-num-to-int (nth args 1))))))))
|
|
((= name "slice")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((start (if (= (len args) 0) 0 (js-num-to-int (nth args 0))))
|
|
(stop
|
|
(if
|
|
(< (len args) 2)
|
|
(len s)
|
|
(js-num-to-int (nth args 1)))))
|
|
(js-string-slice s start stop))))
|
|
((= name "substring")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((start (if (= (len args) 0) 0 (max 0 (js-num-to-int (nth args 0)))))
|
|
(stop
|
|
(if
|
|
(< (len args) 2)
|
|
(len s)
|
|
(max 0 (js-num-to-int (nth args 1))))))
|
|
(let
|
|
((lo (min start stop)) (hi (max start stop)))
|
|
(js-string-slice s lo (min hi (len s)))))))
|
|
((= name "toUpperCase") (fn () (js-upper-case s)))
|
|
((= name "toLowerCase") (fn () (js-lower-case s)))
|
|
((= name "split")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((sep (if (= (len args) 0) :js-undefined (nth args 0)))
|
|
(limit
|
|
(if
|
|
(< (len args) 2)
|
|
-1
|
|
(js-num-to-int (nth args 1)))))
|
|
(let
|
|
((result (js-string-split s (js-to-string sep))))
|
|
(if (< limit 0) result (js-list-take result limit))))))
|
|
((= name "concat")
|
|
(fn (&rest args) (js-string-concat-loop s args 0)))
|
|
((= name "includes")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((needle (if (= (len args) 0) "" (js-to-string (nth args 0)))))
|
|
(>= (js-string-index-of s needle 0) 0))))
|
|
((= name "startsWith")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((needle (if (= (len args) 0) "" (js-to-string (nth args 0))))
|
|
(start
|
|
(if
|
|
(< (len args) 2)
|
|
0
|
|
(js-num-to-int (nth args 1)))))
|
|
(js-string-matches? s needle start 0))))
|
|
((= name "endsWith")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((needle (if (= (len args) 0) "" (js-to-string (nth args 0)))))
|
|
(let
|
|
((end-len (len s)) (n-len (len needle)))
|
|
(if
|
|
(> n-len end-len)
|
|
false
|
|
(js-string-matches? s needle (- end-len n-len) 0))))))
|
|
((= name "trim") (fn () (js-trim s)))
|
|
((= name "trimStart") (fn () (js-trim-left s)))
|
|
((= name "trimEnd") (fn () (js-trim-right s)))
|
|
((= name "repeat")
|
|
(fn (n) (js-string-repeat s (js-num-to-int n) "")))
|
|
((= name "padStart")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((target (if (= (len args) 0) 0 (js-num-to-int (nth args 0))))
|
|
(pad
|
|
(if
|
|
(< (len args) 2)
|
|
" "
|
|
(js-to-string (nth args 1)))))
|
|
(js-string-pad s target pad true))))
|
|
((= name "padEnd")
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((target (if (= (len args) 0) 0 (js-num-to-int (nth args 0))))
|
|
(pad
|
|
(if
|
|
(< (len args) 2)
|
|
" "
|
|
(js-to-string (nth args 1)))))
|
|
(js-string-pad s target pad false))))
|
|
((= name "toString") (fn () s))
|
|
((= name "valueOf") (fn () s))
|
|
((= name "replace")
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((< (len args) 2) s)
|
|
((js-regex? (nth args 0))
|
|
(let
|
|
((rx (nth args 0)) (repl (nth args 1)))
|
|
(let
|
|
((src (get rx "source")))
|
|
(let
|
|
((idx (js-string-index-of (if (get rx "ignoreCase") (js-lower-case s) s) (if (get rx "ignoreCase") (js-lower-case src) src) 0)))
|
|
(if
|
|
(= idx -1)
|
|
s
|
|
(let
|
|
((matched (js-string-slice s idx (+ idx (len src)))))
|
|
(str
|
|
(js-string-slice s 0 idx)
|
|
(if
|
|
(js-function? repl)
|
|
(repl matched)
|
|
(js-to-string repl))
|
|
(js-string-slice s (+ idx (len src)) (len s)))))))))
|
|
(else
|
|
(let
|
|
((needle (js-to-string (nth args 0)))
|
|
(repl (nth args 1)))
|
|
(let
|
|
((idx (js-string-index-of s needle 0)))
|
|
(if
|
|
(= idx -1)
|
|
s
|
|
(str
|
|
(js-string-slice s 0 idx)
|
|
(if
|
|
(js-function? repl)
|
|
(repl needle)
|
|
(js-to-string repl))
|
|
(js-string-slice s (+ idx (len needle)) (len s))))))))))
|
|
((= name "search")
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 0) -1)
|
|
((js-regex? (nth args 0))
|
|
(let
|
|
((rx (nth args 0))
|
|
(src (get (nth args 0) "source")))
|
|
(js-string-index-of
|
|
(if (get rx "ignoreCase") (js-lower-case s) s)
|
|
(if (get rx "ignoreCase") (js-lower-case src) src)
|
|
0)))
|
|
(else
|
|
(js-string-index-of
|
|
s
|
|
(js-to-string (nth args 0))
|
|
0)))))
|
|
((= name "match")
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 0) nil)
|
|
((js-regex? (nth args 0))
|
|
(js-regex-stub-exec (nth args 0) s))
|
|
(else
|
|
(let
|
|
((needle (js-to-string (nth args 0))))
|
|
(let
|
|
((idx (js-string-index-of s needle 0)))
|
|
(if
|
|
(= idx -1)
|
|
nil
|
|
(let ((res (list))) (append! res needle) res))))))))
|
|
((= name "matchAll")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(empty? args)
|
|
(list)
|
|
(let
|
|
((needle (js-to-string (nth args 0))))
|
|
(let
|
|
((loop (fn (start acc) (let ((idx (js-string-index-of s needle start))) (if (= idx -1) acc (let ((m (list))) (begin (append! m needle) (dict-set! m "index" idx) (loop (+ idx (max 1 (len needle))) (begin (append! acc m) acc)))))))))
|
|
(loop 0 (list)))))))
|
|
((= name "at")
|
|
(fn
|
|
(i)
|
|
(let
|
|
((idx (js-num-to-int i)))
|
|
(let
|
|
((actual (if (< idx 0) (+ (len s) idx) idx)))
|
|
(if
|
|
(or (< actual 0) (>= actual (len s)))
|
|
:js-undefined (char-at s actual))))))
|
|
((= name "codePointAt")
|
|
(fn
|
|
(i)
|
|
(let
|
|
((idx (js-num-to-int i)))
|
|
(if
|
|
(or (< idx 0) (>= idx (len s)))
|
|
:js-undefined (char-code (char-at s idx))))))
|
|
((= name "lastIndexOf")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(empty? args)
|
|
-1
|
|
(let
|
|
((needle (js-to-string (nth args 0))))
|
|
(let
|
|
((default-start (- (len s) (len needle)))
|
|
(from
|
|
(if
|
|
(< (len args) 2)
|
|
-1
|
|
(js-num-to-int (nth args 1)))))
|
|
(js-string-last-index-of
|
|
s
|
|
needle
|
|
(if
|
|
(< from 0)
|
|
default-start
|
|
(min from default-start))))))))
|
|
((= name "localeCompare")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(empty? args)
|
|
0
|
|
(let
|
|
((other (js-to-string (nth args 0))))
|
|
(cond
|
|
((< s other) -1)
|
|
((> s other) 1)
|
|
(else 0))))))
|
|
((= name "replaceAll")
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(< (len args) 2)
|
|
s
|
|
(let
|
|
((needle-arg (nth args 0))
|
|
(repl (nth args 1)))
|
|
(let
|
|
((needle (if (js-regex? needle-arg) (get needle-arg "source") (js-to-string needle-arg))))
|
|
(js-string-replace-all
|
|
s
|
|
needle
|
|
(if (js-function? repl) repl (js-to-string repl))))))))
|
|
((= name "normalize") (fn (&rest args) s))
|
|
((= name "toLocaleLowerCase") (fn (&rest args) (js-lower-case s)))
|
|
((= name "toLocaleUpperCase") (fn (&rest args) (js-upper-case s)))
|
|
((= name "isWellFormed") (fn () true))
|
|
((= name "toWellFormed") (fn () s))
|
|
(else js-undefined))))
|
|
|
|
(define
|
|
js-string-slice
|
|
(fn
|
|
(s start stop)
|
|
(let
|
|
((n (len s)))
|
|
(let
|
|
((lo (if (< start 0) (max 0 (+ n start)) (min start n)))
|
|
(hi
|
|
(if
|
|
(< stop 0)
|
|
(max 0 (+ n stop))
|
|
(min stop n))))
|
|
(if (>= lo hi) "" (js-string-slice-loop s lo hi ""))))))
|
|
|
|
(define
|
|
js-string-slice-loop
|
|
(fn
|
|
(s i e acc)
|
|
(cond
|
|
((>= i e) acc)
|
|
(else
|
|
(js-string-slice-loop s (+ i 1) e (str acc (char-at s i)))))))
|
|
|
|
(define
|
|
js-string-index-of
|
|
(fn
|
|
(s needle i)
|
|
(cond
|
|
((> (+ i (len needle)) (len s)) -1)
|
|
((js-string-matches? s needle i 0) i)
|
|
(else (js-string-index-of s needle (+ i 1))))))
|
|
|
|
(define
|
|
js-string-last-index-of
|
|
(fn
|
|
(s needle start)
|
|
(cond
|
|
((< start 0) -1)
|
|
((= needle "") start)
|
|
((js-string-matches? s needle start 0) start)
|
|
(else (js-string-last-index-of s needle (- start 1))))))
|
|
|
|
(define
|
|
js-string-replace-all
|
|
(fn
|
|
(s needle repl)
|
|
(if
|
|
(= needle "")
|
|
s
|
|
(let
|
|
((idx (js-string-index-of s needle 0)))
|
|
(if
|
|
(= idx -1)
|
|
s
|
|
(str
|
|
(js-string-slice s 0 idx)
|
|
(if (js-function? repl) (repl needle) repl)
|
|
(js-string-replace-all
|
|
(js-string-slice s (+ idx (len needle)) (len s))
|
|
needle
|
|
repl)))))))
|
|
|
|
(define
|
|
js-string-matches?
|
|
(fn
|
|
(s needle si ni)
|
|
(cond
|
|
((>= ni (len needle)) true)
|
|
((not (= (char-at s (+ si ni)) (char-at needle ni))) false)
|
|
(else (js-string-matches? s needle si (+ ni 1))))))
|
|
|
|
(define
|
|
js-list-take
|
|
(fn
|
|
(lst n)
|
|
(if
|
|
(or (<= n 0) (empty? lst))
|
|
(list)
|
|
(cons (first lst) (js-list-take (rest lst) (- n 1))))))
|
|
|
|
(define
|
|
js-string-split
|
|
(fn
|
|
(s sep)
|
|
(cond
|
|
((= sep "") (js-string-split-chars s 0 (list)))
|
|
(else (js-string-split-loop s sep 0 0 (list))))))
|
|
|
|
(define
|
|
js-string-split-chars
|
|
(fn
|
|
(s i acc)
|
|
(cond
|
|
((>= i (len s)) acc)
|
|
(else
|
|
(do
|
|
(append! acc (char-at s i))
|
|
(js-string-split-chars s (+ i 1) acc))))))
|
|
|
|
(define
|
|
js-string-split-loop
|
|
(fn
|
|
(s sep start i acc)
|
|
(cond
|
|
((> (+ i (len sep)) (len s))
|
|
(do (append! acc (js-string-slice s start (len s))) acc))
|
|
((js-string-matches? s sep i 0)
|
|
(do
|
|
(append! acc (js-string-slice s start i))
|
|
(js-string-split-loop s sep (+ i (len sep)) (+ i (len sep)) acc)))
|
|
(else (js-string-split-loop s sep start (+ i 1) acc)))))
|
|
|
|
(define
|
|
js-string-concat-loop
|
|
(fn
|
|
(acc args i)
|
|
(cond
|
|
((>= i (len args)) acc)
|
|
(else
|
|
(js-string-concat-loop
|
|
(str acc (js-to-string (nth args i)))
|
|
args
|
|
(+ i 1))))))
|
|
|
|
(begin
|
|
(define
|
|
js-get-prop
|
|
(fn
|
|
(obj key)
|
|
(cond
|
|
((= obj nil) js-undefined)
|
|
((js-undefined? obj) js-undefined)
|
|
((= (type-of obj) "list")
|
|
(cond
|
|
((= key "length") (len obj))
|
|
((= (type-of key) "number")
|
|
(if
|
|
(and (>= key 0) (< key (len obj)))
|
|
(nth obj (js-num-to-int key))
|
|
js-undefined))
|
|
((and (= (type-of key) "string") (js-is-numeric-string? key))
|
|
(let
|
|
((idx (js-num-to-int (js-string-to-number key))))
|
|
(if
|
|
(and (>= idx 0) (< idx (len obj)))
|
|
(nth obj idx)
|
|
js-undefined)))
|
|
((= key "push") (js-array-method obj "push"))
|
|
((= key "pop") (js-array-method obj "pop"))
|
|
((= key "shift") (js-array-method obj "shift"))
|
|
((= key "slice") (js-array-method obj "slice"))
|
|
((= key "indexOf") (js-array-method obj "indexOf"))
|
|
((= key "join") (js-array-method obj "join"))
|
|
((= key "concat") (js-array-method obj "concat"))
|
|
((= key "map") (js-array-method obj "map"))
|
|
((= key "filter") (js-array-method obj "filter"))
|
|
((= key "forEach") (js-array-method obj "forEach"))
|
|
((= key "reduce") (js-array-method obj "reduce"))
|
|
((= key "includes") (js-array-method obj "includes"))
|
|
((= key "find") (js-array-method obj "find"))
|
|
((= key "findIndex") (js-array-method obj "findIndex"))
|
|
((= key "some") (js-array-method obj "some"))
|
|
((= key "every") (js-array-method obj "every"))
|
|
((= key "reverse") (js-array-method obj "reverse"))
|
|
((= key "flat") (js-array-method obj "flat"))
|
|
((= key "fill") (js-array-method obj "fill"))
|
|
((= key "sort") (js-array-method obj "sort"))
|
|
((= key "lastIndexOf") (js-array-method obj "lastIndexOf"))
|
|
((= key "at") (js-array-method obj "at"))
|
|
((= key "unshift") (js-array-method obj "unshift"))
|
|
((= key "splice") (js-array-method obj "splice"))
|
|
((= key "flatMap") (js-array-method obj "flatMap"))
|
|
((= key "findLast") (js-array-method obj "findLast"))
|
|
((= key "findLastIndex") (js-array-method obj "findLastIndex"))
|
|
((= key "reduceRight") (js-array-method obj "reduceRight"))
|
|
((= key "toString") (js-array-method obj "toString"))
|
|
((= key "toLocaleString") (js-array-method obj "toLocaleString"))
|
|
((= key "keys") (js-array-method obj "keys"))
|
|
((= key "values") (js-array-method obj "values"))
|
|
((= key "entries") (js-array-method obj "entries"))
|
|
((= key "copyWithin") (js-array-method obj "copyWithin"))
|
|
((= key "toReversed") (js-array-method obj "toReversed"))
|
|
((= key "toSorted") (js-array-method obj "toSorted"))
|
|
(else (js-dict-get-walk (get Array "prototype") (js-to-string key)))))
|
|
((= (type-of obj) "string")
|
|
(cond
|
|
((= key "length") (len obj))
|
|
((= (type-of key) "number")
|
|
(if
|
|
(and (>= key 0) (< key (len obj)))
|
|
(char-at obj (js-num-to-int key))
|
|
js-undefined))
|
|
((= key "charAt") (js-string-method obj "charAt"))
|
|
((= key "charCodeAt") (js-string-method obj "charCodeAt"))
|
|
((= key "indexOf") (js-string-method obj "indexOf"))
|
|
((= key "slice") (js-string-method obj "slice"))
|
|
((= key "substring") (js-string-method obj "substring"))
|
|
((= key "toUpperCase") (js-string-method obj "toUpperCase"))
|
|
((= key "toLowerCase") (js-string-method obj "toLowerCase"))
|
|
((= key "split") (js-string-method obj "split"))
|
|
((= key "concat") (js-string-method obj "concat"))
|
|
((= key "includes") (js-string-method obj "includes"))
|
|
((= key "startsWith") (js-string-method obj "startsWith"))
|
|
((= key "endsWith") (js-string-method obj "endsWith"))
|
|
((= key "trim") (js-string-method obj "trim"))
|
|
((= key "trimStart") (js-string-method obj "trimStart"))
|
|
((= key "trimEnd") (js-string-method obj "trimEnd"))
|
|
((= key "repeat") (js-string-method obj "repeat"))
|
|
((= key "padStart") (js-string-method obj "padStart"))
|
|
((= key "padEnd") (js-string-method obj "padEnd"))
|
|
((= key "toString") (js-string-method obj "toString"))
|
|
((= key "valueOf") (js-string-method obj "valueOf"))
|
|
((= key "replace") (js-string-method obj "replace"))
|
|
((= key "search") (js-string-method obj "search"))
|
|
((= key "match") (js-string-method obj "match"))
|
|
((= key "at") (js-string-method obj "at"))
|
|
((= key "codePointAt") (js-string-method obj "codePointAt"))
|
|
((= key "lastIndexOf") (js-string-method obj "lastIndexOf"))
|
|
((= key "localeCompare") (js-string-method obj "localeCompare"))
|
|
((= key "replaceAll") (js-string-method obj "replaceAll"))
|
|
((= key "normalize") (js-string-method obj "normalize"))
|
|
((= key "toLocaleLowerCase")
|
|
(js-string-method obj "toLocaleLowerCase"))
|
|
((= key "toLocaleUpperCase")
|
|
(js-string-method obj "toLocaleUpperCase"))
|
|
((= key "isWellFormed") (js-string-method obj "isWellFormed"))
|
|
((= key "toWellFormed") (js-string-method obj "toWellFormed"))
|
|
(else
|
|
(let
|
|
((proto (get String "prototype")))
|
|
(if
|
|
(and (dict? proto) (contains? (keys proto) key))
|
|
(get proto key)
|
|
js-undefined)))))
|
|
((= (type-of obj) "dict")
|
|
(js-dict-get-walk obj (js-to-string key)))
|
|
((and (= obj Promise) (dict-has? __js_promise_statics__ (js-to-string key)))
|
|
(get __js_promise_statics__ (js-to-string key)))
|
|
((and (js-function? obj) (or (= key "prototype") (= key "name") (= key "length") (= key "call") (= key "apply") (= key "bind") (= key "constructor")))
|
|
(cond
|
|
((= key "prototype") (js-get-ctor-proto obj))
|
|
((= key "name") (js-extract-fn-name obj))
|
|
((= key "length") (js-fn-length obj))
|
|
((= key "constructor") js-function-global)
|
|
(else (js-invoke-function-bound obj key))))
|
|
(else js-undefined))))
|
|
(define
|
|
js-dict-get-walk
|
|
(fn
|
|
(obj skey)
|
|
(cond
|
|
((= obj nil) js-undefined)
|
|
((js-undefined? obj) js-undefined)
|
|
((not (= (type-of obj) "dict")) js-undefined)
|
|
((dict-has? obj skey) (get obj skey))
|
|
((dict-has? obj "__proto__")
|
|
(js-dict-get-walk (get obj "__proto__") skey))
|
|
((not (= obj (get Object "prototype")))
|
|
(js-dict-get-walk (get Object "prototype") skey))
|
|
(else js-undefined)))))
|
|
|
|
(define
|
|
js-num-to-int
|
|
(fn
|
|
(v)
|
|
(let
|
|
((n (js-to-number v)))
|
|
(cond
|
|
((js-number-is-nan n) 0)
|
|
((= n (js-infinity-value)) 0)
|
|
((= n (- 0 (js-infinity-value))) 0)
|
|
((>= n 0) (floor n))
|
|
(else (- 0 (floor (- 0 n))))))))
|
|
|
|
(define dict-has? (fn (d k) (contains? (keys d) k)))
|
|
|
|
(begin
|
|
(define
|
|
js-set-prop
|
|
(fn
|
|
(obj key val)
|
|
(cond
|
|
((js-undefined? obj) (error "js-set-prop: cannot set on undefined"))
|
|
((= (type-of obj) "dict")
|
|
(do (dict-set! obj (js-to-string key) val) val))
|
|
((= (type-of obj) "list") (do (js-list-set! obj key val) val))
|
|
(else val))))
|
|
(define
|
|
js-list-set!
|
|
(fn
|
|
(lst key val)
|
|
(cond
|
|
((= (type-of key) "number")
|
|
(let
|
|
((i (js-num-to-int key)) (n (len lst)))
|
|
(cond
|
|
((< i 0) nil)
|
|
((< i n) (set-nth! lst i val))
|
|
((= i n) (append! lst val))
|
|
(else (do (js-pad-list! lst n i) (append! lst val))))))
|
|
((and (= (type-of key) "string") (js-is-numeric-string? key))
|
|
(js-list-set! lst (js-string-to-number key) val))
|
|
((= key "length")
|
|
(let
|
|
((target (js-num-to-int (js-to-number val))) (n (len lst)))
|
|
(cond
|
|
((< target 0) nil)
|
|
((> target n) (js-pad-list! lst n target))
|
|
(else nil))))
|
|
(else nil))))
|
|
(define
|
|
js-pad-list!
|
|
(fn
|
|
(lst from target)
|
|
(cond
|
|
((>= from target) nil)
|
|
(else
|
|
(do
|
|
(append! lst js-undefined)
|
|
(js-pad-list! lst (+ from 1) target)))))))
|
|
|
|
(define js-and (fn (a b-thunk) (if (js-to-boolean a) (b-thunk) a)))
|
|
|
|
(define js-or (fn (a b-thunk) (if (js-to-boolean a) a (b-thunk))))
|
|
|
|
(define
|
|
js-console-log
|
|
(fn (&rest args) (for-each (fn (a) (log-info (js-to-string a))) args)))
|
|
|
|
(define console {:log js-console-log})
|
|
|
|
(define js-math-abs (fn (x) (abs (js-to-number x))))
|
|
|
|
(define js-math-floor (fn (x) (floor (js-to-number x))))
|
|
|
|
(define js-math-ceil (fn (x) (ceil (js-to-number x))))
|
|
|
|
(define js-math-round (fn (x) (floor (+ (js-to-number x) 0.5))))
|
|
|
|
(define
|
|
js-math-max
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((empty? args) -inf)
|
|
(else (js-math-max-loop (first args) (rest args))))))
|
|
|
|
(define
|
|
js-math-max-loop
|
|
(fn
|
|
(acc xs)
|
|
(cond
|
|
((empty? xs) acc)
|
|
(else
|
|
(let
|
|
((h (js-to-number (first xs))))
|
|
(js-math-max-loop (if (> h acc) h acc) (rest xs)))))))
|
|
|
|
(define
|
|
js-math-min
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((empty? args) inf)
|
|
(else (js-math-min-loop (first args) (rest args))))))
|
|
|
|
(define
|
|
js-math-min-loop
|
|
(fn
|
|
(acc xs)
|
|
(cond
|
|
((empty? xs) acc)
|
|
(else
|
|
(let
|
|
((h (js-to-number (first xs))))
|
|
(js-math-min-loop (if (< h acc) h acc) (rest xs)))))))
|
|
|
|
(define js-math-random (fn () 0))
|
|
|
|
(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)))))))
|
|
|
|
(begin
|
|
(define js-math-sin (fn (x) (sin (js-to-number x))))
|
|
(define js-math-cos (fn (x) (cos (js-to-number x))))
|
|
(define js-math-tan (fn (x) (tan (js-to-number x))))
|
|
(define js-math-asin (fn (x) (asin (js-to-number x))))
|
|
(define js-math-acos (fn (x) (acos (js-to-number x))))
|
|
(define js-math-atan (fn (x) (atan (js-to-number x))))
|
|
(define
|
|
js-math-atan2
|
|
(fn (y x) (atan2 (js-to-number y) (js-to-number x))))
|
|
(define js-math-sinh (fn (x) (sinh (js-to-number x))))
|
|
(define js-math-cosh (fn (x) (cosh (js-to-number x))))
|
|
(define js-math-tanh (fn (x) (tanh (js-to-number x))))
|
|
(define js-math-asinh (fn (x) (asinh (js-to-number x))))
|
|
(define js-math-acosh (fn (x) (acosh (js-to-number x))))
|
|
(define js-math-atanh (fn (x) (atanh (js-to-number x))))
|
|
(define js-math-exp (fn (x) (exp (js-to-number x))))
|
|
(define js-math-log (fn (x) (log (js-to-number x))))
|
|
(define js-math-log2 (fn (x) (log2 (js-to-number x))))
|
|
(define js-math-log10 (fn (x) (log10 (js-to-number x))))
|
|
(define js-math-expm1 (fn (x) (expm1 (js-to-number x))))
|
|
(define js-math-log1p (fn (x) (log1p (js-to-number x))))
|
|
(define
|
|
js-math-clz32
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((x (if (empty? args) 0 (js-to-number (nth args 0)))))
|
|
(let
|
|
((n (modulo (floor x) 4294967296)))
|
|
(if (= n 0) 32 (- 31 (floor (log2 n))))))))
|
|
(define
|
|
js-math-imul
|
|
(fn
|
|
(a b)
|
|
(let
|
|
((a32 (modulo (floor (js-to-number a)) 4294967296))
|
|
(b32 (modulo (floor (js-to-number b)) 4294967296)))
|
|
(let
|
|
((result (modulo (* a32 b32) 4294967296)))
|
|
(if (>= result 2147483648) (- result 4294967296) result)))))
|
|
(define js-math-fround (fn (x) (js-to-number x)))
|
|
(define Math {:atan js-math-atan :sign js-math-sign :LN2 0.693147 :cos js-math-cos :imul js-math-imul :min js-math-min :acos js-math-acos :log10 js-math-log10 :LOG10E 0.434294 :tanh js-math-tanh :abs js-math-abs :round js-math-round :log js-math-log :sqrt js-math-sqrt :cosh js-math-cosh :tan js-math-tan :floor js-math-floor :exp js-math-exp :asin js-math-asin :clz32 js-math-clz32 :random js-math-random :LN10 2.30259 :SQRT1_2 0.707107 :sinh js-math-sinh :E 2.71828 :fround js-math-fround :cbrt js-math-cbrt :log1p js-math-log1p :SQRT2 1.41421 :max js-math-max :log2 js-math-log2 :ceil js-math-ceil :pow js-math-pow :sin js-math-sin :hypot js-math-hypot :LOG2E 1.4427 :atanh js-math-atanh :asinh js-math-asinh :acosh js-math-acosh :PI 3.14159 :atan2 js-math-atan2 :trunc js-math-trunc :expm1 js-math-expm1}))
|
|
|
|
(define
|
|
js-number-is-finite
|
|
(fn
|
|
(v)
|
|
(and
|
|
(number? v)
|
|
(not (js-number-is-nan v))
|
|
(not (= v inf))
|
|
(not (= v -inf)))))
|
|
|
|
(define
|
|
js-number-is-nan
|
|
(fn
|
|
(v)
|
|
(and (number? v) (or (= (inspect v) "nan") (= (inspect v) "-nan")))))
|
|
|
|
(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 {:MIN_SAFE_INTEGER -9007199254740991 :MIN_VALUE 4.94066e-324 :isNaN js-number-is-nan :isSafeInteger js-number-is-safe-integer :NEGATIVE_INFINITY (- 0 (js-infinity-value)) :NaN (js-nan-value) :prototype {:toFixed (fn (d) (js-number-to-fixed (js-this) (if (= d nil) 0 (js-to-number d)))) :toExponential (fn (&rest args) (js-to-string (js-this))) :toLocaleString (fn () (js-to-string (js-this))) :toString (fn (&rest args) (let ((this-val (js-this)) (radix (if (empty? args) 10 (js-to-number (nth args 0))))) (js-num-to-str-radix this-val (if (or (= radix nil) (js-undefined? radix)) 10 radix)))) :toPrecision (fn (&rest args) (js-to-string (js-this))) :valueOf (fn () (js-this))} :isInteger js-number-is-integer :__callable__ js-to-number :MAX_VALUE (js-max-value-approx) :POSITIVE_INFINITY (js-infinity-value) :isFinite js-number-is-finite :MAX_SAFE_INTEGER 9007199254740991 :EPSILON 2.22045e-16})
|
|
|
|
(dict-set! Number "length" 1)
|
|
|
|
(dict-set! Number "name" "Number")
|
|
|
|
(dict-set! (get Number "prototype") "constructor" Number)
|
|
|
|
(dict-set!
|
|
Number
|
|
"__callable__"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((raw (if (= (len args) 0) 0 (js-to-number (nth args 0)))))
|
|
(let
|
|
((this-val (js-this)))
|
|
(if
|
|
(and
|
|
(dict? this-val)
|
|
(contains? (keys this-val) "__proto__")
|
|
(= (get this-val "__proto__") (get Number "prototype")))
|
|
(begin (dict-set! this-val "__js_number_value__" raw) this-val)
|
|
raw)))))
|
|
|
|
(dict-set!
|
|
(get Number "prototype")
|
|
"valueOf"
|
|
(fn
|
|
()
|
|
(let
|
|
((this-val (js-this)))
|
|
(if
|
|
(and
|
|
(dict? this-val)
|
|
(contains? (keys this-val) "__js_number_value__"))
|
|
(get this-val "__js_number_value__")
|
|
this-val))))
|
|
|
|
(dict-set!
|
|
(get Number "prototype")
|
|
"toString"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-raw (js-this)))
|
|
(let
|
|
((this-val (if (and (dict? this-raw) (contains? (keys this-raw) "__js_number_value__")) (get this-raw "__js_number_value__") this-raw)))
|
|
(let
|
|
((radix (if (empty? args) 10 (js-to-number (nth args 0)))))
|
|
(js-num-to-str-radix
|
|
this-val
|
|
(if (or (= radix nil) (js-undefined? radix)) 10 radix)))))))
|
|
|
|
(define isFinite js-global-is-finite)
|
|
|
|
(define isNaN js-global-is-nan)
|
|
|
|
(define __js_microtask_queue__ (dict))
|
|
|
|
(dict-set! __js_microtask_queue__ "q" (list))
|
|
|
|
(define
|
|
js-mt-push!
|
|
(fn
|
|
(thunk)
|
|
(dict-set!
|
|
__js_microtask_queue__
|
|
"q"
|
|
(append (get __js_microtask_queue__ "q") (list thunk)))))
|
|
|
|
(define
|
|
js-mt-pop!
|
|
(fn
|
|
()
|
|
(let
|
|
((q (get __js_microtask_queue__ "q")))
|
|
(if
|
|
(empty? q)
|
|
nil
|
|
(let
|
|
((h (first q)))
|
|
(dict-set! __js_microtask_queue__ "q" (rest q))
|
|
h)))))
|
|
|
|
(define js-mt-empty? (fn () (empty? (get __js_microtask_queue__ "q"))))
|
|
|
|
(define
|
|
js-drain-microtasks!
|
|
(fn
|
|
()
|
|
(cond
|
|
((js-mt-empty?) :js-undefined)
|
|
(else (let ((t (js-mt-pop!))) (t) (js-drain-microtasks!))))))
|
|
|
|
(define
|
|
js-promise?
|
|
(fn
|
|
(v)
|
|
(and
|
|
(= (type-of v) "dict")
|
|
(dict-has? v "__js_promise__")
|
|
(= (get v "__js_promise__") true))))
|
|
|
|
(define
|
|
js-make-promise
|
|
(fn
|
|
()
|
|
(let
|
|
((p (dict)))
|
|
(dict-set! p "__js_promise__" true)
|
|
(dict-set! p "state" "pending")
|
|
(dict-set! p "value" :js-undefined)
|
|
(dict-set! p "callbacks" (list))
|
|
p)))
|
|
|
|
(define
|
|
js-promise-resolve!
|
|
(fn
|
|
(p value)
|
|
(cond
|
|
((not (= (get p "state") "pending")) :js-undefined)
|
|
((js-promise? value)
|
|
(js-promise-then-internal!
|
|
value
|
|
(fn (v) (js-promise-resolve! p v))
|
|
(fn (r) (js-promise-reject! p r))))
|
|
(else
|
|
(begin
|
|
(dict-set! p "state" "fulfilled")
|
|
(dict-set! p "value" value)
|
|
(js-promise-flush-callbacks! p))))))
|
|
|
|
(define
|
|
js-promise-reject!
|
|
(fn
|
|
(p reason)
|
|
(cond
|
|
((not (= (get p "state") "pending")) :js-undefined)
|
|
(else
|
|
(begin
|
|
(dict-set! p "state" "rejected")
|
|
(dict-set! p "value" reason)
|
|
(js-promise-flush-callbacks! p))))))
|
|
|
|
(define
|
|
js-iterable-to-list
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((list? v) v)
|
|
((= (type-of v) "string") (js-string-to-list v 0 (list)))
|
|
((dict? v)
|
|
(cond
|
|
((contains? (keys v) "length") (js-arraylike-to-list v))
|
|
(else
|
|
(let
|
|
((result (list)))
|
|
(begin
|
|
(for-each (fn (k) (append! result (get v k))) (keys v))
|
|
result)))))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-arraylike-to-list
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((list? v) v)
|
|
((= (type-of v) "string") (js-string-to-list v 0 (list)))
|
|
((dict? v)
|
|
(let
|
|
((n-val (get v "length")))
|
|
(if
|
|
(or (= n-val nil) (js-undefined? n-val))
|
|
(list)
|
|
(let
|
|
((n (js-to-number n-val)))
|
|
(js-arraylike-to-list-loop v 0 n (list))))))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-arraylike-to-list-loop
|
|
(fn
|
|
(v i n acc)
|
|
(if
|
|
(>= i n)
|
|
acc
|
|
(let
|
|
((val (get v (str i))))
|
|
(do
|
|
(append! acc (if (= val nil) :js-undefined val))
|
|
(js-arraylike-to-list-loop v (+ i 1) n acc))))))
|
|
|
|
(define
|
|
js-string-to-list
|
|
(fn
|
|
(s i acc)
|
|
(if
|
|
(>= i (len s))
|
|
acc
|
|
(begin
|
|
(append! acc (char-at s i))
|
|
(js-string-to-list s (+ i 1) acc)))))
|
|
|
|
(define
|
|
js-object-keys
|
|
(fn
|
|
(o)
|
|
(cond
|
|
((dict? o)
|
|
(let
|
|
((result (list)))
|
|
(for-each (fn (k) (append! result k)) (keys o))
|
|
result))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-object-values
|
|
(fn
|
|
(o)
|
|
(cond
|
|
((dict? o)
|
|
(let
|
|
((result (list)))
|
|
(for-each (fn (k) (append! result (get o k))) (keys o))
|
|
result))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-object-entries
|
|
(fn
|
|
(o)
|
|
(cond
|
|
((dict? o)
|
|
(let
|
|
((result (list)))
|
|
(for-each
|
|
(fn
|
|
(k)
|
|
(let
|
|
((pair (list)))
|
|
(append! pair k)
|
|
(append! pair (get o k))
|
|
(append! result pair)))
|
|
(keys o))
|
|
result))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-object-assign
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 0) (dict))
|
|
(else
|
|
(let
|
|
((target (nth args 0)))
|
|
(for-each
|
|
(fn
|
|
(src)
|
|
(when
|
|
(dict? src)
|
|
(for-each
|
|
(fn (k) (dict-set! target k (get src k)))
|
|
(keys src))))
|
|
(rest args))
|
|
target)))))
|
|
|
|
(define js-object-freeze (fn (o) o))
|
|
|
|
(define
|
|
js-object-get-prototype-of
|
|
(fn
|
|
(o)
|
|
(cond
|
|
((= o nil) (error "TypeError: Cannot convert null to object"))
|
|
((js-undefined? o)
|
|
(error "TypeError: Cannot convert undefined to object"))
|
|
((dict? o)
|
|
(if (contains? (keys o) "__proto__") (get o "__proto__") nil))
|
|
(else nil))))
|
|
|
|
(define
|
|
js-object-set-prototype-of
|
|
(fn
|
|
(o proto)
|
|
(begin (when (dict? o) (dict-set! o "__proto__" proto)) o)))
|
|
|
|
(define
|
|
js-object-create
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((proto (if (empty? args) nil (nth args 0))))
|
|
(let
|
|
((obj (dict)))
|
|
(begin
|
|
(when (not (= proto nil)) (dict-set! obj "__proto__" proto))
|
|
(when
|
|
(and (>= (len args) 2) (dict? (nth args 1)))
|
|
(for-each
|
|
(fn
|
|
(k)
|
|
(dict-set!
|
|
obj
|
|
k
|
|
(get (get (nth args 1) k) "value")))
|
|
(keys (nth args 1))))
|
|
obj)))))
|
|
|
|
(define
|
|
js-object-define-property
|
|
(fn
|
|
(o key desc)
|
|
(begin
|
|
(when
|
|
(and (dict? o) (dict? desc) (contains? (keys desc) "value"))
|
|
(dict-set! o (js-to-string key) (get desc "value")))
|
|
o)))
|
|
|
|
(define
|
|
js-object-define-properties
|
|
(fn
|
|
(o descs)
|
|
(begin
|
|
(when
|
|
(and (dict? o) (dict? descs))
|
|
(for-each
|
|
(fn (k) (js-object-define-property o k (get descs k)))
|
|
(keys descs)))
|
|
o)))
|
|
|
|
(define
|
|
js-object-get-own-property-names
|
|
(fn
|
|
(o)
|
|
(cond
|
|
((list? o)
|
|
(let ((r (list))) (begin (js-list-keys-loop o 0 r) r)))
|
|
((dict? o) (js-object-keys o))
|
|
(else (list)))))
|
|
|
|
(define
|
|
js-object-get-own-property-descriptor
|
|
(fn
|
|
(o key)
|
|
(if
|
|
(and (dict? o) (contains? (keys o) (js-to-string key)))
|
|
{:configurable true :enumerable true :value (get o (js-to-string key)) :writable true}
|
|
:js-undefined)))
|
|
|
|
(define
|
|
js-object-get-own-property-descriptors
|
|
(fn
|
|
(o)
|
|
(let
|
|
((out (dict)))
|
|
(begin
|
|
(when
|
|
(dict? o)
|
|
(for-each
|
|
(fn
|
|
(k)
|
|
(dict-set! out k (js-object-get-own-property-descriptor o k)))
|
|
(keys o)))
|
|
out))))
|
|
|
|
(define js-object-is-extensible (fn (o) (not (js-undefined? o))))
|
|
|
|
(define js-object-is-frozen (fn (o) false))
|
|
|
|
(define js-object-is-sealed (fn (o) false))
|
|
|
|
(define js-object-prevent-extensions (fn (o) o))
|
|
|
|
(define js-object-seal (fn (o) o))
|
|
|
|
(define
|
|
js-object-is
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((and (js-number-is-nan a) (js-number-is-nan b)) true)
|
|
((and (= a 0) (= b 0))
|
|
(let ((ia (inspect a)) (ib (inspect b))) (= ia ib)))
|
|
(else (js-strict-eq a b)))))
|
|
|
|
(define
|
|
js-object-from-entries
|
|
(fn
|
|
(iter)
|
|
(let
|
|
((out (dict)) (lst (js-iterable-to-list iter)))
|
|
(begin
|
|
(for-each
|
|
(fn
|
|
(pair)
|
|
(when
|
|
(and (list? pair) (>= (len pair) 2))
|
|
(dict-set!
|
|
out
|
|
(js-to-string (nth pair 0))
|
|
(nth pair 1))))
|
|
lst)
|
|
out))))
|
|
|
|
(define
|
|
js-object-has-own
|
|
(fn
|
|
(o key)
|
|
(cond
|
|
((dict? o) (contains? (keys o) (js-to-string key)))
|
|
((list? o)
|
|
(let
|
|
((idx (js-to-number key)))
|
|
(and (>= idx 0) (< idx (len o)) (integer? idx))))
|
|
(else false))))
|
|
|
|
(define Object {:keys js-object-keys :getPrototypeOf js-object-get-prototype-of :isSealed js-object-is-sealed :seal js-object-seal :create js-object-create :isExtensible js-object-is-extensible :is js-object-is :setPrototypeOf js-object-set-prototype-of :getOwnPropertyNames js-object-get-own-property-names :getOwnPropertyDescriptors js-object-get-own-property-descriptors :defineProperty js-object-define-property :fromEntries js-object-from-entries :getOwnPropertyDescriptor js-object-get-own-property-descriptor :assign js-object-assign :isFrozen js-object-is-frozen :freeze js-object-freeze :values js-object-values :hasOwn js-object-has-own :prototype {:hasOwnProperty (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :toLocaleString (fn () "[object Object]") :isPrototypeOf (fn (o) (let ((this-val (js-this))) (cond ((not (dict? o)) false) (else (let ((proto (if (contains? (keys o) "__proto__") (get o "__proto__") nil))) (cond ((= proto this-val) true) ((= proto nil) false) (else ((get (get Object "prototype") "isPrototypeOf") proto)))))))) :toString (fn () "[object Object]") :propertyIsEnumerable (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :valueOf (fn () (js-this))} :__callable__ (fn (&rest args) (let ((this-val (js-this))) (let ((is-new (and (dict? this-val) (contains? (keys this-val) "__proto__") (= (get this-val "__proto__") (get Object "prototype"))))) (cond ((= (len args) 0) (if is-new this-val (dict))) ((or (= (nth args 0) nil) (js-undefined? (nth args 0))) (if is-new this-val (dict))) ((= (type-of (nth args 0)) "string") (js-new-call String (list (nth args 0)))) ((= (js-typeof (nth args 0)) "number") (js-new-call Number (list (nth args 0)))) ((= (js-typeof (nth args 0)) "boolean") (js-new-call Boolean (list (nth args 0)))) (else (nth args 0)))))) :preventExtensions js-object-prevent-extensions :entries js-object-entries :defineProperties js-object-define-properties})
|
|
|
|
(dict-set! Object "length" 1)
|
|
|
|
(dict-set! Object "name" "Object")
|
|
|
|
(dict-set! (get Object "prototype") "constructor" Object)
|
|
|
|
(define
|
|
js-delete-prop
|
|
(fn
|
|
(obj key)
|
|
(cond
|
|
((dict? obj)
|
|
(begin (dict-set! obj (js-to-string key) js-undefined) true))
|
|
(else true))))
|
|
|
|
(define
|
|
js-optchain-get
|
|
(fn
|
|
(obj key)
|
|
(if
|
|
(or (= obj nil) (js-undefined? obj))
|
|
js-undefined
|
|
(js-get-prop obj key))))
|
|
|
|
(define
|
|
js-optchain-call
|
|
(fn
|
|
(fn-val args)
|
|
(if
|
|
(or (= fn-val nil) (js-undefined? fn-val))
|
|
js-undefined
|
|
(js-call-plain fn-val args))))
|
|
|
|
(define
|
|
js-array-spread-build
|
|
(fn
|
|
(&rest items)
|
|
(let
|
|
((result (list)))
|
|
(for-each
|
|
(fn
|
|
(item)
|
|
(let
|
|
((kind (nth item 0)))
|
|
(cond
|
|
((= kind "js-spread")
|
|
(for-each
|
|
(fn (x) (append! result x))
|
|
(js-iterable-to-list (nth item 1))))
|
|
(else (append! result (nth item 1))))))
|
|
items)
|
|
result)))
|
|
|
|
(define js-array-is-array (fn (v) (list? v)))
|
|
|
|
(define js-array-of (fn (&rest args) args))
|
|
|
|
(define
|
|
js-array-proto-fn
|
|
(fn
|
|
(name)
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(let
|
|
((recv (cond ((list? this-val) this-val) ((and (dict? this-val) (contains? (keys this-val) "length")) (js-arraylike-to-list this-val)) (else this-val))))
|
|
(js-invoke-method recv name args))))))
|
|
|
|
(define
|
|
js-array-from
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 0) (list))
|
|
(else
|
|
(let
|
|
((src (js-iterable-to-list (nth args 0)))
|
|
(map-fn
|
|
(if (< (len args) 2) nil (nth args 1))))
|
|
(if
|
|
(= map-fn nil)
|
|
(let
|
|
((result (list)))
|
|
(for-each (fn (x) (append! result x)) src)
|
|
result)
|
|
(let
|
|
((result (list)) (i 0))
|
|
(for-each
|
|
(fn
|
|
(x)
|
|
(append! result (map-fn x))
|
|
(set! i (+ i 1)))
|
|
src)
|
|
result)))))))
|
|
|
|
(define Array {:of js-array-of :from js-array-from :isArray js-array-is-array :prototype {:reverse (js-array-proto-fn "reverse") :fill (js-array-proto-fn "fill") :flatMap (js-array-proto-fn "flatMap") :keys (js-array-proto-fn "keys") :forEach (js-array-proto-fn "forEach") :toString (js-array-proto-fn "toString") :copyWithin (js-array-proto-fn "copyWithin") :toReversed (js-array-proto-fn "toReversed") :pop (js-array-proto-fn "pop") :at (js-array-proto-fn "at") :push (js-array-proto-fn "push") :map (js-array-proto-fn "map") :some (js-array-proto-fn "some") :flat (js-array-proto-fn "flat") :toSorted (js-array-proto-fn "toSorted") :indexOf (js-array-proto-fn "indexOf") :unshift (js-array-proto-fn "unshift") :every (js-array-proto-fn "every") :sort (js-array-proto-fn "sort") :findIndex (js-array-proto-fn "findIndex") :toLocaleString (js-array-proto-fn "toLocaleString") :find (js-array-proto-fn "find") :includes (js-array-proto-fn "includes") :findLastIndex (js-array-proto-fn "findLastIndex") :slice (js-array-proto-fn "slice") :reduce (js-array-proto-fn "reduce") :values (js-array-proto-fn "values") :join (js-array-proto-fn "join") :reduceRight (js-array-proto-fn "reduceRight") :shift (js-array-proto-fn "shift") :filter (js-array-proto-fn "filter") :findLast (js-array-proto-fn "findLast") :concat (js-array-proto-fn "concat") :lastIndexOf (js-array-proto-fn "lastIndexOf") :splice (js-array-proto-fn "splice") :entries (js-array-proto-fn "entries")} :__callable__ (fn (&rest args) (cond ((= (len args) 0) (list)) ((and (= (len args) 1) (number? (nth args 0))) (js-make-list-of-length (js-num-to-int (nth args 0)) :js-undefined)) (else args)))})
|
|
|
|
(dict-set! Array "length" 1)
|
|
|
|
(dict-set! (get Array "prototype") "constructor" Array)
|
|
|
|
(dict-set! Array "name" "Array")
|
|
|
|
(dict-set!
|
|
(get Array "prototype")
|
|
"toString"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(let
|
|
((items (cond ((list? this-val) this-val) ((and (dict? this-val) (contains? (keys this-val) "length")) (js-arraylike-to-list this-val)) (else (list)))))
|
|
(js-list-join items ",")))))
|
|
|
|
(define
|
|
js-string-from-char-code
|
|
(fn (&rest args) (js-string-from-char-code-loop args 0 "")))
|
|
|
|
(define
|
|
js-string-from-code-point-loop
|
|
(fn
|
|
(args i acc)
|
|
(if
|
|
(>= i (len args))
|
|
acc
|
|
(let
|
|
((cp (floor (js-to-number (nth args i)))))
|
|
(if
|
|
(< cp 65536)
|
|
(js-string-from-code-point-loop
|
|
args
|
|
(+ i 1)
|
|
(str acc (js-code-to-char (js-num-to-int cp))))
|
|
(let
|
|
((hi (+ 55296 (floor (/ (- cp 65536) 1024))))
|
|
(lo (+ 56320 (modulo (- cp 65536) 1024))))
|
|
(js-string-from-code-point-loop
|
|
args
|
|
(+ i 1)
|
|
(str
|
|
(str acc (js-code-to-char (js-num-to-int hi)))
|
|
(js-code-to-char (js-num-to-int lo))))))))))
|
|
|
|
(define
|
|
js-string-from-code-point
|
|
(fn (&rest args) (js-string-from-code-point-loop args 0 "")))
|
|
|
|
(define
|
|
js-string-from-char-code-loop
|
|
(fn
|
|
(args i acc)
|
|
(if
|
|
(>= i (len args))
|
|
acc
|
|
(let
|
|
((n (js-to-number (nth args i))))
|
|
(let
|
|
((code (if (js-global-is-nan n) 0 (modulo (js-math-trunc n) 65536))))
|
|
(js-string-from-char-code-loop
|
|
args
|
|
(+ i 1)
|
|
(str acc (char-from-code code))))))))
|
|
|
|
(define
|
|
js-string-proto-fn
|
|
(fn
|
|
(name)
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(let
|
|
((s (cond ((= (type-of this-val) "string") this-val) ((and (= (type-of this-val) "dict") (contains? (keys this-val) "__js_string_value__")) (get this-val "__js_string_value__")) (else "[object Object]"))))
|
|
(js-invoke-method s name args))))))
|
|
|
|
(define String {:raw (fn (&rest args) (if (empty? args) "" (js-to-string (nth args 0)))) :prototype {:replace (js-string-proto-fn "replace") :toLocaleUpperCase (js-string-proto-fn "toLocaleUpperCase") :trimStart (js-string-proto-fn "trimStart") :includes (js-string-proto-fn "includes") :charAt (js-string-proto-fn "charAt") :match (js-string-proto-fn "match") :charCodeAt (js-string-proto-fn "charCodeAt") :slice (js-string-proto-fn "slice") :toString (js-string-proto-fn "toString") :toLocaleLowerCase (js-string-proto-fn "toLocaleLowerCase") :toUpperCase (js-string-proto-fn "toUpperCase") :trimEnd (js-string-proto-fn "trimEnd") :repeat (js-string-proto-fn "repeat") :padStart (js-string-proto-fn "padStart") :search (js-string-proto-fn "search") :substring (js-string-proto-fn "substring") :replaceAll (js-string-proto-fn "replaceAll") :trim (js-string-proto-fn "trim") :valueOf (js-string-proto-fn "valueOf") :at (js-string-proto-fn "at") :normalize (js-string-proto-fn "normalize") :split (js-string-proto-fn "split") :endsWith (js-string-proto-fn "endsWith") :indexOf (js-string-proto-fn "indexOf") :localeCompare (js-string-proto-fn "localeCompare") :toLowerCase (js-string-proto-fn "toLowerCase") :concat (js-string-proto-fn "concat") :startsWith (js-string-proto-fn "startsWith") :padEnd (js-string-proto-fn "padEnd") :codePointAt (js-string-proto-fn "codePointAt") :lastIndexOf (js-string-proto-fn "lastIndexOf")} :__callable__ (fn (&rest args) (if (= (len args) 0) "" (js-to-string (nth args 0)))) :fromCharCode js-string-from-char-code})
|
|
|
|
(dict-set! String "length" 1)
|
|
|
|
(dict-set! (get String "prototype") "constructor" String)
|
|
|
|
(dict-set! String "name" "String")
|
|
|
|
(dict-set! String "fromCodePoint" js-string-from-code-point)
|
|
|
|
(dict-set! String "fromCharCode" js-string-from-char-code)
|
|
|
|
(dict-set!
|
|
String
|
|
"__callable__"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((raw (if (= (len args) 0) "" (js-to-string (nth args 0)))))
|
|
(let
|
|
((this-val (js-this)))
|
|
(if
|
|
(and
|
|
(dict? this-val)
|
|
(contains? (keys this-val) "__proto__")
|
|
(= (get this-val "__proto__") (get String "prototype")))
|
|
(begin
|
|
(dict-set! this-val "__js_string_value__" raw)
|
|
(dict-set! this-val "length" (len raw))
|
|
this-val)
|
|
raw)))))
|
|
|
|
(define Boolean {:__callable__ (fn (&rest args) (if (= (len args) 0) false (js-to-boolean (nth args 0))))})
|
|
|
|
(dict-set!
|
|
(get String "prototype")
|
|
"matchAll"
|
|
(js-string-proto-fn "matchAll"))
|
|
|
|
(dict-set! Boolean "length" 1)
|
|
|
|
(dict-set! Boolean "name" "Boolean")
|
|
|
|
(dict-set! Boolean "prototype" {:constructor Boolean})
|
|
|
|
(dict-set!
|
|
Boolean
|
|
"__callable__"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((val (if (> (len args) 0) (js-to-boolean (nth args 0)) false)))
|
|
(let
|
|
((this-val (js-this)))
|
|
(if
|
|
(dict? this-val)
|
|
(begin
|
|
(dict-set! this-val "__js_boolean_value__" val)
|
|
(dict-set! this-val "__proto__" (get Boolean "prototype"))
|
|
this-val)
|
|
(if val true false))))))
|
|
|
|
(dict-set!
|
|
(get Boolean "prototype")
|
|
"valueOf"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(if
|
|
(and
|
|
(= (type-of this-val) "dict")
|
|
(contains? (keys this-val) "__js_boolean_value__"))
|
|
(get this-val "__js_boolean_value__")
|
|
this-val))))
|
|
|
|
(dict-set!
|
|
(get Boolean "prototype")
|
|
"toString"
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(let
|
|
((b (if (and (= (type-of this-val) "dict") (contains? (keys this-val) "__js_boolean_value__")) (get this-val "__js_boolean_value__") this-val)))
|
|
(if b "true" "false")))))
|
|
|
|
(define
|
|
parseInt
|
|
(fn
|
|
(&rest args)
|
|
(cond
|
|
((= (len args) 0) (js-nan-value))
|
|
(else
|
|
(let
|
|
((s (js-to-string (nth args 0)))
|
|
(radix-arg
|
|
(if
|
|
(< (len args) 2)
|
|
10
|
|
(js-to-number (nth args 1)))))
|
|
(let
|
|
((radix (if (or (js-number-is-nan radix-arg) (= radix-arg 0)) 10 radix-arg)))
|
|
(js-parse-int-str (js-trim s) (js-math-trunc radix))))))))
|
|
|
|
(define
|
|
js-parse-int-str
|
|
(fn
|
|
(s radix)
|
|
(cond
|
|
((= s "") (js-nan-value))
|
|
(else
|
|
(let
|
|
((first (char-at s 0)))
|
|
(cond
|
|
((= first "-")
|
|
(let
|
|
((r (js-parse-int-digits (js-string-slice s 1 (len s)) radix 0 false)))
|
|
(if (js-number-is-nan r) r (- 0 r))))
|
|
((= first "+")
|
|
(js-parse-int-digits
|
|
(js-string-slice s 1 (len s))
|
|
radix
|
|
0
|
|
false))
|
|
(else (js-parse-int-digits s radix 0 false))))))))
|
|
|
|
(define
|
|
js-parse-int-digits
|
|
(fn
|
|
(s radix acc sawdigit)
|
|
(if
|
|
(= (len s) 0)
|
|
(if sawdigit acc (js-nan-value))
|
|
(let
|
|
((c (char-at s 0)))
|
|
(let
|
|
((d (js-digit-value c radix)))
|
|
(if
|
|
(= d -1)
|
|
(if sawdigit acc (js-nan-value))
|
|
(js-parse-int-digits
|
|
(js-string-slice s 1 (len s))
|
|
radix
|
|
(+ (* acc radix) d)
|
|
true)))))))
|
|
|
|
(define
|
|
js-digit-value
|
|
(fn
|
|
(c radix)
|
|
(let
|
|
((code (char-code c)))
|
|
(let
|
|
((d (cond ((and (>= code 48) (<= code 57)) (- code 48)) ((and (>= code 97) (<= code 122)) (+ 10 (- code 97))) ((and (>= code 65) (<= code 90)) (+ 10 (- code 65))) (else -1))))
|
|
(if (or (= d -1) (>= d radix)) -1 d)))))
|
|
|
|
(define
|
|
parseFloat
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
(js-nan-value)
|
|
(let
|
|
((s (js-trim (js-to-string (nth args 0)))))
|
|
(cond
|
|
((= s "") (js-nan-value))
|
|
((= s "Infinity") (js-infinity-value))
|
|
((= s "+Infinity") (js-infinity-value))
|
|
((= s "-Infinity") (- 0 (js-infinity-value)))
|
|
(else (js-parse-float-prefix s)))))))
|
|
|
|
(define
|
|
js-parse-float-prefix
|
|
(fn
|
|
(s)
|
|
(let
|
|
((end (js-float-prefix-end s 0 false false false)))
|
|
(cond
|
|
((= end 0) (js-nan-value))
|
|
(else (js-parse-num-safe (js-string-slice s 0 end)))))))
|
|
|
|
(define
|
|
js-float-prefix-end
|
|
(fn
|
|
(s i sawdigit sawdot sawe)
|
|
(cond
|
|
((>= i (len s)) i)
|
|
(else
|
|
(let
|
|
((c (char-at s i)))
|
|
(cond
|
|
((or (= c "0") (= c "1") (= c "2") (= c "3") (= c "4") (= c "5") (= c "6") (= c "7") (= c "8") (= c "9"))
|
|
(js-float-prefix-end s (+ i 1) true sawdot sawe))
|
|
((and (= c ".") (not sawdot) (not sawe))
|
|
(js-float-prefix-end s (+ i 1) sawdigit true sawe))
|
|
((and (or (= c "e") (= c "E")) sawdigit (not sawe))
|
|
(js-float-prefix-end s (+ i 1) false sawdot true))
|
|
((and (or (= c "+") (= c "-")) (= i 0))
|
|
(js-float-prefix-end s (+ i 1) sawdigit sawdot sawe))
|
|
((and (or (= c "+") (= c "-")) sawe)
|
|
(let
|
|
((prev (char-at s (- i 1))))
|
|
(if
|
|
(or (= prev "e") (= prev "E"))
|
|
(js-float-prefix-end
|
|
s
|
|
(+ i 1)
|
|
sawdigit
|
|
sawdot
|
|
sawe)
|
|
i)))
|
|
(else i)))))))
|
|
|
|
(define
|
|
encodeURIComponent
|
|
(fn
|
|
(v)
|
|
(let ((s (js-to-string v))) (js-uri-encode-loop s 0 ""))))
|
|
|
|
(define decodeURIComponent (fn (v) (js-to-string v)))
|
|
|
|
(define encodeURI (fn (v) (js-to-string v)))
|
|
|
|
(define decodeURI (fn (v) (js-to-string v)))
|
|
|
|
(define
|
|
js-uri-encode-loop
|
|
(fn
|
|
(s i acc)
|
|
(cond
|
|
((>= i (len s)) acc)
|
|
(else
|
|
(let
|
|
((c (char-at s i)))
|
|
(let
|
|
((code (char-code c)))
|
|
(cond
|
|
((= c " ")
|
|
(js-uri-encode-loop s (+ i 1) (str acc "%20")))
|
|
((and (>= code 48) (<= code 57))
|
|
(js-uri-encode-loop s (+ i 1) (str acc c)))
|
|
((and (>= code 65) (<= code 90))
|
|
(js-uri-encode-loop s (+ i 1) (str acc c)))
|
|
((and (>= code 97) (<= code 122))
|
|
(js-uri-encode-loop s (+ i 1) (str acc c)))
|
|
((or (= c "-") (= c "_") (= c ".") (= c "~") (= c "!") (= c "*") (= c "'") (= c "(") (= c ")"))
|
|
(js-uri-encode-loop s (+ i 1) (str acc c)))
|
|
(else
|
|
(js-uri-encode-loop
|
|
s
|
|
(+ i 1)
|
|
(str acc "%" (js-hex-2 code)))))))))))
|
|
|
|
(define
|
|
js-hex-2
|
|
(fn
|
|
(n)
|
|
(let
|
|
((hi (js-math-trunc (/ n 16))) (lo (mod n 16)))
|
|
(str (js-hex-digit hi) (js-hex-digit lo)))))
|
|
|
|
(define
|
|
js-hex-digit
|
|
(fn
|
|
(d)
|
|
(cond
|
|
((< d 10) (js-to-string d))
|
|
(else (js-code-to-char (+ 55 d))))))
|
|
|
|
(define
|
|
js-json-stringify
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
js-undefined
|
|
(js-json-stringify-value (nth args 0)))))
|
|
|
|
(define
|
|
js-json-stringify-value
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((= v nil) "null")
|
|
((js-undefined? v) js-undefined)
|
|
((= (type-of v) "boolean") (if v "true" "false"))
|
|
((number? v) (js-number-to-string v))
|
|
((= (type-of v) "string") (js-json-escape-string v))
|
|
((list? v)
|
|
(let
|
|
((parts (list)))
|
|
(for-each
|
|
(fn
|
|
(x)
|
|
(let
|
|
((s (js-json-stringify-value x)))
|
|
(if
|
|
(js-undefined? s)
|
|
(append! parts "null")
|
|
(append! parts s))))
|
|
v)
|
|
(str "[" (join "," parts) "]")))
|
|
((dict? v)
|
|
(let
|
|
((parts (list)))
|
|
(for-each
|
|
(fn
|
|
(k)
|
|
(let
|
|
((val (get v k)))
|
|
(let
|
|
((vs (js-json-stringify-value val)))
|
|
(if
|
|
(not (js-undefined? vs))
|
|
(append! parts (str (js-json-escape-string k) ":" vs))))))
|
|
(keys v))
|
|
(str "{" (join "," parts) "}")))
|
|
(else "null"))))
|
|
|
|
(define
|
|
js-json-escape-string
|
|
(fn (s) (str "\"" (js-json-escape-loop s 0 "") "\"")))
|
|
|
|
(define
|
|
js-json-escape-loop
|
|
(fn
|
|
(s i acc)
|
|
(if
|
|
(>= i (len s))
|
|
acc
|
|
(let
|
|
((c (char-at s i)))
|
|
(cond
|
|
((= c "\"")
|
|
(js-json-escape-loop s (+ i 1) (str acc "\\\"")))
|
|
((= c "\\")
|
|
(js-json-escape-loop s (+ i 1) (str acc "\\\\")))
|
|
((= c "\n")
|
|
(js-json-escape-loop s (+ i 1) (str acc "\\n")))
|
|
((= c "\r")
|
|
(js-json-escape-loop s (+ i 1) (str acc "\\r")))
|
|
((= c "\t")
|
|
(js-json-escape-loop s (+ i 1) (str acc "\\t")))
|
|
(else (js-json-escape-loop s (+ i 1) (str acc c))))))))
|
|
|
|
(define
|
|
js-json-parse
|
|
(fn
|
|
(&rest args)
|
|
(if
|
|
(= (len args) 0)
|
|
js-undefined
|
|
(let
|
|
((st (dict)))
|
|
(dict-set! st "s" (js-to-string (nth args 0)))
|
|
(dict-set! st "i" 0)
|
|
(js-json-parse-value st)))))
|
|
|
|
(define
|
|
js-json-skip-ws!
|
|
(fn
|
|
(st)
|
|
(let
|
|
((s (get st "s")) (i (get st "i")))
|
|
(cond
|
|
((>= i (len s)) nil)
|
|
((or (= (char-at s i) " ") (= (char-at s i) "\t") (= (char-at s i) "\n") (= (char-at s i) "\r"))
|
|
(begin (dict-set! st "i" (+ i 1)) (js-json-skip-ws! st)))
|
|
(else nil)))))
|
|
|
|
(define
|
|
js-json-parse-value
|
|
(fn
|
|
(st)
|
|
(js-json-skip-ws! st)
|
|
(let
|
|
((s (get st "s")) (i (get st "i")))
|
|
(cond
|
|
((>= i (len s)) (error "JSON: unexpected end"))
|
|
((= (char-at s i) "\"") (js-json-parse-string st))
|
|
((= (char-at s i) "[") (js-json-parse-array st))
|
|
((= (char-at s i) "{") (js-json-parse-object st))
|
|
((= (char-at s i) "t")
|
|
(begin (dict-set! st "i" (+ i 4)) true))
|
|
((= (char-at s i) "f")
|
|
(begin (dict-set! st "i" (+ i 5)) false))
|
|
((= (char-at s i) "n")
|
|
(begin (dict-set! st "i" (+ i 4)) nil))
|
|
(else (js-json-parse-number st))))))
|
|
|
|
(define
|
|
js-json-parse-string
|
|
(fn
|
|
(st)
|
|
(let
|
|
((s (get st "s")))
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(let
|
|
((buf (list)))
|
|
(js-json-parse-string-loop st s buf)
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(join "" buf)))))
|
|
|
|
(define
|
|
js-json-parse-string-loop
|
|
(fn
|
|
(st s buf)
|
|
(let
|
|
((i (get st "i")))
|
|
(cond
|
|
((>= i (len s)) nil)
|
|
((= (char-at s i) "\"") nil)
|
|
((= (char-at s i) "\\")
|
|
(begin
|
|
(when
|
|
(< (+ i 1) (len s))
|
|
(let
|
|
((e (char-at s (+ i 1))))
|
|
(cond
|
|
((= e "n") (append! buf "\n"))
|
|
((= e "t") (append! buf "\t"))
|
|
((= e "r") (append! buf "\r"))
|
|
((= e "\"") (append! buf "\""))
|
|
((= e "\\") (append! buf "\\"))
|
|
((= e "/") (append! buf "/"))
|
|
(else (append! buf e)))))
|
|
(dict-set! st "i" (+ i 2))
|
|
(js-json-parse-string-loop st s buf)))
|
|
(else
|
|
(begin
|
|
(append! buf (char-at s i))
|
|
(dict-set! st "i" (+ i 1))
|
|
(js-json-parse-string-loop st s buf)))))))
|
|
|
|
(define
|
|
js-json-parse-number
|
|
(fn
|
|
(st)
|
|
(let
|
|
((s (get st "s")) (i (get st "i")))
|
|
(let
|
|
((start i))
|
|
(js-json-parse-number-loop st s)
|
|
(js-to-number (js-string-slice s start (get st "i")))))))
|
|
|
|
(define
|
|
js-json-parse-number-loop
|
|
(fn
|
|
(st s)
|
|
(let
|
|
((i (get st "i")))
|
|
(cond
|
|
((>= i (len s)) nil)
|
|
((or (js-is-digit? (char-at s i)) (= (char-at s i) "-") (= (char-at s i) "+") (= (char-at s i) ".") (= (char-at s i) "e") (= (char-at s i) "E"))
|
|
(begin
|
|
(dict-set! st "i" (+ i 1))
|
|
(js-json-parse-number-loop st s)))
|
|
(else nil)))))
|
|
|
|
(define
|
|
js-json-parse-array
|
|
(fn
|
|
(st)
|
|
(let
|
|
((result (list)))
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(js-json-skip-ws! st)
|
|
(cond
|
|
((and (< (get st "i") (len (get st "s"))) (= (char-at (get st "s") (get st "i")) "]"))
|
|
(begin (dict-set! st "i" (+ (get st "i") 1)) result))
|
|
(else (begin (js-json-parse-array-loop st result) result))))))
|
|
|
|
(define
|
|
js-json-parse-array-loop
|
|
(fn
|
|
(st result)
|
|
(append! result (js-json-parse-value st))
|
|
(js-json-skip-ws! st)
|
|
(let
|
|
((c (char-at (get st "s") (get st "i"))))
|
|
(cond
|
|
((= c ",")
|
|
(begin
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(js-json-skip-ws! st)
|
|
(js-json-parse-array-loop st result)))
|
|
((= c "]") (dict-set! st "i" (+ (get st "i") 1)))
|
|
(else (error "JSON: expected , or ]"))))))
|
|
|
|
(define
|
|
js-json-parse-object
|
|
(fn
|
|
(st)
|
|
(let
|
|
((result (dict)))
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(js-json-skip-ws! st)
|
|
(cond
|
|
((and (< (get st "i") (len (get st "s"))) (= (char-at (get st "s") (get st "i")) "}"))
|
|
(begin (dict-set! st "i" (+ (get st "i") 1)) result))
|
|
(else (begin (js-json-parse-object-loop st result) result))))))
|
|
|
|
(define
|
|
js-json-parse-object-loop
|
|
(fn
|
|
(st result)
|
|
(js-json-skip-ws! st)
|
|
(let
|
|
((k (js-json-parse-string st)))
|
|
(js-json-skip-ws! st)
|
|
(when
|
|
(not (= (char-at (get st "s") (get st "i")) ":"))
|
|
(error "JSON: expected :"))
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(let ((v (js-json-parse-value st))) (dict-set! result k v))
|
|
(js-json-skip-ws! st)
|
|
(let
|
|
((c (char-at (get st "s") (get st "i"))))
|
|
(cond
|
|
((= c ",")
|
|
(begin
|
|
(dict-set! st "i" (+ (get st "i") 1))
|
|
(js-json-parse-object-loop st result)))
|
|
((= c "}") (dict-set! st "i" (+ (get st "i") 1)))
|
|
(else (error "JSON: expected , or }")))))))
|
|
|
|
(define JSON {:stringify js-json-stringify :parse js-json-parse})
|
|
|
|
(define
|
|
js-promise-flush-callbacks!
|
|
(fn
|
|
(p)
|
|
(let
|
|
((cbs (get p "callbacks")))
|
|
(dict-set! p "callbacks" (list))
|
|
(for-each
|
|
(fn (cb) (js-mt-push! (fn () (js-promise-run-callback! p cb))))
|
|
cbs))))
|
|
|
|
(define
|
|
js-promise-run-callback!
|
|
(fn
|
|
(p cb)
|
|
(let
|
|
((on-fulfilled (nth cb 0))
|
|
(on-rejected (nth cb 1))
|
|
(result-promise (nth cb 2))
|
|
(state (get p "state"))
|
|
(value (get p "value")))
|
|
(cond
|
|
((= state "fulfilled")
|
|
(if
|
|
(js-function? on-fulfilled)
|
|
(js-promise-run-handler! result-promise on-fulfilled value)
|
|
(js-promise-resolve! result-promise value)))
|
|
((= state "rejected")
|
|
(if
|
|
(js-function? on-rejected)
|
|
(js-promise-run-handler! result-promise on-rejected value)
|
|
(js-promise-reject! result-promise value)))
|
|
(else :js-undefined)))))
|
|
|
|
(define
|
|
js-promise-run-handler!
|
|
(fn
|
|
(result-promise handler arg)
|
|
(let
|
|
((outcome (js-promise-try-call handler arg)))
|
|
(cond
|
|
((get outcome "threw")
|
|
(js-promise-reject! result-promise (get outcome "error")))
|
|
(else (js-promise-resolve! result-promise (get outcome "value")))))))
|
|
|
|
(define
|
|
js-call-arity-tolerant
|
|
(fn
|
|
(handler arg)
|
|
(cond
|
|
((= (type-of handler) "lambda")
|
|
(let
|
|
((params (lambda-params handler)))
|
|
(cond
|
|
((empty? params) (handler))
|
|
((= (first params) "&rest") (handler arg))
|
|
(else (handler arg)))))
|
|
(else (handler arg)))))
|
|
|
|
(define
|
|
js-promise-try-call
|
|
(fn
|
|
(handler arg)
|
|
(let
|
|
((out (dict)))
|
|
(dict-set! out "threw" false)
|
|
(dict-set! out "value" :js-undefined)
|
|
(dict-set! out "error" :js-undefined)
|
|
(guard
|
|
(e
|
|
(else
|
|
(begin
|
|
(dict-set! out "threw" true)
|
|
(dict-set! out "error" e)
|
|
out)))
|
|
(dict-set! out "value" (js-call-arity-tolerant handler arg))
|
|
out))))
|
|
|
|
(define
|
|
js-promise-then-internal!
|
|
(fn
|
|
(p on-fulfilled on-rejected)
|
|
(let
|
|
((new-p (js-make-promise)) (cb (list on-fulfilled on-rejected)))
|
|
(let
|
|
((cb3 (append cb (list new-p))))
|
|
(cond
|
|
((= (get p "state") "pending")
|
|
(dict-set!
|
|
p
|
|
"callbacks"
|
|
(append (get p "callbacks") (list cb3))))
|
|
(else (js-mt-push! (fn () (js-promise-run-callback! p cb3))))))
|
|
new-p)))
|
|
|
|
(define
|
|
js-promise-then!
|
|
(fn
|
|
(p args)
|
|
(let
|
|
((on-f (if (>= (len args) 1) (nth args 0) :js-undefined))
|
|
(on-r
|
|
(if
|
|
(>= (len args) 2)
|
|
(nth args 1)
|
|
:js-undefined)))
|
|
(js-promise-then-internal! p on-f on-r))))
|
|
|
|
(define
|
|
js-promise-catch!
|
|
(fn
|
|
(p args)
|
|
(let
|
|
((on-r (if (>= (len args) 1) (nth args 0) :js-undefined)))
|
|
(js-promise-then-internal! p :js-undefined on-r))))
|
|
|
|
(define
|
|
js-promise-finally!
|
|
(fn
|
|
(p args)
|
|
(let
|
|
((on-fin (if (>= (len args) 1) (nth args 0) :js-undefined)))
|
|
(let
|
|
((pass-val (fn (v) (begin (when (js-function? on-fin) (on-fin)) v)))
|
|
(pass-err
|
|
(fn
|
|
(r)
|
|
(begin
|
|
(when (js-function? on-fin) (on-fin))
|
|
(let
|
|
((throw-p (js-make-promise)))
|
|
(js-promise-reject! throw-p r)
|
|
throw-p)))))
|
|
(js-promise-then-internal! p pass-val pass-err)))))
|
|
|
|
(define
|
|
js-invoke-promise-method
|
|
(fn
|
|
(p name args)
|
|
(cond
|
|
((= name "then") (js-promise-then! p args))
|
|
((= name "catch") (js-promise-catch! p args))
|
|
((= name "finally") (js-promise-finally! p args))
|
|
(else (error (str "TypeError: Promise." name " is not a function"))))))
|
|
|
|
(define
|
|
js-promise-builtin-method?
|
|
(fn (name) (or (= name "then") (= name "catch") (= name "finally"))))
|
|
|
|
(define
|
|
Promise
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((executor (if (empty? args) :js-undefined (first args)))
|
|
(p (js-make-promise)))
|
|
(let
|
|
((resolve-fn (fn (&rest a) (let ((v (if (empty? a) :js-undefined (first a)))) (js-promise-resolve! p v) :js-undefined)))
|
|
(reject-fn
|
|
(fn
|
|
(&rest a)
|
|
(let
|
|
((r (if (empty? a) :js-undefined (first a))))
|
|
(js-promise-reject! p r)
|
|
:js-undefined))))
|
|
(cond
|
|
((js-function? executor)
|
|
(guard
|
|
(e (else (js-promise-reject! p e)))
|
|
(executor resolve-fn reject-fn)))
|
|
(else :js-undefined))
|
|
p))))
|
|
|
|
(define
|
|
js-promise-resolve-static
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((v (if (empty? args) :js-undefined (first args))))
|
|
(cond
|
|
((js-promise? v) v)
|
|
(else (let ((p (js-make-promise))) (js-promise-resolve! p v) p))))))
|
|
|
|
(define
|
|
js-promise-reject-static
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((r (if (empty? args) :js-undefined (first args)))
|
|
(p (js-make-promise)))
|
|
(js-promise-reject! p r)
|
|
p)))
|
|
|
|
(define
|
|
js-make-list-of-length
|
|
(fn (n fill) (js-make-list-loop (list) n fill)))
|
|
|
|
(define
|
|
js-make-list-loop
|
|
(fn
|
|
(acc n fill)
|
|
(cond
|
|
((<= n 0) acc)
|
|
(else
|
|
(begin
|
|
(append! acc fill)
|
|
(js-make-list-loop acc (- n 1) fill))))))
|
|
|
|
(define
|
|
js-promise-all-loop!
|
|
(fn
|
|
(result-p items state idx)
|
|
(cond
|
|
((>= idx (len items)) :js-undefined)
|
|
(else
|
|
(let
|
|
((item (nth items idx)) (i idx))
|
|
(let
|
|
((child (if (js-promise? item) item (js-promise-resolve-static item))))
|
|
(js-promise-then-internal!
|
|
child
|
|
(fn
|
|
(v)
|
|
(let
|
|
((results (get state "results")))
|
|
(set-nth! results i v)
|
|
(dict-set!
|
|
state
|
|
"remaining"
|
|
(- (get state "remaining") 1))
|
|
(cond
|
|
((= (get state "remaining") 0)
|
|
(js-promise-resolve! result-p results))
|
|
(else :js-undefined))))
|
|
(fn (r) (js-promise-reject! result-p r))))
|
|
(js-promise-all-loop! result-p items state (+ idx 1)))))))
|
|
|
|
(define
|
|
js-promise-all-static
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((items (if (empty? args) (list) (first args)))
|
|
(p (js-make-promise)))
|
|
(cond
|
|
((= (len items) 0)
|
|
(begin (js-promise-resolve! p (list)) p))
|
|
(else
|
|
(let
|
|
((n (len items)) (state (dict)))
|
|
(dict-set! state "remaining" n)
|
|
(dict-set!
|
|
state
|
|
"results"
|
|
(js-make-list-of-length n :js-undefined))
|
|
(js-promise-all-loop! p items state 0)
|
|
p))))))
|
|
|
|
(define
|
|
js-promise-race-static
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((items (if (empty? args) (list) (first args)))
|
|
(p (js-make-promise)))
|
|
(for-each
|
|
(fn
|
|
(item)
|
|
(let
|
|
((child (if (js-promise? item) item (js-promise-resolve-static item))))
|
|
(js-promise-then-internal!
|
|
child
|
|
(fn (v) (js-promise-resolve! p v))
|
|
(fn (r) (js-promise-reject! p r)))))
|
|
items)
|
|
p)))
|
|
|
|
(define __js_promise_statics__ (dict))
|
|
|
|
(dict-set! __js_promise_statics__ "resolve" js-promise-resolve-static)
|
|
|
|
(dict-set! __js_promise_statics__ "reject" js-promise-reject-static)
|
|
|
|
(dict-set! __js_promise_statics__ "all" js-promise-all-static)
|
|
|
|
(dict-set! __js_promise_statics__ "race" js-promise-race-static)
|
|
|
|
(define
|
|
js-async-wrap
|
|
(fn
|
|
(thunk)
|
|
(let
|
|
((p (js-make-promise)))
|
|
(guard
|
|
(e (else (js-promise-reject! p e)))
|
|
(let
|
|
((v (thunk)))
|
|
(cond
|
|
((js-promise? v)
|
|
(js-promise-then-internal!
|
|
v
|
|
(fn (x) (js-promise-resolve! p x))
|
|
(fn (r) (js-promise-reject! p r))))
|
|
(else (js-promise-resolve! p v)))))
|
|
p)))
|
|
|
|
(define
|
|
js-await-value
|
|
(fn
|
|
(v)
|
|
(cond
|
|
((not (js-promise? v)) v)
|
|
(else
|
|
(begin
|
|
(js-drain-microtasks!)
|
|
(let
|
|
((state (get v "state")))
|
|
(cond
|
|
((= state "fulfilled") (get v "value"))
|
|
((= state "rejected") (raise (get v "value")))
|
|
(else
|
|
(begin
|
|
(js-drain-microtasks!)
|
|
(let
|
|
((state2 (get v "state")))
|
|
(cond
|
|
((= state2 "fulfilled") (get v "value"))
|
|
((= state2 "rejected") (raise (get v "value")))
|
|
(else (error "await on pending Promise (no scheduler)")))))))))))))
|
|
|
|
(define __drain (fn () (js-drain-microtasks!) :js-undefined))
|
|
|
|
(define __js_regex_platform__ (dict))
|
|
|
|
(define
|
|
js-regex-platform-override!
|
|
(fn (op impl) (dict-set! __js_regex_platform__ op impl)))
|
|
|
|
(define
|
|
js-regex?
|
|
(fn (v) (and (dict? v) (contains? (keys v) "__js_regex__"))))
|
|
|
|
(define
|
|
js-regex-has-flag?
|
|
(fn (flags ch) (>= (js-string-index-of flags ch 0) 0)))
|
|
|
|
(define
|
|
js-regex-new
|
|
(fn
|
|
(pattern flags)
|
|
(let
|
|
((rx (dict))
|
|
(fl (if (js-undefined? flags) "" (if (= flags nil) "" flags))))
|
|
(dict-set! rx "__js_regex__" true)
|
|
(dict-set! rx "source" pattern)
|
|
(dict-set! rx "flags" fl)
|
|
(dict-set! rx "global" (js-regex-has-flag? fl "g"))
|
|
(dict-set! rx "ignoreCase" (js-regex-has-flag? fl "i"))
|
|
(dict-set! rx "multiline" (js-regex-has-flag? fl "m"))
|
|
(dict-set! rx "sticky" (js-regex-has-flag? fl "y"))
|
|
(dict-set! rx "unicode" (js-regex-has-flag? fl "u"))
|
|
(dict-set! rx "dotAll" (js-regex-has-flag? fl "s"))
|
|
(dict-set! rx "hasIndices" (js-regex-has-flag? fl "d"))
|
|
(dict-set! rx "lastIndex" 0)
|
|
rx)))
|
|
|
|
(define
|
|
js-regex-stub-test
|
|
(fn
|
|
(rx s)
|
|
(let
|
|
((src (get rx "source")) (ci (get rx "ignoreCase")))
|
|
(let
|
|
((hay (if ci (js-lower-case s) s))
|
|
(needle (if ci (js-lower-case src) src)))
|
|
(>= (js-string-index-of hay needle 0) 0)))))
|
|
|
|
(define
|
|
js-regex-stub-exec
|
|
(fn
|
|
(rx s)
|
|
(let
|
|
((src (get rx "source")) (ci (get rx "ignoreCase")))
|
|
(let
|
|
((hay (if ci (js-lower-case s) s))
|
|
(needle (if ci (js-lower-case src) src)))
|
|
(let
|
|
((idx (js-string-index-of hay needle 0)))
|
|
(if
|
|
(= idx -1)
|
|
nil
|
|
(let
|
|
((matched (js-string-slice s idx (+ idx (len src))))
|
|
(res (list)))
|
|
(append! res matched)
|
|
res)))))))
|
|
|
|
(define
|
|
js-regex-invoke-method
|
|
(fn
|
|
(rx name args)
|
|
(cond
|
|
((= name "test")
|
|
(let
|
|
((impl (get __js_regex_platform__ "test"))
|
|
(arg
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0)))))
|
|
(if
|
|
(js-undefined? impl)
|
|
(js-regex-stub-test rx arg)
|
|
(impl rx arg))))
|
|
((= name "exec")
|
|
(let
|
|
((impl (get __js_regex_platform__ "exec"))
|
|
(arg
|
|
(if
|
|
(= (len args) 0)
|
|
""
|
|
(js-to-string (nth args 0)))))
|
|
(if
|
|
(js-undefined? impl)
|
|
(js-regex-stub-exec rx arg)
|
|
(impl rx arg))))
|
|
((= name "toString")
|
|
(str "/" (get rx "source") "/" (get rx "flags")))
|
|
(else js-undefined))))
|
|
|
|
(begin
|
|
(dict-set! Object "__proto__" (get js-function-global "prototype"))
|
|
(dict-set! Array "__proto__" (get js-function-global "prototype"))
|
|
(dict-set! Number "__proto__" (get js-function-global "prototype"))
|
|
(dict-set! String "__proto__" (get js-function-global "prototype"))
|
|
(dict-set! Boolean "__proto__" (get js-function-global "prototype")))
|
|
|
|
(define js-global {:undefined js-undefined :JSON JSON :parseInt parseInt :Object Object :isNaN js-global-is-nan :Infinity inf :NaN 0 :String String :Boolean Boolean :Array Array :Math Math :parseFloat parseFloat :Number Number :console console :isFinite js-global-is-finite})
|