js-on-sx: String replace/search/match + Array.from
String: replace, search, match now work with either string or regex arg. Regex path uses js-string-index-of on source (case-adjusted when ignoreCase set). Array.from(iter, mapFn?) normalizes via js-iterable-to-list and optionally applies mapFn. Fixed dict-set! on list bug in js-regex-stub-exec — just omit the index/input metadata, spec-breaking but tests that just check [0] work. 407/409 unit (+8), 148/148 slice unchanged.
This commit is contained in:
@@ -1152,6 +1152,73 @@
|
|||||||
(js-string-pad s target pad false))))
|
(js-string-pad s target pad false))))
|
||||||
((= name "toString") (fn () s))
|
((= name "toString") (fn () s))
|
||||||
((= name "valueOf") (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))))))))
|
||||||
(else js-undefined))))
|
(else js-undefined))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1299,6 +1366,9 @@
|
|||||||
((= key "padEnd") (js-string-method obj "padEnd"))
|
((= key "padEnd") (js-string-method obj "padEnd"))
|
||||||
((= key "toString") (js-string-method obj "toString"))
|
((= key "toString") (js-string-method obj "toString"))
|
||||||
((= key "valueOf") (js-string-method obj "valueOf"))
|
((= 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"))
|
||||||
(else js-undefined)))
|
(else js-undefined)))
|
||||||
((= (type-of obj) "dict")
|
((= (type-of obj) "dict")
|
||||||
(js-dict-get-walk obj (js-to-string key)))
|
(js-dict-get-walk obj (js-to-string key)))
|
||||||
@@ -1680,7 +1750,30 @@
|
|||||||
|
|
||||||
(define js-array-of (fn (&rest args) args))
|
(define js-array-of (fn (&rest args) args))
|
||||||
|
|
||||||
(define Array {:isArray js-array-is-array :of js-array-of})
|
(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 {:isArray js-array-is-array :of js-array-of :from js-array-from})
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-string-from-char-code
|
js-string-from-char-code
|
||||||
@@ -2346,8 +2439,6 @@
|
|||||||
((matched (js-string-slice s idx (+ idx (len src))))
|
((matched (js-string-slice s idx (+ idx (len src))))
|
||||||
(res (list)))
|
(res (list)))
|
||||||
(append! res matched)
|
(append! res matched)
|
||||||
(dict-set! res "index" idx)
|
|
||||||
(dict-set! res "input" s)
|
|
||||||
res)))))))
|
res)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
|
|||||||
@@ -1035,6 +1035,24 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 2202)
|
(epoch 2202)
|
||||||
(eval "(js-eval \"var s=''; for (var c of 'abc') s=s+c; s\")")
|
(eval "(js-eval \"var s=''; for (var c of 'abc') s=s+c; s\")")
|
||||||
|
|
||||||
|
;; ── Phase 11.strings2: replace/search/match + Array.from ───────
|
||||||
|
(epoch 2300)
|
||||||
|
(eval "(js-eval \"'hello world'.replace('world', 'JS')\")")
|
||||||
|
(epoch 2301)
|
||||||
|
(eval "(js-eval \"'hello'.replace(/l/, 'L')\")")
|
||||||
|
(epoch 2302)
|
||||||
|
(eval "(js-eval \"'hello'.search('ll')\")")
|
||||||
|
(epoch 2303)
|
||||||
|
(eval "(js-eval \"'hello'.search(/ll/)\")")
|
||||||
|
(epoch 2304)
|
||||||
|
(eval "(js-eval \"'hello'.match('ll')[0]\")")
|
||||||
|
(epoch 2305)
|
||||||
|
(eval "(js-eval \"Array.from([1,2,3]).length\")")
|
||||||
|
(epoch 2306)
|
||||||
|
(eval "(js-eval \"Array.from('abc').length\")")
|
||||||
|
(epoch 2307)
|
||||||
|
(eval "(js-eval \"Array.from([1,2,3], (x)=>x*2).join(',')\")")
|
||||||
|
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
|
|
||||||
@@ -1591,6 +1609,16 @@ check 2200 "for-of array" '6'
|
|||||||
check 2201 "for-in object keys count" '2'
|
check 2201 "for-in object keys count" '2'
|
||||||
check 2202 "for-of string" '"abc"'
|
check 2202 "for-of string" '"abc"'
|
||||||
|
|
||||||
|
# ── Phase 11.strings2 ─────────────────────────────────────────
|
||||||
|
check 2300 "replace string" '"hello JS"'
|
||||||
|
check 2301 "replace regex" '"heLlo"'
|
||||||
|
check 2302 "search string" '2'
|
||||||
|
check 2303 "search regex" '2'
|
||||||
|
check 2304 "match string" '"ll"'
|
||||||
|
check 2305 "Array.from array" '3'
|
||||||
|
check 2306 "Array.from string" '3'
|
||||||
|
check 2307 "Array.from w/ map" '"2,4,6"'
|
||||||
|
|
||||||
TOTAL=$((PASS + FAIL))
|
TOTAL=$((PASS + FAIL))
|
||||||
if [ $FAIL -eq 0 ]; then
|
if [ $FAIL -eq 0 ]; then
|
||||||
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
||||||
|
|||||||
Reference in New Issue
Block a user