erlang: lists sublist/2,3 + nthtail/2 + split/2 + droplast/1 (854/854)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s

Slicing family in lib/erlang/lists-ext.sx. sublist lenient; nthtail
and split strict (badarg when list shorter than N); droplast raises
on []. lists_ext suite 70 -> 83.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 13:51:49 +00:00
parent 5a1412515a
commit d2c1400737
5 changed files with 118 additions and 6 deletions

View File

@@ -422,6 +422,68 @@
(fn (vs)
(er-ext-unzip (er-bif-arg1 vs "lists:unzip") (er-mk-nil) (er-mk-nil))))
;; ── slicing (sublist / nthtail / split / droplast) ────────────────
(define
er-ext-sublist2
(fn (lst n)
(cond
(or (<= n 0) (er-nil? lst)) (er-mk-nil)
(er-cons? lst)
(er-mk-cons (get lst :head) (er-ext-sublist2 (get lst :tail) (- n 1)))
:else (er-mk-nil))))
;; lenient drop (used by sublist/3); never raises
(define
er-ext-drop-cons
(fn (lst n)
(cond
(or (<= n 0) (er-nil? lst)) lst
(er-cons? lst) (er-ext-drop-cons (get lst :tail) (- n 1))
:else lst)))
;; strict drop (used by nthtail/2 + split/2); raises if list too short
(define
er-ext-nthtail
(fn (n lst)
(cond
(<= n 0) lst
(er-cons? lst) (er-ext-nthtail (- n 1) (get lst :tail))
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
(define
er-bif-lists-sublist
(fn (vs)
(cond
(= (len vs) 2) (er-ext-sublist2 (nth vs 0) (nth vs 1))
(= (len vs) 3)
(er-ext-sublist2
(er-ext-drop-cons (nth vs 0) (- (nth vs 1) 1))
(nth vs 2))
:else (error "Erlang: lists:sublist: wrong arity"))))
(define
er-bif-lists-nthtail
(fn (vs) (er-ext-nthtail (nth vs 0) (nth vs 1))))
(define
er-bif-lists-split
(fn (vs)
(let ((n (nth vs 0)) (lst (nth vs 1)))
(er-mk-tuple
(list (er-ext-sublist2 lst n) (er-ext-nthtail n lst))))))
(define
er-ext-droplast
(fn (lst)
(cond
(and (er-cons? lst) (er-nil? (get lst :tail))) (er-mk-nil)
(er-cons? lst) (er-mk-cons (get lst :head) (er-ext-droplast (get lst :tail)))
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
(define
er-bif-lists-droplast
(fn (vs) (er-ext-droplast (er-bif-arg1 vs "lists:droplast"))))
;; ── register ──────────────────────────────────────────────────────
;; Hook into er-register-builtin-bifs! rather than registering once:
;; the registry can be reset + rebuilt mid-run (tests/runtime.sx does
@@ -449,7 +511,12 @@
(er-register-pure-bif! "lists" "min" 1 er-bif-lists-min)
(er-register-pure-bif! "lists" "zip" 2 er-bif-lists-zip)
(er-register-pure-bif! "lists" "zipwith" 3 er-bif-lists-zipwith)
(er-register-pure-bif! "lists" "unzip" 1 er-bif-lists-unzip)))
(er-register-pure-bif! "lists" "unzip" 1 er-bif-lists-unzip)
(er-register-pure-bif! "lists" "sublist" 2 er-bif-lists-sublist)
(er-register-pure-bif! "lists" "sublist" 3 er-bif-lists-sublist)
(er-register-pure-bif! "lists" "nthtail" 2 er-bif-lists-nthtail)
(er-register-pure-bif! "lists" "split" 2 er-bif-lists-split)
(er-register-pure-bif! "lists" "droplast" 1 er-bif-lists-droplast)))
(define er-ext-prev-register-builtins er-register-builtin-bifs!)
(define er-register-builtin-bifs!

View File

@@ -1,7 +1,7 @@
{
"language": "erlang",
"total_pass": 841,
"total": 841,
"total_pass": 854,
"total": 854,
"suites": [
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
{"name":"parse","pass":52,"total":52,"status":"ok"},
@@ -15,6 +15,6 @@
{"name":"ffi","pass":37,"total":37,"status":"ok"},
{"name":"vm","pass":78,"total":78,"status":"ok"},
{"name":"send_after","pass":10,"total":10,"status":"ok"},
{"name":"lists_ext","pass":70,"total":70,"status":"ok"}
{"name":"lists_ext","pass":83,"total":83,"status":"ok"}
]
}

View File

@@ -1,6 +1,6 @@
# Erlang-on-SX Scoreboard
**Total: 841 / 841 tests passing**
**Total: 854 / 854 tests passing**
| | Suite | Pass | Total |
|---|---|---|---|
@@ -16,7 +16,7 @@
| ✅ | ffi | 37 | 37 |
| ✅ | vm | 78 | 78 |
| ✅ | send_after | 10 | 10 |
| ✅ | lists_ext | 70 | 70 |
| ✅ | lists_ext | 83 | 83 |
Generated by `lib/erlang/conformance.sh`.

View File

@@ -267,3 +267,46 @@
(er-lx-test "zip/unzip roundtrip"
(er-lx-nm "lists:unzip(lists:zip([1,2],[3,4])) =:= {[1,2],[3,4]}") "true")
;; ── lists:sublist/2,3 ─────────────────────────────────────────────
(er-lx-test "sublist/2 first n"
(er-lx-nm "lists:sublist([1,2,3,4,5],3) =:= [1,2,3]") "true")
(er-lx-test "sublist/2 over length"
(er-lx-nm "lists:sublist([1,2],5) =:= [1,2]") "true")
(er-lx-test "sublist/2 zero"
(er-lx-nm "lists:sublist([1,2,3],0) =:= []") "true")
(er-lx-test "sublist/3 mid"
(er-lx-nm "lists:sublist([1,2,3,4,5],2,3) =:= [2,3,4]") "true")
(er-lx-test "sublist/3 to end"
(er-lx-nm "lists:sublist([1,2,3],2,10) =:= [2,3]") "true")
;; ── lists:nthtail/2 ───────────────────────────────────────────────
(er-lx-test "nthtail mid"
(er-lx-nm "lists:nthtail(2,[1,2,3,4]) =:= [3,4]") "true")
(er-lx-test "nthtail zero"
(er-lx-nm "lists:nthtail(0,[1,2]) =:= [1,2]") "true")
(er-lx-test "nthtail full"
(er-lx-nm "lists:nthtail(3,[1,2,3]) =:= []") "true")
;; ── lists:split/2 ─────────────────────────────────────────────────
(er-lx-test "split mid"
(er-lx-nm "lists:split(2,[1,2,3,4,5]) =:= {[1,2],[3,4,5]}") "true")
(er-lx-test "split zero"
(er-lx-nm "lists:split(0,[1,2]) =:= {[],[1,2]}") "true")
(er-lx-test "split full"
(er-lx-nm "lists:split(3,[1,2,3]) =:= {[1,2,3],[]}") "true")
;; ── lists:droplast/1 ──────────────────────────────────────────────
(er-lx-test "droplast"
(er-lx-nm "lists:droplast([1,2,3]) =:= [1,2]") "true")
(er-lx-test "droplast single"
(er-lx-nm "lists:droplast([9]) =:= []") "true")

View File

@@ -159,6 +159,8 @@ The Phase 9 opcodes are registered, tested, and bridged SX↔OCaml, but inert: n
_Newest first._
- **2026-06-30 stdlib hardening — `lists` slicing** — Added `sublist/2`, `sublist/3`, `nthtail/2`, `split/2`, `droplast/1` to `lib/erlang/lists-ext.sx`. `sublist` is lenient (clamps to list length); `nthtail/2` and `split/2` are strict (`badarg` when the list is shorter than N, matching the stdlib); `droplast/1` raises on `[]`. `lists_ext` suite 70→**83** (+13). Conformance **841 → 854/854**. loops/erlang only.
- **2026-06-30 stdlib hardening — `lists` zip family** — Added `zip/2`, `zipwith/3`, `unzip/1` to `lib/erlang/lists-ext.sx`. Length mismatch (zip/zipwith) and malformed/non-pair elements (unzip) raise `badarg` (port equivalent of Erlang's `function_clause`). `lists_ext` suite 62→**70** (+8, incl. a zip/unzip roundtrip). Conformance **833 → 841/841**. loops/erlang only.
- **2026-06-30 stdlib hardening — `lists` flatten/max/min** — Added `flatten/1` (deep recursive flatten via `er-list-append`), `max/1`, `min/1` (full term order via `er-ext-lt?`, `badarg` on empty) to `lib/erlang/lists-ext.sx`. Gotcha caught: `er-ext-lt?` returns a raw SX boolean, so the extreme-finder uses it directly in `if` rather than wrapping in `er-truthy?` (which only recognises Erlang bool atoms, not SX booleans — the first cut wrapped it and silently never updated the running best). `lists_ext` suite 52→**62** (+10). Conformance **823 → 833/833**. loops/erlang only.