diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index 08d0ab7b..58ad2369 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -1624,6 +1624,61 @@ (= 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 @@ -1654,6 +1709,36 @@ ((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 @@ -1791,6 +1876,18 @@ ((= 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))) diff --git a/lib/js/test.sh b/lib/js/test.sh index ae9fc823..9ca2f09e 100755 --- a/lib/js/test.sh +++ b/lib/js/test.sh @@ -1213,6 +1213,28 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 3709) (eval "(js-eval \"var a=[1,2,3]; a.keys().join(',')\")") +;; ── Phase 11.strmore: more String.prototype methods ───────── +(epoch 3800) +(eval "(js-eval \"'hello'.at(0)\")") +(epoch 3801) +(eval "(js-eval \"'hello'.at(-1)\")") +(epoch 3802) +(eval "(js-eval \"'hello'.codePointAt(0)\")") +(epoch 3803) +(eval "(js-eval \"'hello world hello'.lastIndexOf('hello')\")") +(epoch 3804) +(eval "(js-eval \"'abc'.localeCompare('abd')\")") +(epoch 3805) +(eval "(js-eval \"'abc'.localeCompare('abc')\")") +(epoch 3806) +(eval "(js-eval \"'hello hello'.replaceAll('hello', 'bye')\")") +(epoch 3807) +(eval "(js-eval \"'a,b,c'.replaceAll(',', '-')\")") +(epoch 3808) +(eval "(js-eval \"'hi'.toLocaleUpperCase()\")") +(epoch 3809) +(eval "(js-eval \"'HI'.toLocaleLowerCase()\")") + ;; ── Phase 11.arrlike: Array.prototype.* on {length, 0:..., 1:...} ── (epoch 3500) (eval "(js-eval \"var a = {length: 3, 0: 41, 1: 42, 2: 43}; Array.prototype.slice.call(a).length\")") @@ -1887,6 +1909,18 @@ check 3707 "arr.toReversed" '"2,1,3"' check 3708 "arr.toSorted" '"1,1,3,4,5"' check 3709 "arr.keys" '"0,1,2"' +# ── Phase 11.strmore: more String.prototype methods ─────────── +check 3800 "'hello'.at(0)" '"h"' +check 3801 "'hello'.at(-1)" '"o"' +check 3802 "'hello'.codePointAt(0)" '104' +check 3803 "lastIndexOf found" '12' +check 3804 "localeCompare less" '-1' +check 3805 "localeCompare equal" '0' +check 3806 "replaceAll multiple" '"bye bye"' +check 3807 "replaceAll commas" '"a-b-c"' +check 3808 "toLocaleUpperCase" '"HI"' +check 3809 "toLocaleLowerCase" '"hi"' + # ── Phase 11.arrlike: array-like receivers on Array.prototype ─ check 3500 "slice.call arrLike length" '3' check 3501 "slice.call arrLike join" '"41,42,43"'