erlang: lists foldr/3 + partition/2 + takewhile/dropwhile/splitwith (823/823)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m5s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m5s
Higher-order traversal family in lib/erlang/lists-ext.sx, registered
pure via the er-register-builtin-bifs! wrapper. foldr right-folds;
partition returns {Yes,No} order-preserved; splitwith = {takewhile,
dropwhile}. lists_ext suite 38 -> 52.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,6 +249,76 @@
|
|||||||
(er-bool
|
(er-bool
|
||||||
(not (er-ext-lt? (er-ext-tup-elem b n) (er-ext-tup-elem a n))))))))))
|
(not (er-ext-lt? (er-ext-tup-elem b n) (er-ext-tup-elem a n))))))))))
|
||||||
|
|
||||||
|
;; ── higher-order traversal (foldr / partition / *while) ───────────
|
||||||
|
(define
|
||||||
|
er-ext-foldr
|
||||||
|
(fn (f acc lst)
|
||||||
|
(cond
|
||||||
|
(er-nil? lst) acc
|
||||||
|
(er-cons? lst)
|
||||||
|
(er-apply-fun f (list (get lst :head) (er-ext-foldr f acc (get lst :tail))))
|
||||||
|
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-bif-lists-foldr
|
||||||
|
(fn (vs) (er-ext-foldr (nth vs 0) (nth vs 1) (nth vs 2))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-ext-partition
|
||||||
|
(fn (pred lst yes no)
|
||||||
|
(cond
|
||||||
|
(er-nil? lst)
|
||||||
|
(er-mk-tuple
|
||||||
|
(list
|
||||||
|
(er-list-reverse-iter yes (er-mk-nil))
|
||||||
|
(er-list-reverse-iter no (er-mk-nil))))
|
||||||
|
(er-cons? lst)
|
||||||
|
(if (er-truthy? (er-apply-fun pred (list (get lst :head))))
|
||||||
|
(er-ext-partition pred (get lst :tail) (er-mk-cons (get lst :head) yes) no)
|
||||||
|
(er-ext-partition pred (get lst :tail) yes (er-mk-cons (get lst :head) no)))
|
||||||
|
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-bif-lists-partition
|
||||||
|
(fn (vs) (er-ext-partition (nth vs 0) (nth vs 1) (er-mk-nil) (er-mk-nil))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-ext-takewhile
|
||||||
|
(fn (pred lst)
|
||||||
|
(cond
|
||||||
|
(er-nil? lst) (er-mk-nil)
|
||||||
|
(er-cons? lst)
|
||||||
|
(if (er-truthy? (er-apply-fun pred (list (get lst :head))))
|
||||||
|
(er-mk-cons (get lst :head) (er-ext-takewhile pred (get lst :tail)))
|
||||||
|
(er-mk-nil))
|
||||||
|
:else (er-mk-nil))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-bif-lists-takewhile
|
||||||
|
(fn (vs) (er-ext-takewhile (nth vs 0) (nth vs 1))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-ext-dropwhile
|
||||||
|
(fn (pred lst)
|
||||||
|
(cond
|
||||||
|
(er-nil? lst) (er-mk-nil)
|
||||||
|
(er-cons? lst)
|
||||||
|
(if (er-truthy? (er-apply-fun pred (list (get lst :head))))
|
||||||
|
(er-ext-dropwhile pred (get lst :tail))
|
||||||
|
lst)
|
||||||
|
:else lst)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-bif-lists-dropwhile
|
||||||
|
(fn (vs) (er-ext-dropwhile (nth vs 0) (nth vs 1))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
er-bif-lists-splitwith
|
||||||
|
(fn (vs)
|
||||||
|
(let ((pred (nth vs 0)) (lst (nth vs 1)))
|
||||||
|
(er-mk-tuple
|
||||||
|
(list (er-ext-takewhile pred lst) (er-ext-dropwhile pred lst))))))
|
||||||
|
|
||||||
;; ── register ──────────────────────────────────────────────────────
|
;; ── register ──────────────────────────────────────────────────────
|
||||||
;; Hook into er-register-builtin-bifs! rather than registering once:
|
;; Hook into er-register-builtin-bifs! rather than registering once:
|
||||||
;; the registry can be reset + rebuilt mid-run (tests/runtime.sx does
|
;; the registry can be reset + rebuilt mid-run (tests/runtime.sx does
|
||||||
@@ -265,7 +335,12 @@
|
|||||||
(er-register-pure-bif! "lists" "keyreplace" 4 er-bif-lists-keyreplace)
|
(er-register-pure-bif! "lists" "keyreplace" 4 er-bif-lists-keyreplace)
|
||||||
(er-register-pure-bif! "lists" "keystore" 4 er-bif-lists-keystore)
|
(er-register-pure-bif! "lists" "keystore" 4 er-bif-lists-keystore)
|
||||||
(er-register-pure-bif! "lists" "keytake" 3 er-bif-lists-keytake)
|
(er-register-pure-bif! "lists" "keytake" 3 er-bif-lists-keytake)
|
||||||
(er-register-pure-bif! "lists" "keysort" 2 er-bif-lists-keysort)))
|
(er-register-pure-bif! "lists" "keysort" 2 er-bif-lists-keysort)
|
||||||
|
(er-register-pure-bif! "lists" "foldr" 3 er-bif-lists-foldr)
|
||||||
|
(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)))
|
||||||
|
|
||||||
(define er-ext-prev-register-builtins er-register-builtin-bifs!)
|
(define er-ext-prev-register-builtins er-register-builtin-bifs!)
|
||||||
(define er-register-builtin-bifs!
|
(define er-register-builtin-bifs!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"language": "erlang",
|
"language": "erlang",
|
||||||
"total_pass": 809,
|
"total_pass": 823,
|
||||||
"total": 809,
|
"total": 823,
|
||||||
"suites": [
|
"suites": [
|
||||||
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
|
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
|
||||||
{"name":"parse","pass":52,"total":52,"status":"ok"},
|
{"name":"parse","pass":52,"total":52,"status":"ok"},
|
||||||
@@ -15,6 +15,6 @@
|
|||||||
{"name":"ffi","pass":37,"total":37,"status":"ok"},
|
{"name":"ffi","pass":37,"total":37,"status":"ok"},
|
||||||
{"name":"vm","pass":78,"total":78,"status":"ok"},
|
{"name":"vm","pass":78,"total":78,"status":"ok"},
|
||||||
{"name":"send_after","pass":10,"total":10,"status":"ok"},
|
{"name":"send_after","pass":10,"total":10,"status":"ok"},
|
||||||
{"name":"lists_ext","pass":38,"total":38,"status":"ok"}
|
{"name":"lists_ext","pass":52,"total":52,"status":"ok"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Erlang-on-SX Scoreboard
|
# Erlang-on-SX Scoreboard
|
||||||
|
|
||||||
**Total: 809 / 809 tests passing**
|
**Total: 823 / 823 tests passing**
|
||||||
|
|
||||||
| | Suite | Pass | Total |
|
| | Suite | Pass | Total |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
| ✅ | ffi | 37 | 37 |
|
| ✅ | ffi | 37 | 37 |
|
||||||
| ✅ | vm | 78 | 78 |
|
| ✅ | vm | 78 | 78 |
|
||||||
| ✅ | send_after | 10 | 10 |
|
| ✅ | send_after | 10 | 10 |
|
||||||
| ✅ | lists_ext | 38 | 38 |
|
| ✅ | lists_ext | 52 | 52 |
|
||||||
|
|
||||||
|
|
||||||
Generated by `lib/erlang/conformance.sh`.
|
Generated by `lib/erlang/conformance.sh`.
|
||||||
|
|||||||
@@ -154,3 +154,54 @@
|
|||||||
(er-lx-test "keysort stable on equal keys"
|
(er-lx-test "keysort stable on equal keys"
|
||||||
(er-lx-nm
|
(er-lx-nm
|
||||||
"lists:keysort(1, [{a,1},{a,2},{a,3}]) =:= [{a,1},{a,2},{a,3}]") "true")
|
"lists:keysort(1, [{a,1},{a,2},{a,3}]) =:= [{a,1},{a,2},{a,3}]") "true")
|
||||||
|
|
||||||
|
;; ── lists:foldr/3 ─────────────────────────────────────────────────
|
||||||
|
(er-lx-test "foldr preserves order"
|
||||||
|
(er-lx-nm
|
||||||
|
"lists:foldr(fun(X,Acc) -> [X|Acc] end, [], [1,2,3]) =:= [1,2,3]") "true")
|
||||||
|
|
||||||
|
(er-lx-test "foldr sum"
|
||||||
|
(erlang-eval-ast "lists:foldr(fun(X,A) -> X+A end, 0, [1,2,3,4])") 10)
|
||||||
|
|
||||||
|
(er-lx-test "foldr empty returns acc"
|
||||||
|
(erlang-eval-ast "lists:foldr(fun(X,A) -> X+A end, 42, [])") 42)
|
||||||
|
|
||||||
|
;; ── lists:partition/2 ─────────────────────────────────────────────
|
||||||
|
(er-lx-test "partition evens/odds"
|
||||||
|
(er-lx-nm
|
||||||
|
"lists:partition(fun(X) -> X rem 2 =:= 0 end, [1,2,3,4,5]) =:= {[2,4],[1,3,5]}")
|
||||||
|
"true")
|
||||||
|
|
||||||
|
(er-lx-test "partition all satisfy"
|
||||||
|
(er-lx-nm "lists:partition(fun(_) -> true end, [1,2]) =:= {[1,2],[]}") "true")
|
||||||
|
|
||||||
|
(er-lx-test "partition empty"
|
||||||
|
(er-lx-nm "lists:partition(fun(_) -> true end, []) =:= {[],[]}") "true")
|
||||||
|
|
||||||
|
;; ── lists:takewhile/2 ─────────────────────────────────────────────
|
||||||
|
(er-lx-test "takewhile prefix"
|
||||||
|
(er-lx-nm "lists:takewhile(fun(X) -> X < 3 end, [1,2,3,4,1]) =:= [1,2]") "true")
|
||||||
|
|
||||||
|
(er-lx-test "takewhile none"
|
||||||
|
(er-lx-nm "lists:takewhile(fun(X) -> X < 0 end, [1,2]) =:= []") "true")
|
||||||
|
|
||||||
|
(er-lx-test "takewhile all"
|
||||||
|
(er-lx-nm "lists:takewhile(fun(X) -> X < 9 end, [1,2,3]) =:= [1,2,3]") "true")
|
||||||
|
|
||||||
|
;; ── lists:dropwhile/2 ─────────────────────────────────────────────
|
||||||
|
(er-lx-test "dropwhile prefix"
|
||||||
|
(er-lx-nm "lists:dropwhile(fun(X) -> X < 3 end, [1,2,3,4,1]) =:= [3,4,1]") "true")
|
||||||
|
|
||||||
|
(er-lx-test "dropwhile all"
|
||||||
|
(er-lx-nm "lists:dropwhile(fun(X) -> X < 9 end, [1,2,3]) =:= []") "true")
|
||||||
|
|
||||||
|
(er-lx-test "dropwhile none"
|
||||||
|
(er-lx-nm "lists:dropwhile(fun(X) -> X < 0 end, [1,2]) =:= [1,2]") "true")
|
||||||
|
|
||||||
|
;; ── lists:splitwith/2 ─────────────────────────────────────────────
|
||||||
|
(er-lx-test "splitwith"
|
||||||
|
(er-lx-nm
|
||||||
|
"lists:splitwith(fun(X) -> X < 3 end, [1,2,3,4,1]) =:= {[1,2],[3,4,1]}") "true")
|
||||||
|
|
||||||
|
(er-lx-test "splitwith empty"
|
||||||
|
(er-lx-nm "lists:splitwith(fun(_) -> true end, []) =:= {[],[]}") "true")
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ The Phase 9 opcodes are registered, tested, and bridged SX↔OCaml, but inert: n
|
|||||||
|
|
||||||
_Newest first._
|
_Newest first._
|
||||||
|
|
||||||
|
- **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.
|
- **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.
|
||||||
|
|
||||||
- **2026-06-30 stdlib hardening — `lists:sort/1,2` + `lists:usort/1`** — Roadmap is saturated within this loop's scope (every remaining `[ ]` is blocked: `httpc`/`sqlite` on absent host primitives, 10a/10c on out-of-scope `lib/compiler.sx`). Continued as forever-loop hardening by filling idiomatic-Erlang stdlib gaps. Added the `lists` sort family in a **new file `lib/erlang/lists-ext.sx`** (loaded after `runtime.sx`): stable merge sort over an SX-list bridge, registered via `er-register-pure-bif!`. `lists:sort/1` and `usort/1` use full Erlang term order; `sort/2` takes a `fun(A,B)->bool` comparator. **Two notable findings:** (1) the shared `er-lt?` (transpile.sx) only deep-compares numbers/atoms/strings and treats *any two tuples (or lists) as order-equal* — so `lists:sort` (and, latently, `min/2`/`max/2`) would not order compound terms. Fixed locally with a self-contained `er-ext-lt?` that compares tuples by arity-then-elementwise and lists elementwise (shorter proper prefix first), delegating cross-type cases to `er-lt?`. `er-lt?` itself left untouched (shared by the `<` operator; can't edit transpile.sx — see Blockers). (2) `tests/runtime.sx` resets the BIF registry mid-run via `er-register-builtin-bifs!`, which would wipe a one-shot registration; so `lists-ext.sx` **wraps** `er-register-builtin-bifs!` to re-add its BIFs on every rebuild. New `lists_ext` suite (17 tests: term order, dup-keeping, stability, descending comparator, usort dedup). Conformance **771 → 788/788** (12→13 suites). New-file workaround forced because every sx-tree write tool (incl. `sx_write_file`) raises yojson "Expected string, got null" in this worktree — authored via the `Write` fallback + `sx_validate`, the same pattern other loops use. loops/erlang only.
|
- **2026-06-30 stdlib hardening — `lists:sort/1,2` + `lists:usort/1`** — Roadmap is saturated within this loop's scope (every remaining `[ ]` is blocked: `httpc`/`sqlite` on absent host primitives, 10a/10c on out-of-scope `lib/compiler.sx`). Continued as forever-loop hardening by filling idiomatic-Erlang stdlib gaps. Added the `lists` sort family in a **new file `lib/erlang/lists-ext.sx`** (loaded after `runtime.sx`): stable merge sort over an SX-list bridge, registered via `er-register-pure-bif!`. `lists:sort/1` and `usort/1` use full Erlang term order; `sort/2` takes a `fun(A,B)->bool` comparator. **Two notable findings:** (1) the shared `er-lt?` (transpile.sx) only deep-compares numbers/atoms/strings and treats *any two tuples (or lists) as order-equal* — so `lists:sort` (and, latently, `min/2`/`max/2`) would not order compound terms. Fixed locally with a self-contained `er-ext-lt?` that compares tuples by arity-then-elementwise and lists elementwise (shorter proper prefix first), delegating cross-type cases to `er-lt?`. `er-lt?` itself left untouched (shared by the `<` operator; can't edit transpile.sx — see Blockers). (2) `tests/runtime.sx` resets the BIF registry mid-run via `er-register-builtin-bifs!`, which would wipe a one-shot registration; so `lists-ext.sx` **wraps** `er-register-builtin-bifs!` to re-add its BIFs on every rebuild. New `lists_ext` suite (17 tests: term order, dup-keeping, stability, descending comparator, usort dedup). Conformance **771 → 788/788** (12→13 suites). New-file workaround forced because every sx-tree write tool (incl. `sx_write_file`) raises yojson "Expected string, got null" in this worktree — authored via the `Write` fallback + `sx_validate`, the same pattern other loops use. loops/erlang only.
|
||||||
|
|||||||
Reference in New Issue
Block a user