erlang: lists flatten/1 + max/1 + min/1 (833/833)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 58s

Structural/aggregate ops in lib/erlang/lists-ext.sx: flatten/1 deep
flatten, max/1 and min/1 by full Erlang term order (badarg on empty).
Extreme-finder uses er-ext-lt?'s SX boolean directly in if (er-truthy?
only recognises Erlang bool atoms). lists_ext suite 52 -> 62.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 13:41:15 +00:00
parent 42a16f7cf3
commit 3ae35a4b9b
5 changed files with 91 additions and 6 deletions

View File

@@ -319,6 +319,53 @@
(er-mk-tuple
(list (er-ext-takewhile pred lst) (er-ext-dropwhile pred lst))))))
;; ── structural / aggregate (flatten / max / min) ──────────────────
(define
er-ext-flatten
(fn (lst)
(cond
(er-nil? lst) (er-mk-nil)
(er-cons? lst)
(let ((h (get lst :head)))
(if (or (er-nil? h) (er-cons? h))
(er-list-append (er-ext-flatten h) (er-ext-flatten (get lst :tail)))
(er-mk-cons h (er-ext-flatten (get lst :tail)))))
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
(define
er-bif-lists-flatten
(fn (vs) (er-ext-flatten (er-bif-arg1 vs "lists:flatten"))))
(define
er-ext-extreme
(fn (lst best lt?)
(cond
(er-nil? lst) best
(er-cons? lst)
(er-ext-extreme
(get lst :tail)
(if (lt? best (get lst :head)) (get lst :head) best)
lt?)
:else best)))
(define
er-bif-lists-max
(fn (vs)
(let ((lst (er-bif-arg1 vs "lists:max")))
(if (er-cons? lst)
(er-ext-extreme (get lst :tail) (get lst :head)
(fn (a b) (er-ext-lt? a b)))
(raise (er-mk-error-marker (er-mk-atom "badarg")))))))
(define
er-bif-lists-min
(fn (vs)
(let ((lst (er-bif-arg1 vs "lists:min")))
(if (er-cons? lst)
(er-ext-extreme (get lst :tail) (get lst :head)
(fn (a b) (er-ext-lt? b a)))
(raise (er-mk-error-marker (er-mk-atom "badarg")))))))
;; ── register ──────────────────────────────────────────────────────
;; Hook into er-register-builtin-bifs! rather than registering once:
;; the registry can be reset + rebuilt mid-run (tests/runtime.sx does
@@ -340,7 +387,10 @@
(er-register-pure-bif! "lists" "partition" 2 er-bif-lists-partition)
(er-register-pure-bif! "lists" "takewhile" 2 er-bif-lists-takewhile)
(er-register-pure-bif! "lists" "dropwhile" 2 er-bif-lists-dropwhile)
(er-register-pure-bif! "lists" "splitwith" 2 er-bif-lists-splitwith)))
(er-register-pure-bif! "lists" "splitwith" 2 er-bif-lists-splitwith)
(er-register-pure-bif! "lists" "flatten" 1 er-bif-lists-flatten)
(er-register-pure-bif! "lists" "max" 1 er-bif-lists-max)
(er-register-pure-bif! "lists" "min" 1 er-bif-lists-min)))
(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": 823,
"total": 823,
"total_pass": 833,
"total": 833,
"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":52,"total":52,"status":"ok"}
{"name":"lists_ext","pass":62,"total":62,"status":"ok"}
]
}

View File

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

View File

@@ -205,3 +205,36 @@
(er-lx-test "splitwith empty"
(er-lx-nm "lists:splitwith(fun(_) -> true end, []) =:= {[],[]}") "true")
;; ── lists:flatten/1 ───────────────────────────────────────────────
(er-lx-test "flatten nested"
(er-lx-nm "lists:flatten([1,[2,[3,4]],5]) =:= [1,2,3,4,5]") "true")
(er-lx-test "flatten already flat"
(er-lx-nm "lists:flatten([1,2,3]) =:= [1,2,3]") "true")
(er-lx-test "flatten empty"
(er-lx-nm "lists:flatten([]) =:= []") "true")
(er-lx-test "flatten deep empties"
(er-lx-nm "lists:flatten([[],[1],[[]]]) =:= [1]") "true")
(er-lx-test "flatten length"
(erlang-eval-ast "length(lists:flatten([[1,2],[3],[4,5,6]]))") 6)
;; ── lists:max/1 ───────────────────────────────────────────────────
(er-lx-test "max ints"
(erlang-eval-ast "lists:max([3,1,4,1,5,9,2,6])") 9)
(er-lx-test "max single"
(erlang-eval-ast "lists:max([7])") 7)
(er-lx-test "max atoms term order"
(er-lx-nm "lists:max([a,c,b]) =:= c") "true")
;; ── lists:min/1 ───────────────────────────────────────────────────
(er-lx-test "min ints"
(erlang-eval-ast "lists:min([3,1,4,1,5])") 1)
(er-lx-test "min mixed term order"
(er-lx-nm "lists:min([a,1,b]) =:= 1") "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` 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.
- **2026-06-30 stdlib hardening — `lists` higher-order traversal** — Added `foldr/3`, `partition/2`, `takewhile/2`, `dropwhile/2`, `splitwith/2` to `lib/erlang/lists-ext.sx`, registered pure through the `er-register-builtin-bifs!` wrapper (consistent with the existing pure `map`/`filter`/`foldl`). `foldr` right-folds (order-preserving when consing); `partition` returns `{Satisfying, NotSatisfying}` order-preserved via `er-list-reverse-iter`; `splitwith` = `{takewhile, dropwhile}`. `lists_ext` suite 38→**52** (+14). Conformance **809 → 823/823**. loops/erlang only.
- **2026-06-30 stdlib hardening — `lists` keylists** — Added the keylist family to `lib/erlang/lists-ext.sx`: `keyfind/3`, `keymember/3`, `keydelete/3`, `keyreplace/4`, `keystore/4`, `keytake/3`, `keysort/2`. All operate on lists of tuples keyed on element N (1-indexed), act on the first match only, and pass through non-tuples / tuples shorter than N. Key comparison is `==` (`er-equal?`) per the stdlib; `keysort/2` reuses the stable `er-ext-msort` + `er-ext-lt?` from the sort commit, comparing extracted keys. `keytake/3` returns `{value, Tuple, Rest}` / `false`. Registered through the same `er-register-builtin-bifs!` wrapper so they survive registry resets. `lists_ext` suite 17→**38** (+21: hit/miss/first-match-only/short-tuple-skip across all seven, keysort by elem 1 and 2 + stability). Conformance **788 → 809/809**. Test-harness note: `element(2, T)` returns an integer (no `:name`), so those two cases compare the raw number via `erlang-eval-ast` rather than `er-lx-nm`. loops/erlang only.