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))))
|
||||
((= 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))))))))
|
||||
(else js-undefined))))
|
||||
|
||||
(define
|
||||
@@ -1299,6 +1366,9 @@
|
||||
((= 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"))
|
||||
(else js-undefined)))
|
||||
((= (type-of obj) "dict")
|
||||
(js-dict-get-walk obj (js-to-string key)))
|
||||
@@ -1680,7 +1750,30 @@
|
||||
|
||||
(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
|
||||
js-string-from-char-code
|
||||
@@ -2346,8 +2439,6 @@
|
||||
((matched (js-string-slice s idx (+ idx (len src))))
|
||||
(res (list)))
|
||||
(append! res matched)
|
||||
(dict-set! res "index" idx)
|
||||
(dict-set! res "input" s)
|
||||
res)))))))
|
||||
|
||||
(define
|
||||
|
||||
@@ -1035,6 +1035,24 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 2202)
|
||||
(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
|
||||
|
||||
|
||||
@@ -1591,6 +1609,16 @@ check 2200 "for-of array" '6'
|
||||
check 2201 "for-in object keys count" '2'
|
||||
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))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
||||
|
||||
Reference in New Issue
Block a user