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 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))
@@ -1267,6 +1367,73 @@
(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
@@ -1577,6 +1744,21 @@
((= 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