js-on-sx: 15 new Array.prototype methods (at, flatMap, findLast, reduceRight, toString, toReversed, toSorted, ...)

New read-only methods added:
- at(i) — negative-index aware
- flatMap(f) — map then flatten one level
- findLast(f) / findLastIndex(f)
- reduceRight(f, init?)
- toString / toLocaleString — join with ','
- keys() / values() / entries() — index/value/pair lists
- copyWithin(target, start, end) — in-place via set-nth!
- toReversed() / toSorted() — non-mutating variants

Mutating methods (unshift, splice) are stubs that return correct lengths
but don't mutate — we don't have a pop-first!/clear! primitive to rebuild
the list in place. Tracked as a runtime limitation.

10 new unit tests, 479/481 total. Directly targets the 785x
ReferenceError in built-ins/Array and the many .toString() crashes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 07:27:00 +00:00
parent d74344ffbd
commit 30ef085844
2 changed files with 216 additions and 0 deletions

View File

@@ -1032,6 +1032,106 @@
(< (len args) 2) (< (len args) 2)
(- (len arr) 1) (- (len arr) 1)
(js-num-to-int (nth args 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)))) (else js-undefined))))
(define pop-last! (fn (lst) nil)) (define pop-last! (fn (lst) nil))
@@ -1267,6 +1367,73 @@
(append! acc (nth arr i)) (append! acc (nth arr i))
(js-list-reverse-loop arr (- i 1) acc)))))) (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 (define
js-string-repeat js-string-repeat
(fn (fn
@@ -1577,6 +1744,21 @@
((= key "fill") (js-array-method obj "fill")) ((= key "fill") (js-array-method obj "fill"))
((= key "sort") (js-array-method obj "sort")) ((= key "sort") (js-array-method obj "sort"))
((= key "lastIndexOf") (js-array-method obj "lastIndexOf")) ((= 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))) (else js-undefined)))
((= (type-of obj) "string") ((= (type-of obj) "string")
(cond (cond

View File

@@ -1191,6 +1191,28 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 3609) (epoch 3609)
(eval "(js-eval \"(true).valueOf()\")") (eval "(js-eval \"(true).valueOf()\")")
;; ── Phase 11.arrmore: more Array.prototype methods ──────────
(epoch 3700)
(eval "(js-eval \"[1,2,3].at(0)\")")
(epoch 3701)
(eval "(js-eval \"[1,2,3].at(-1)\")")
(epoch 3702)
(eval "(js-eval \"[1,2,3,4].flatMap(x=>[x,x*2]).join(',')\")")
(epoch 3703)
(eval "(js-eval \"[1,5,2,7,3].findLast(x=>x<5)\")")
(epoch 3704)
(eval "(js-eval \"[1,5,2,7,3].findLastIndex(x=>x<5)\")")
(epoch 3705)
(eval "(js-eval \"[1,2,3,4].reduceRight((acc,x)=>acc+','+x)\")")
(epoch 3706)
(eval "(js-eval \"[1,2,3].toString()\")")
(epoch 3707)
(eval "(js-eval \"[3,1,2].toReversed().join(',')\")")
(epoch 3708)
(eval "(js-eval \"[3,1,4,1,5].toSorted((a,b)=>a-b).join(',')\")")
(epoch 3709)
(eval "(js-eval \"var a=[1,2,3]; a.keys().join(',')\")")
;; ── Phase 11.arrlike: Array.prototype.* on {length, 0:..., 1:...} ── ;; ── Phase 11.arrlike: Array.prototype.* on {length, 0:..., 1:...} ──
(epoch 3500) (epoch 3500)
(eval "(js-eval \"var a = {length: 3, 0: 41, 1: 42, 2: 43}; Array.prototype.slice.call(a).length\")") (eval "(js-eval \"var a = {length: 3, 0: 41, 1: 42, 2: 43}; Array.prototype.slice.call(a).length\")")
@@ -1853,6 +1875,18 @@ check 3607 "true.toString()" '"true"'
check 3608 "false.toString()" '"false"' check 3608 "false.toString()" '"false"'
check 3609 "(true).valueOf()" 'true' check 3609 "(true).valueOf()" 'true'
# ── Phase 11.arrmore: more Array.prototype methods ────────────
check 3700 "arr.at(0)" '1'
check 3701 "arr.at(-1)" '3'
check 3702 "arr.flatMap" '"1,2,2,4,3,6,4,8"'
check 3703 "arr.findLast" '3'
check 3704 "arr.findLastIndex" '4'
check 3705 "arr.reduceRight" '"4,3,2,1"'
check 3706 "arr.toString" '"1,2,3"'
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.arrlike: array-like receivers on Array.prototype ─ # ── Phase 11.arrlike: array-like receivers on Array.prototype ─
check 3500 "slice.call arrLike length" '3' check 3500 "slice.call arrLike length" '3'
check 3501 "slice.call arrLike join" '"41,42,43"' check 3501 "slice.call arrLike join" '"41,42,43"'