JS 'globalThis' now rewrites to SX (js-global) — the global object dict. JS 'eval' rewrites to js-global-eval, a no-op stub that echoes its first arg. Many test262 tests probe eval's existence or pass simple literals through it; a no-op is better than 'Undefined symbol: eval'. A full eval would require plumbing js-eval into the runtime with access to the enclosing lexical scope — non-trivial. The stub unblocks tests that just need eval to be callable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3544 lines
102 KiB
Plaintext
3544 lines
102 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 () (/ 0 0)))
|
|
|
|
(define js-infinity-value (fn () (/ 1 0)))
|
|
|
|
;; ── Type predicates ───────────────────────────────────────────────
|
|
|
|
(define js-max-value-approx (fn () (js-max-value-loop 1 1000)))
|
|
|
|
;; ── Boolean coercion (ToBoolean) ──────────────────────────────────
|
|
|
|
(define
|
|
js-global-eval
|
|
(fn (&rest args) (if (empty? args) :js-undefined (nth args 0))))
|
|
|
|
;; ── Numeric coercion (ToNumber) ───────────────────────────────────
|
|
|
|
(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)))))))
|
|
|
|
;; 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-undefined :js-undefined)
|
|
|
|
;; 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-undefined? (fn (v) (= v :js-undefined)))
|
|
|
|
;; Minimal string->number for the slice. Handles integers, negatives,
|
|
;; and simple decimals. Returns 0 on malformed input.
|
|
(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"))))
|
|
|
|
(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-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)))))))))
|
|
|
|
;; 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-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") "")
|
|
((= 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)"))))))
|
|
|
|
;; ── String coercion (ToString) ────────────────────────────────────
|
|
|
|
(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)"))))))
|
|
|
|
;; ── Arithmetic (JS rules) ─────────────────────────────────────────
|
|
|
|
;; JS `+`: if either operand is a string → string concat, else numeric.
|
|
(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 "")))))))
|
|
|
|
(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))))))
|
|
|
|
(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))))))
|
|
|
|
(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)))
|
|
(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))))))
|
|
|
|
(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) (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")))))))))
|
|
|
|
;; Bitwise + logical-not
|
|
(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))))
|
|
|
|
;; ── Equality ──────────────────────────────────────────────────────
|
|
|
|
;; Strict equality (===): no coercion; js-undefined matches 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)))
|
|
|
|
;; Abstract equality (==): type coercion rules.
|
|
;; Simplified: number↔string coerce both to number; null == undefined;
|
|
;; everything else falls back to strict equality.
|
|
(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 ""))))
|
|
|
|
;; ── Relational comparisons ────────────────────────────────────────
|
|
|
|
;; Abstract relational comparison from ES5.
|
|
;; Numbers compare numerically; two strings compare lexicographically;
|
|
;; mixed types coerce both to numbers.
|
|
(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)
|
|
(error "TypeError: 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)) (= (type-of ret) "dict"))
|
|
ret
|
|
obj))))))
|
|
|
|
(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))))))
|
|
|
|
(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))))))
|
|
|
|
(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))))
|
|
|
|
;; ── 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
|
|
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))))
|
|
|
|
(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))))
|
|
|
|
;; Setter — mutates the dict. Returns the new value (JS assignment yields rhs).
|
|
(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))))
|
|
|
|
;; ── 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
|
|
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
|
|
js-function?
|
|
(fn
|
|
(v)
|
|
(let
|
|
((t (type-of v)))
|
|
(or
|
|
(= t "lambda")
|
|
(= t "function")
|
|
(= t "component")
|
|
(and (= t "dict") (contains? (keys v) "__callable__"))))))
|
|
|
|
;; ── console.log ───────────────────────────────────────────────────
|
|
|
|
;; Trivial bridge. `log-info` is available on OCaml; fall back to print.
|
|
(define __js_proto_table__ (dict))
|
|
|
|
(define __js_next_id__ (dict))
|
|
|
|
;; ── Math object ───────────────────────────────────────────────────
|
|
|
|
(dict-set! __js_next_id__ "n" 0)
|
|
(define
|
|
js-get-ctor-proto
|
|
(fn
|
|
(ctor)
|
|
(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))
|
|
(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)))))) ; deterministic placeholder for tests
|
|
|
|
(define
|
|
js-is-numeric-string?
|
|
(fn (s) (js-is-numeric-loop s 0 false false false)))
|
|
|
|
;; 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-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-num-from-string
|
|
(fn
|
|
(s)
|
|
(let
|
|
((trimmed (js-trim s)))
|
|
(cond
|
|
((= trimmed "") 0)
|
|
(else (js-parse-decimal trimmed 0 0 1 false 0))))))
|
|
|
|
(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 (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-number-to-string (fn (n) (str 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) (js-to-number b))))
|
|
|
|
(define js-mod (fn (a b) (mod (js-to-number a) (js-to-number b))))
|
|
|
|
(define js-pow (fn (a b) (pow (js-to-number a) (js-to-number b))))
|
|
|
|
(define js-neg (fn (a) (- 0 (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)))
|
|
(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 i)))
|
|
(if
|
|
(and (>= idx 0) (< idx (len s)))
|
|
(char-code (char-at s idx))
|
|
0))))
|
|
((= name "indexOf")
|
|
(fn (needle) (js-string-index-of s (js-to-string needle) 0)))
|
|
((= 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 (sep) (js-string-split s (js-to-string sep))))
|
|
((= 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 "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))))
|
|
(js-string-last-index-of s needle (- (len s) (len needle)))))))
|
|
((= 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-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))
|
|
((= 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-undefined)))
|
|
((= (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 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")))
|
|
(cond
|
|
((= key "prototype") (js-get-ctor-proto obj))
|
|
((= key "name") "")
|
|
((= key "length") (js-fn-length obj))
|
|
(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))
|
|
(else js-undefined)))))
|
|
|
|
(define
|
|
js-num-to-int
|
|
(fn
|
|
(v)
|
|
(let
|
|
((n (if (number? v) v (js-to-number v))))
|
|
(if (>= n 0) (floor n) (- 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))))))
|
|
((= key "length") 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) (- 0 (/ 1 0)))
|
|
(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) (/ 1 0))
|
|
(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)))))))
|
|
|
|
(define Math {:random js-math-random :trunc js-math-trunc :LN10 2.30259 :SQRT1_2 0.707107 :floor js-math-floor :PI 3.14159 :sqrt js-math-sqrt :hypot js-math-hypot :LOG2E 1.4427 :round js-math-round :ceil js-math-ceil :abs js-math-abs :pow js-math-pow :max js-math-max :LOG10E 0.434294 :SQRT2 1.41421 :cbrt js-math-cbrt :min js-math-min :sign js-math-sign :E 2.71828 :LN2 0.693147})
|
|
|
|
(define
|
|
js-number-is-finite
|
|
(fn
|
|
(v)
|
|
(and
|
|
(number? v)
|
|
(not (js-number-is-nan v))
|
|
(not (= v (/ 1 0)))
|
|
(not (= v (/ -1 0))))))
|
|
|
|
(define
|
|
js-number-is-nan
|
|
(fn
|
|
(v)
|
|
(and (number? v) (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 {:isFinite js-number-is-finite :MAX_SAFE_INTEGER 9007199254740991 :EPSILON 2.22045e-16 :MAX_VALUE (js-max-value-approx) :POSITIVE_INFINITY (js-infinity-value) :__callable__ js-to-number :isInteger js-number-is-integer :prototype {:valueOf (fn () (js-this)) :toPrecision (fn (&rest args) (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)))) :toLocaleString (fn () (js-to-string (js-this))) :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)))} :isNaN js-number-is-nan :isSafeInteger js-number-is-safe-integer :NEGATIVE_INFINITY (- 0 (js-infinity-value)) :NaN (js-nan-value) :MIN_VALUE 4.94066e-324 :MIN_SAFE_INTEGER -9007199254740991})
|
|
|
|
(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)))
|
|
{:writable true :value (get o (js-to-string key)) :enumerable true :configurable 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 {:entries js-object-entries :defineProperties js-object-define-properties :__callable__ (fn (&rest args) (cond ((= (len args) 0) (dict)) (else (nth args 0)))) :preventExtensions js-object-prevent-extensions :prototype {:valueOf (fn () (js-this)) :propertyIsEnumerable (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :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]") :hasOwnProperty (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :toLocaleString (fn () "[object Object]")} :values js-object-values :hasOwn js-object-has-own :freeze js-object-freeze :assign js-object-assign :isFrozen js-object-is-frozen :getOwnPropertyDescriptor js-object-get-own-property-descriptor :fromEntries js-object-from-entries :defineProperty js-object-define-property :setPrototypeOf js-object-set-prototype-of :getOwnPropertyNames js-object-get-own-property-names :getOwnPropertyDescriptors js-object-get-own-property-descriptors :create js-object-create :isExtensible js-object-is-extensible :is js-object-is :keys js-object-keys :getPrototypeOf js-object-get-prototype-of :isSealed js-object-is-sealed :seal js-object-seal})
|
|
|
|
(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 {:__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))) :prototype {:entries (js-array-proto-fn "entries") :concat (js-array-proto-fn "concat") :lastIndexOf (js-array-proto-fn "lastIndexOf") :splice (js-array-proto-fn "splice") :filter (js-array-proto-fn "filter") :findLast (js-array-proto-fn "findLast") :shift (js-array-proto-fn "shift") :join (js-array-proto-fn "join") :reduceRight (js-array-proto-fn "reduceRight") :values (js-array-proto-fn "values") :reduce (js-array-proto-fn "reduce") :slice (js-array-proto-fn "slice") :includes (js-array-proto-fn "includes") :findLastIndex (js-array-proto-fn "findLastIndex") :find (js-array-proto-fn "find") :toLocaleString (js-array-proto-fn "toLocaleString") :findIndex (js-array-proto-fn "findIndex") :sort (js-array-proto-fn "sort") :every (js-array-proto-fn "every") :indexOf (js-array-proto-fn "indexOf") :unshift (js-array-proto-fn "unshift") :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") :at (js-array-proto-fn "at") :pop (js-array-proto-fn "pop") :toReversed (js-array-proto-fn "toReversed") :copyWithin (js-array-proto-fn "copyWithin") :toString (js-array-proto-fn "toString") :forEach (js-array-proto-fn "forEach") :fill (js-array-proto-fn "fill") :flatMap (js-array-proto-fn "flatMap") :keys (js-array-proto-fn "keys") :reverse (js-array-proto-fn "reverse")} :isArray js-array-is-array :of js-array-of :from js-array-from})
|
|
|
|
(define
|
|
js-string-from-char-code
|
|
(fn (&rest args) (js-string-from-char-code-loop args 0 "")))
|
|
|
|
(define
|
|
js-string-from-char-code-loop
|
|
(fn
|
|
(args i acc)
|
|
(if
|
|
(>= i (len args))
|
|
acc
|
|
(js-string-from-char-code-loop
|
|
args
|
|
(+ i 1)
|
|
(str acc (js-code-to-char (js-num-to-int (nth args i))))))))
|
|
|
|
(define
|
|
js-string-proto-fn
|
|
(fn
|
|
(name)
|
|
(fn
|
|
(&rest args)
|
|
(let
|
|
((this-val (js-this)))
|
|
(js-invoke-method (js-to-string this-val) name args)))))
|
|
|
|
(define String {:fromCharCode js-string-from-char-code :__callable__ (fn (&rest args) (if (= (len args) 0) "" (js-to-string (nth args 0)))) :prototype {: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") :indexOf (js-string-proto-fn "indexOf") :localeCompare (js-string-proto-fn "localeCompare") :split (js-string-proto-fn "split") :endsWith (js-string-proto-fn "endsWith") :trim (js-string-proto-fn "trim") :valueOf (js-string-proto-fn "valueOf") :at (js-string-proto-fn "at") :normalize (js-string-proto-fn "normalize") :substring (js-string-proto-fn "substring") :replaceAll (js-string-proto-fn "replaceAll") :repeat (js-string-proto-fn "repeat") :padStart (js-string-proto-fn "padStart") :search (js-string-proto-fn "search") :toUpperCase (js-string-proto-fn "toUpperCase") :trimEnd (js-string-proto-fn "trimEnd") :toString (js-string-proto-fn "toString") :toLocaleLowerCase (js-string-proto-fn "toLocaleLowerCase") :charCodeAt (js-string-proto-fn "charCodeAt") :slice (js-string-proto-fn "slice") :charAt (js-string-proto-fn "charAt") :match (js-string-proto-fn "match") :includes (js-string-proto-fn "includes") :trimStart (js-string-proto-fn "trimStart") :toLocaleUpperCase (js-string-proto-fn "toLocaleUpperCase") :replace (js-string-proto-fn "replace")} :raw (fn (&rest args) (if (empty? args) "" (js-to-string (nth args 0))))})
|
|
|
|
(define Boolean {:__callable__ (fn (&rest args) (if (= (len args) 0) false (js-to-boolean (nth args 0))))})
|
|
|
|
(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 {:parse js-json-parse :stringify js-json-stringify})
|
|
|
|
(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))))
|
|
|
|
(define js-global {:isFinite js-global-is-finite :console console :Number Number :parseFloat parseFloat :Math Math :Array Array :Boolean Boolean :String String :NaN 0 :Infinity inf :isNaN js-global-is-nan :Object Object :parseInt parseInt :JSON JSON :undefined js-undefined})
|