Compare commits
10 Commits
loops/blog
...
loops/erla
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d20f41498 | |||
| 27dedf9b0a | |||
| 3d8607a40a | |||
| 394d5790ad | |||
| d2c1400737 | |||
| 5a1412515a | |||
| 3ae35a4b9b | |||
| 42a16f7cf3 | |||
| 343c508939 | |||
| 355a482dfe |
@@ -39,6 +39,7 @@ SUITES=(
|
||||
"ffi|er-ffi-test-pass|er-ffi-test-count"
|
||||
"vm|er-vm-test-pass|er-vm-test-count"
|
||||
"send_after|er-sa-test-pass|er-sa-test-count"
|
||||
"lists_ext|er-lx-test-pass|er-lx-test-count"
|
||||
)
|
||||
|
||||
cat > "$TMPFILE" << 'EPOCHS'
|
||||
@@ -63,6 +64,7 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(load "lib/erlang/tests/ffi.sx")
|
||||
(load "lib/erlang/tests/vm.sx")
|
||||
(load "lib/erlang/tests/send_after.sx")
|
||||
(load "lib/erlang/tests/lists_ext.sx")
|
||||
(epoch 100)
|
||||
(eval "(list er-test-pass er-test-count)")
|
||||
(epoch 101)
|
||||
@@ -87,6 +89,8 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(eval "(list er-vm-test-pass er-vm-test-count)")
|
||||
(epoch 111)
|
||||
(eval "(list er-sa-test-pass er-sa-test-count)")
|
||||
(epoch 112)
|
||||
(eval "(list er-lx-test-pass er-lx-test-count)")
|
||||
EPOCHS
|
||||
|
||||
timeout 600 "$SX_SERVER" < "$TMPFILE" > "$OUTFILE" 2>&1
|
||||
|
||||
@@ -1753,6 +1753,42 @@
|
||||
(er-register-pure-bif! "lists" "any" 2 er-bif-lists-any)
|
||||
(er-register-pure-bif! "lists" "all" 2 er-bif-lists-all)
|
||||
(er-register-pure-bif! "lists" "duplicate" 2 er-bif-lists-duplicate)
|
||||
(er-register-pure-bif! "lists" "sort" 1 er-bif-lists-sort)
|
||||
(er-register-pure-bif! "lists" "sort" 2 er-bif-lists-sort)
|
||||
(er-register-pure-bif! "lists" "usort" 1 er-bif-lists-usort)
|
||||
(er-register-pure-bif! "lists" "keyfind" 3 er-bif-lists-keyfind)
|
||||
(er-register-pure-bif! "lists" "keymember" 3 er-bif-lists-keymember)
|
||||
(er-register-pure-bif! "lists" "keydelete" 3 er-bif-lists-keydelete)
|
||||
(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" "keytake" 3 er-bif-lists-keytake)
|
||||
(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)
|
||||
(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)
|
||||
(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" "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)
|
||||
(er-register-pure-bif! "lists" "flatmap" 2 er-bif-lists-flatmap)
|
||||
(er-register-pure-bif! "lists" "filtermap" 2 er-bif-lists-filtermap)
|
||||
(er-register-pure-bif! "lists" "mapfoldl" 3 er-bif-lists-mapfoldl)
|
||||
(er-register-pure-bif! "lists" "search" 2 er-bif-lists-search)
|
||||
(er-register-pure-bif! "proplists" "get_value" 2 er-bif-pl-get-value)
|
||||
(er-register-pure-bif! "proplists" "get_value" 3 er-bif-pl-get-value)
|
||||
(er-register-pure-bif! "proplists" "get_all_values" 2 er-bif-pl-get-all-values)
|
||||
(er-register-pure-bif! "proplists" "is_defined" 2 er-bif-pl-is-defined)
|
||||
(er-register-pure-bif! "proplists" "lookup" 2 er-bif-pl-lookup)
|
||||
(er-register-pure-bif! "proplists" "delete" 2 er-bif-pl-delete)
|
||||
;; io module — side-effecting (writes to io buffer)
|
||||
(er-register-bif! "io" "format" 1 er-bif-io-format)
|
||||
(er-register-bif! "io" "format" 2 er-bif-io-format)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"language": "erlang",
|
||||
"total_pass": 771,
|
||||
"total": 771,
|
||||
"total_pass": 874,
|
||||
"total": 874,
|
||||
"suites": [
|
||||
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
|
||||
{"name":"parse","pass":52,"total":52,"status":"ok"},
|
||||
@@ -14,6 +14,7 @@
|
||||
{"name":"fib","pass":8,"total":8,"status":"ok"},
|
||||
{"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":"send_after","pass":10,"total":10,"status":"ok"},
|
||||
{"name":"lists_ext","pass":103,"total":103,"status":"ok"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Erlang-on-SX Scoreboard
|
||||
|
||||
**Total: 771 / 771 tests passing**
|
||||
**Total: 874 / 874 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
@@ -16,6 +16,7 @@
|
||||
| ✅ | ffi | 37 | 37 |
|
||||
| ✅ | vm | 78 | 78 |
|
||||
| ✅ | send_after | 10 | 10 |
|
||||
| ✅ | lists_ext | 103 | 103 |
|
||||
|
||||
|
||||
Generated by `lib/erlang/conformance.sh`.
|
||||
|
||||
385
lib/erlang/tests/lists_ext.sx
Normal file
385
lib/erlang/tests/lists_ext.sx
Normal file
@@ -0,0 +1,385 @@
|
||||
;; lists-ext tests — lists:sort/1, lists:sort/2, lists:usort/1.
|
||||
;; Each case evaluates an Erlang expression that reduces to the bool
|
||||
;; atom `true` (via =:= on the sorted result) and checks its name.
|
||||
|
||||
(define er-lx-test-count 0)
|
||||
(define er-lx-test-pass 0)
|
||||
(define er-lx-test-fails (list))
|
||||
|
||||
(define
|
||||
er-lx-test
|
||||
(fn
|
||||
(name actual expected)
|
||||
(set! er-lx-test-count (+ er-lx-test-count 1))
|
||||
(if
|
||||
(= actual expected)
|
||||
(set! er-lx-test-pass (+ er-lx-test-pass 1))
|
||||
(append! er-lx-test-fails {:name name :expected expected :actual actual}))))
|
||||
|
||||
;; eval an Erlang source string and return the result atom's name
|
||||
(define er-lx-nm (fn (src) (get (erlang-eval-ast src) :name)))
|
||||
|
||||
;; ── lists:sort/1 ──────────────────────────────────────────────────
|
||||
(er-lx-test "sort/1 ascending"
|
||||
(er-lx-nm "lists:sort([3,1,2]) =:= [1,2,3]") "true")
|
||||
|
||||
(er-lx-test "sort/1 already sorted"
|
||||
(er-lx-nm "lists:sort([1,2,3]) =:= [1,2,3]") "true")
|
||||
|
||||
(er-lx-test "sort/1 empty"
|
||||
(er-lx-nm "lists:sort([]) =:= []") "true")
|
||||
|
||||
(er-lx-test "sort/1 singleton"
|
||||
(er-lx-nm "lists:sort([7]) =:= [7]") "true")
|
||||
|
||||
(er-lx-test "sort/1 keeps duplicates"
|
||||
(er-lx-nm "lists:sort([3,1,2,1]) =:= [1,1,2,3]") "true")
|
||||
|
||||
(er-lx-test "sort/1 length preserved"
|
||||
(erlang-eval-ast "length(lists:sort([5,4,3,2,1]))") 5)
|
||||
|
||||
(er-lx-test "sort/1 term order: number < atom"
|
||||
(er-lx-nm "lists:sort([b,a,1]) =:= [1,a,b]") "true")
|
||||
|
||||
(er-lx-test "sort/1 tuples elementwise"
|
||||
(er-lx-nm "lists:sort([{2,a},{1,b},{1,a}]) =:= [{1,a},{1,b},{2,a}]") "true")
|
||||
|
||||
;; ── lists:sort/2 ──────────────────────────────────────────────────
|
||||
(er-lx-test "sort/2 ascending =<"
|
||||
(er-lx-nm "lists:sort(fun(A,B) -> A =< B end, [3,1,2]) =:= [1,2,3]") "true")
|
||||
|
||||
(er-lx-test "sort/2 descending >="
|
||||
(er-lx-nm "lists:sort(fun(A,B) -> A >= B end, [1,3,2]) =:= [3,2,1]") "true")
|
||||
|
||||
(er-lx-test "sort/2 stable on equal keys"
|
||||
(er-lx-nm
|
||||
"lists:sort(fun({A,_},{B,_}) -> A =< B end, [{1,x},{1,y},{0,z}]) =:= [{0,z},{1,x},{1,y}]")
|
||||
"true")
|
||||
|
||||
(er-lx-test "sort/2 empty"
|
||||
(er-lx-nm "lists:sort(fun(A,B) -> A =< B end, []) =:= []") "true")
|
||||
|
||||
;; ── lists:usort/1 ─────────────────────────────────────────────────
|
||||
(er-lx-test "usort/1 removes duplicates"
|
||||
(er-lx-nm "lists:usort([3,1,2,1,3]) =:= [1,2,3]") "true")
|
||||
|
||||
(er-lx-test "usort/1 empty"
|
||||
(er-lx-nm "lists:usort([]) =:= []") "true")
|
||||
|
||||
(er-lx-test "usort/1 all equal collapses to one"
|
||||
(er-lx-nm "lists:usort([5,5,5]) =:= [5]") "true")
|
||||
|
||||
(er-lx-test "usort/1 already unique"
|
||||
(er-lx-nm "lists:usort([1,2,3]) =:= [1,2,3]") "true")
|
||||
|
||||
(er-lx-test "usort/1 length after dedup"
|
||||
(erlang-eval-ast "length(lists:usort([4,4,2,2,1,1,4]))") 3)
|
||||
|
||||
;; ── lists:keyfind/3 ───────────────────────────────────────────────
|
||||
(er-lx-test "keyfind hit"
|
||||
(erlang-eval-ast "element(2, lists:keyfind(b, 1, [{a,1},{b,2},{c,3}]))") 2)
|
||||
|
||||
(er-lx-test "keyfind first match only"
|
||||
(erlang-eval-ast "element(2, lists:keyfind(a, 1, [{a,1},{a,9}]))") 1)
|
||||
|
||||
(er-lx-test "keyfind miss returns false"
|
||||
(er-lx-nm "lists:keyfind(z, 1, [{a,1},{b,2}])") "false")
|
||||
|
||||
(er-lx-test "keyfind on second element"
|
||||
(er-lx-nm "element(1, lists:keyfind(2, 2, [{a,1},{b,2}]))") "b")
|
||||
|
||||
(er-lx-test "keyfind skips short tuples"
|
||||
(er-lx-nm "lists:keyfind(x, 2, [{x},{y,x}]) =:= {y,x}") "true")
|
||||
|
||||
;; ── lists:keymember/3 ─────────────────────────────────────────────
|
||||
(er-lx-test "keymember true"
|
||||
(er-lx-nm "lists:keymember(b, 1, [{a,1},{b,2}])") "true")
|
||||
|
||||
(er-lx-test "keymember false"
|
||||
(er-lx-nm "lists:keymember(z, 1, [{a,1},{b,2}])") "false")
|
||||
|
||||
;; ── lists:keydelete/3 ─────────────────────────────────────────────
|
||||
(er-lx-test "keydelete removes first match"
|
||||
(er-lx-nm "lists:keydelete(b, 1, [{a,1},{b,2},{c,3}]) =:= [{a,1},{c,3}]") "true")
|
||||
|
||||
(er-lx-test "keydelete only first"
|
||||
(er-lx-nm "lists:keydelete(a, 1, [{a,1},{a,2},{b,3}]) =:= [{a,2},{b,3}]") "true")
|
||||
|
||||
(er-lx-test "keydelete miss unchanged"
|
||||
(er-lx-nm "lists:keydelete(z, 1, [{a,1},{b,2}]) =:= [{a,1},{b,2}]") "true")
|
||||
|
||||
;; ── lists:keyreplace/4 ────────────────────────────────────────────
|
||||
(er-lx-test "keyreplace hit"
|
||||
(er-lx-nm
|
||||
"lists:keyreplace(b, 1, [{a,1},{b,2},{c,3}], {b,99}) =:= [{a,1},{b,99},{c,3}]")
|
||||
"true")
|
||||
|
||||
(er-lx-test "keyreplace miss unchanged"
|
||||
(er-lx-nm
|
||||
"lists:keyreplace(z, 1, [{a,1}], {z,0}) =:= [{a,1}]") "true")
|
||||
|
||||
;; ── lists:keystore/4 ──────────────────────────────────────────────
|
||||
(er-lx-test "keystore replaces existing"
|
||||
(er-lx-nm
|
||||
"lists:keystore(b, 1, [{a,1},{b,2}], {b,99}) =:= [{a,1},{b,99}]") "true")
|
||||
|
||||
(er-lx-test "keystore appends when absent"
|
||||
(er-lx-nm
|
||||
"lists:keystore(z, 1, [{a,1},{b,2}], {z,0}) =:= [{a,1},{b,2},{z,0}]") "true")
|
||||
|
||||
;; ── lists:keytake/3 ───────────────────────────────────────────────
|
||||
(er-lx-test "keytake hit value tag"
|
||||
(er-lx-nm "element(1, lists:keytake(b, 1, [{a,1},{b,2},{c,3}]))") "value")
|
||||
|
||||
(er-lx-test "keytake hit tuple"
|
||||
(er-lx-nm
|
||||
"element(2, lists:keytake(b, 1, [{a,1},{b,2},{c,3}])) =:= {b,2}") "true")
|
||||
|
||||
(er-lx-test "keytake hit rest"
|
||||
(er-lx-nm
|
||||
"element(3, lists:keytake(b, 1, [{a,1},{b,2},{c,3}])) =:= [{a,1},{c,3}]") "true")
|
||||
|
||||
(er-lx-test "keytake miss false"
|
||||
(er-lx-nm "lists:keytake(z, 1, [{a,1}])") "false")
|
||||
|
||||
;; ── lists:keysort/2 ───────────────────────────────────────────────
|
||||
(er-lx-test "keysort by element 1"
|
||||
(er-lx-nm
|
||||
"lists:keysort(1, [{c,3},{a,1},{b,2}]) =:= [{a,1},{b,2},{c,3}]") "true")
|
||||
|
||||
(er-lx-test "keysort by element 2"
|
||||
(er-lx-nm
|
||||
"lists:keysort(2, [{a,3},{b,1},{c,2}]) =:= [{b,1},{c,2},{a,3}]") "true")
|
||||
|
||||
(er-lx-test "keysort stable on equal keys"
|
||||
(er-lx-nm
|
||||
"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")
|
||||
|
||||
;; ── 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")
|
||||
|
||||
;; ── lists:zip/2 ───────────────────────────────────────────────────
|
||||
(er-lx-test "zip pairs"
|
||||
(er-lx-nm "lists:zip([a,b,c],[1,2,3]) =:= [{a,1},{b,2},{c,3}]") "true")
|
||||
|
||||
(er-lx-test "zip empty"
|
||||
(er-lx-nm "lists:zip([],[]) =:= []") "true")
|
||||
|
||||
(er-lx-test "zip length"
|
||||
(erlang-eval-ast "length(lists:zip([1,2],[3,4]))") 2)
|
||||
|
||||
;; ── lists:zipwith/3 ───────────────────────────────────────────────
|
||||
(er-lx-test "zipwith sum"
|
||||
(er-lx-nm
|
||||
"lists:zipwith(fun(X,Y) -> X+Y end, [1,2,3], [10,20,30]) =:= [11,22,33]")
|
||||
"true")
|
||||
|
||||
(er-lx-test "zipwith tuple"
|
||||
(er-lx-nm "lists:zipwith(fun(X,Y) -> {X,Y} end, [a], [1]) =:= [{a,1}]") "true")
|
||||
|
||||
;; ── lists:unzip/1 ─────────────────────────────────────────────────
|
||||
(er-lx-test "unzip"
|
||||
(er-lx-nm "lists:unzip([{a,1},{b,2},{c,3}]) =:= {[a,b,c],[1,2,3]}") "true")
|
||||
|
||||
(er-lx-test "unzip empty"
|
||||
(er-lx-nm "lists:unzip([]) =:= {[],[]}") "true")
|
||||
|
||||
(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")
|
||||
|
||||
;; ── lists:flatmap/2 ───────────────────────────────────────────────
|
||||
(er-lx-test "flatmap duplicates"
|
||||
(er-lx-nm "lists:flatmap(fun(X) -> [X,X] end, [1,2]) =:= [1,1,2,2]") "true")
|
||||
|
||||
(er-lx-test "flatmap empty"
|
||||
(er-lx-nm "lists:flatmap(fun(X) -> [X] end, []) =:= []") "true")
|
||||
|
||||
;; ── lists:filtermap/2 ─────────────────────────────────────────────
|
||||
(er-lx-test "filtermap transform"
|
||||
(er-lx-nm
|
||||
"lists:filtermap(fun(X) -> case X rem 2 of 0 -> {true, X*10}; _ -> false end end, [1,2,3,4]) =:= [20,40]")
|
||||
"true")
|
||||
|
||||
(er-lx-test "filtermap bool keep"
|
||||
(er-lx-nm "lists:filtermap(fun(X) -> X > 2 end, [1,2,3,4]) =:= [3,4]") "true")
|
||||
|
||||
;; ── lists:mapfoldl/3 ──────────────────────────────────────────────
|
||||
(er-lx-test "mapfoldl map+acc"
|
||||
(er-lx-nm
|
||||
"lists:mapfoldl(fun(X,A) -> {X*2, A+X} end, 0, [1,2,3]) =:= {[2,4,6],6}") "true")
|
||||
|
||||
(er-lx-test "mapfoldl empty"
|
||||
(er-lx-nm "lists:mapfoldl(fun(X,A) -> {X,A} end, 5, []) =:= {[],5}") "true")
|
||||
|
||||
;; ── lists:search/2 ────────────────────────────────────────────────
|
||||
(er-lx-test "search hit"
|
||||
(er-lx-nm "lists:search(fun(X) -> X > 2 end, [1,2,3,4]) =:= {value,3}") "true")
|
||||
|
||||
(er-lx-test "search miss"
|
||||
(er-lx-nm "lists:search(fun(X) -> X > 9 end, [1,2,3])") "false")
|
||||
|
||||
;; ── proplists:get_value/2,3 ───────────────────────────────────────
|
||||
(er-lx-test "pl get_value hit"
|
||||
(erlang-eval-ast "proplists:get_value(b, [{a,1},{b,2}])") 2)
|
||||
|
||||
(er-lx-test "pl get_value miss undefined"
|
||||
(er-lx-nm "proplists:get_value(z, [{a,1}])") "undefined")
|
||||
|
||||
(er-lx-test "pl get_value default"
|
||||
(erlang-eval-ast "proplists:get_value(z, [{a,1}], 99)") 99)
|
||||
|
||||
(er-lx-test "pl get_value bare atom is true"
|
||||
(er-lx-nm "proplists:get_value(flag, [flag, {a,1}])") "true")
|
||||
|
||||
(er-lx-test "pl get_value first occurrence"
|
||||
(erlang-eval-ast "proplists:get_value(a, [{a,1},{a,2}])") 1)
|
||||
|
||||
;; ── proplists:get_all_values/2 ────────────────────────────────────
|
||||
(er-lx-test "pl get_all_values"
|
||||
(er-lx-nm
|
||||
"proplists:get_all_values(a, [{a,1},{b,2},{a,3}]) =:= [1,3]") "true")
|
||||
|
||||
;; ── proplists:is_defined/2 ────────────────────────────────────────
|
||||
(er-lx-test "pl is_defined true"
|
||||
(er-lx-nm "proplists:is_defined(b, [{a,1},{b,2}])") "true")
|
||||
|
||||
(er-lx-test "pl is_defined false"
|
||||
(er-lx-nm "proplists:is_defined(z, [{a,1}])") "false")
|
||||
|
||||
;; ── proplists:lookup/2 ────────────────────────────────────────────
|
||||
(er-lx-test "pl lookup hit"
|
||||
(er-lx-nm "proplists:lookup(b, [{a,1},{b,2}]) =:= {b,2}") "true")
|
||||
|
||||
(er-lx-test "pl lookup bare atom"
|
||||
(er-lx-nm "proplists:lookup(flag, [flag]) =:= {flag,true}") "true")
|
||||
|
||||
(er-lx-test "pl lookup miss"
|
||||
(er-lx-nm "proplists:lookup(z, [{a,1}])") "none")
|
||||
|
||||
;; ── proplists:delete/2 ────────────────────────────────────────────
|
||||
(er-lx-test "pl delete removes all"
|
||||
(er-lx-nm "proplists:delete(a, [{a,1},{b,2},{a,3}]) =:= [{b,2}]") "true")
|
||||
@@ -2039,4 +2039,657 @@
|
||||
(range 0 (len ks)))
|
||||
out)))
|
||||
|
||||
;; ── extra lists + proplists BIFs (folded from lists-ext.sx) ──
|
||||
;; ── cons <-> SX-list bridges ──────────────────────────────────────
|
||||
(define
|
||||
er-cons->sxlist
|
||||
(fn (lst)
|
||||
(cond
|
||||
(er-nil? lst) (list)
|
||||
(er-cons? lst) (cons (get lst :head) (er-cons->sxlist (get lst :tail)))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-sxlist->cons
|
||||
(fn (xs)
|
||||
(if (= (len xs) 0)
|
||||
(er-mk-nil)
|
||||
(er-mk-cons (first xs) (er-sxlist->cons (rest xs))))))
|
||||
|
||||
;; ── merge sort over SX lists (stable) ─────────────────────────────
|
||||
(define
|
||||
er-ext-take
|
||||
(fn (xs n)
|
||||
(if (or (= n 0) (= (len xs) 0))
|
||||
(list)
|
||||
(cons (first xs) (er-ext-take (rest xs) (- n 1))))))
|
||||
|
||||
(define
|
||||
er-ext-drop
|
||||
(fn (xs n)
|
||||
(if (or (= n 0) (= (len xs) 0))
|
||||
xs
|
||||
(er-ext-drop (rest xs) (- n 1)))))
|
||||
|
||||
;; le? returns a truthy value (Erlang bool atom or SX bool) iff a
|
||||
;; should sort at-or-before b. Taking from the left half first on a
|
||||
;; true result keeps the sort stable.
|
||||
(define
|
||||
er-ext-merge
|
||||
(fn (a b le?)
|
||||
(cond
|
||||
(= (len a) 0) b
|
||||
(= (len b) 0) a
|
||||
(er-truthy? (le? (first a) (first b)))
|
||||
(cons (first a) (er-ext-merge (rest a) b le?))
|
||||
:else (cons (first b) (er-ext-merge a (rest b) le?)))))
|
||||
|
||||
(define
|
||||
er-ext-msort
|
||||
(fn (xs le?)
|
||||
(if (<= (len xs) 1)
|
||||
xs
|
||||
(let ((mid (quotient (len xs) 2)))
|
||||
(er-ext-merge
|
||||
(er-ext-msort (er-ext-take xs mid) le?)
|
||||
(er-ext-msort (er-ext-drop xs mid) le?)
|
||||
le?)))))
|
||||
|
||||
;; Full Erlang term order. The shared er-lt? (transpile.sx) only
|
||||
;; deep-compares numbers/atoms/strings and otherwise falls back to a
|
||||
;; coarse type rank — so any two tuples (or two lists) compare as
|
||||
;; order-equal there. er-ext-lt? adds the missing structural cases:
|
||||
;; tuples by arity then elementwise, lists elementwise with a shorter
|
||||
;; proper prefix sorting first. Cross-type cases delegate to er-lt?.
|
||||
(define
|
||||
er-ext-lt-seq
|
||||
(fn (ea eb i)
|
||||
(cond
|
||||
(>= i (len ea)) false
|
||||
(er-ext-lt? (nth ea i) (nth eb i)) true
|
||||
(er-ext-lt? (nth eb i) (nth ea i)) false
|
||||
:else (er-ext-lt-seq ea eb (+ i 1)))))
|
||||
|
||||
(define
|
||||
er-ext-lt?
|
||||
(fn (a b)
|
||||
(cond
|
||||
(and (er-tuple? a) (er-tuple? b))
|
||||
(let ((ea (get a :elements)) (eb (get b :elements)))
|
||||
(cond
|
||||
(< (len ea) (len eb)) true
|
||||
(> (len ea) (len eb)) false
|
||||
:else (er-ext-lt-seq ea eb 0)))
|
||||
(and (er-cons? a) (er-cons? b))
|
||||
(cond
|
||||
(er-ext-lt? (get a :head) (get b :head)) true
|
||||
(er-ext-lt? (get b :head) (get a :head)) false
|
||||
:else (er-ext-lt? (get a :tail) (get b :tail)))
|
||||
(and (er-nil? a) (er-cons? b)) true
|
||||
(and (er-cons? a) (er-nil? b)) false
|
||||
(and (er-nil? a) (er-nil? b)) false
|
||||
:else (er-lt? a b))))
|
||||
|
||||
;; Default Erlang term order: a =< b == not (b < a).
|
||||
(define
|
||||
er-ext-term-le
|
||||
(fn (a b) (er-bool (not (er-ext-lt? b a)))))
|
||||
|
||||
;; ── lists:sort/1, lists:sort/2 ────────────────────────────────────
|
||||
(define
|
||||
er-bif-lists-sort
|
||||
(fn (vs)
|
||||
(cond
|
||||
(= (len vs) 1)
|
||||
(er-sxlist->cons
|
||||
(er-ext-msort (er-cons->sxlist (nth vs 0)) er-ext-term-le))
|
||||
(= (len vs) 2)
|
||||
(let ((f (nth vs 0)) (lst (nth vs 1)))
|
||||
(er-sxlist->cons
|
||||
(er-ext-msort
|
||||
(er-cons->sxlist lst)
|
||||
(fn (a b) (er-apply-fun f (list a b))))))
|
||||
:else (error "Erlang: lists:sort: wrong arity"))))
|
||||
|
||||
;; ── lists:usort/1 (sort then drop adjacent term-equal dups) ───────
|
||||
(define
|
||||
er-ext-dedup
|
||||
(fn (xs)
|
||||
(cond
|
||||
(= (len xs) 0) (list)
|
||||
(= (len xs) 1) xs
|
||||
(er-equal? (first xs) (nth xs 1)) (er-ext-dedup (rest xs))
|
||||
:else (cons (first xs) (er-ext-dedup (rest xs))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-usort
|
||||
(fn (vs)
|
||||
(let ((lst (er-bif-arg1 vs "lists:usort")))
|
||||
(er-sxlist->cons
|
||||
(er-ext-dedup
|
||||
(er-ext-msort (er-cons->sxlist lst) er-ext-term-le))))))
|
||||
|
||||
;; ── keylists (lists of tuples keyed on element N, 1-indexed) ──────
|
||||
;; keyfind/keymember/keydelete/keyreplace/keystore/keytake/keysort.
|
||||
;; Key comparison is == (er-equal?), matching the standard lib. Only
|
||||
;; the FIRST matching tuple is acted on. Non-tuples / tuples shorter
|
||||
;; than N never match and are passed through unchanged.
|
||||
(define
|
||||
er-ext-tup-elem
|
||||
(fn (tup n)
|
||||
(if (er-tuple? tup)
|
||||
(let ((es (get tup :elements)))
|
||||
(if (and (>= n 1) (<= n (len es))) (nth es (- n 1)) nil))
|
||||
nil)))
|
||||
|
||||
(define
|
||||
er-ext-key-match?
|
||||
(fn (key n tup)
|
||||
(and
|
||||
(er-tuple? tup)
|
||||
(>= n 1)
|
||||
(<= n (len (get tup :elements)))
|
||||
(er-equal? key (nth (get tup :elements) (- n 1))))))
|
||||
|
||||
(define
|
||||
er-ext-keyfind
|
||||
(fn (key n lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-atom "false")
|
||||
(er-cons? lst)
|
||||
(if (er-ext-key-match? key n (get lst :head))
|
||||
(get lst :head)
|
||||
(er-ext-keyfind key n (get lst :tail)))
|
||||
:else (er-mk-atom "false"))))
|
||||
|
||||
(define
|
||||
er-ext-keydelete
|
||||
(fn (key n lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-nil)
|
||||
(er-cons? lst)
|
||||
(if (er-ext-key-match? key n (get lst :head))
|
||||
(get lst :tail)
|
||||
(er-mk-cons (get lst :head) (er-ext-keydelete key n (get lst :tail))))
|
||||
:else lst)))
|
||||
|
||||
(define
|
||||
er-ext-keyreplace
|
||||
(fn (key n lst new)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-nil)
|
||||
(er-cons? lst)
|
||||
(if (er-ext-key-match? key n (get lst :head))
|
||||
(er-mk-cons new (get lst :tail))
|
||||
(er-mk-cons (get lst :head) (er-ext-keyreplace key n (get lst :tail) new)))
|
||||
:else lst)))
|
||||
|
||||
(define
|
||||
er-ext-keystore
|
||||
(fn (key n lst new)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-cons new (er-mk-nil))
|
||||
(er-cons? lst)
|
||||
(if (er-ext-key-match? key n (get lst :head))
|
||||
(er-mk-cons new (get lst :tail))
|
||||
(er-mk-cons (get lst :head) (er-ext-keystore key n (get lst :tail) new)))
|
||||
:else lst)))
|
||||
|
||||
(define
|
||||
er-bif-lists-keyfind
|
||||
(fn (vs) (er-ext-keyfind (nth vs 0) (nth vs 1) (nth vs 2))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keymember
|
||||
(fn (vs)
|
||||
(er-bool (not (er-atom? (er-ext-keyfind (nth vs 0) (nth vs 1) (nth vs 2)))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keydelete
|
||||
(fn (vs) (er-ext-keydelete (nth vs 0) (nth vs 1) (nth vs 2))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keyreplace
|
||||
(fn (vs) (er-ext-keyreplace (nth vs 0) (nth vs 1) (nth vs 2) (nth vs 3))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keystore
|
||||
(fn (vs) (er-ext-keystore (nth vs 0) (nth vs 1) (nth vs 2) (nth vs 3))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keytake
|
||||
(fn (vs)
|
||||
(let ((key (nth vs 0)) (n (nth vs 1)) (lst (nth vs 2)))
|
||||
(let ((hit (er-ext-keyfind key n lst)))
|
||||
(if (er-atom? hit)
|
||||
(er-mk-atom "false")
|
||||
(er-mk-tuple
|
||||
(list (er-mk-atom "value") hit (er-ext-keydelete key n lst))))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-keysort
|
||||
(fn (vs)
|
||||
(let ((n (nth vs 0)) (lst (nth vs 1)))
|
||||
(er-sxlist->cons
|
||||
(er-ext-msort
|
||||
(er-cons->sxlist lst)
|
||||
(fn (a b)
|
||||
(er-bool
|
||||
(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))))))
|
||||
|
||||
;; ── 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")))))))
|
||||
|
||||
;; ── zip family (zip / zipwith / unzip) ────────────────────────────
|
||||
;; Length mismatch raises badarg (real Erlang raises function_clause;
|
||||
;; badarg is the closest in-port equivalent).
|
||||
(define
|
||||
er-ext-zip
|
||||
(fn (a b)
|
||||
(cond
|
||||
(and (er-nil? a) (er-nil? b)) (er-mk-nil)
|
||||
(and (er-cons? a) (er-cons? b))
|
||||
(er-mk-cons
|
||||
(er-mk-tuple (list (get a :head) (get b :head)))
|
||||
(er-ext-zip (get a :tail) (get b :tail)))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-zip
|
||||
(fn (vs) (er-ext-zip (nth vs 0) (nth vs 1))))
|
||||
|
||||
(define
|
||||
er-ext-zipwith
|
||||
(fn (f a b)
|
||||
(cond
|
||||
(and (er-nil? a) (er-nil? b)) (er-mk-nil)
|
||||
(and (er-cons? a) (er-cons? b))
|
||||
(er-mk-cons
|
||||
(er-apply-fun f (list (get a :head) (get b :head)))
|
||||
(er-ext-zipwith f (get a :tail) (get b :tail)))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-zipwith
|
||||
(fn (vs) (er-ext-zipwith (nth vs 0) (nth vs 1) (nth vs 2))))
|
||||
|
||||
(define
|
||||
er-ext-unzip
|
||||
(fn (lst as bs)
|
||||
(cond
|
||||
(er-nil? lst)
|
||||
(er-mk-tuple
|
||||
(list
|
||||
(er-list-reverse-iter as (er-mk-nil))
|
||||
(er-list-reverse-iter bs (er-mk-nil))))
|
||||
(and (er-cons? lst) (er-tuple? (get lst :head)))
|
||||
(let ((es (get (get lst :head) :elements)))
|
||||
(if (= (len es) 2)
|
||||
(er-ext-unzip (get lst :tail)
|
||||
(er-mk-cons (nth es 0) as)
|
||||
(er-mk-cons (nth es 1) bs))
|
||||
(raise (er-mk-error-marker (er-mk-atom "badarg")))))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-unzip
|
||||
(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"))))
|
||||
|
||||
;; ── more higher-order (flatmap / filtermap / mapfoldl / search) ───
|
||||
(define
|
||||
er-ext-flatmap
|
||||
(fn (f lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-nil)
|
||||
(er-cons? lst)
|
||||
(er-list-append
|
||||
(er-apply-fun f (list (get lst :head)))
|
||||
(er-ext-flatmap f (get lst :tail)))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-flatmap
|
||||
(fn (vs) (er-ext-flatmap (nth vs 0) (nth vs 1))))
|
||||
|
||||
(define
|
||||
er-ext-atom-true?
|
||||
(fn (v) (and (er-atom? v) (= (get v :name) "true"))))
|
||||
|
||||
(define
|
||||
er-ext-filtermap
|
||||
(fn (f lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-nil)
|
||||
(er-cons? lst)
|
||||
(let ((r (er-apply-fun f (list (get lst :head)))))
|
||||
(cond
|
||||
(er-ext-atom-true? r)
|
||||
(er-mk-cons (get lst :head) (er-ext-filtermap f (get lst :tail)))
|
||||
(and
|
||||
(er-tuple? r)
|
||||
(= (len (get r :elements)) 2)
|
||||
(er-ext-atom-true? (nth (get r :elements) 0)))
|
||||
(er-mk-cons (nth (get r :elements) 1) (er-ext-filtermap f (get lst :tail)))
|
||||
:else (er-ext-filtermap f (get lst :tail))))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-filtermap
|
||||
(fn (vs) (er-ext-filtermap (nth vs 0) (nth vs 1))))
|
||||
|
||||
(define
|
||||
er-ext-mapfoldl
|
||||
(fn (f acc lst mapped)
|
||||
(cond
|
||||
(er-nil? lst)
|
||||
(er-mk-tuple (list (er-list-reverse-iter mapped (er-mk-nil)) acc))
|
||||
(er-cons? lst)
|
||||
(let ((r (er-apply-fun f (list (get lst :head) acc))))
|
||||
(let ((es (get r :elements)))
|
||||
(er-ext-mapfoldl f (nth es 1) (get lst :tail)
|
||||
(er-mk-cons (nth es 0) mapped))))
|
||||
:else (raise (er-mk-error-marker (er-mk-atom "badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-lists-mapfoldl
|
||||
(fn (vs) (er-ext-mapfoldl (nth vs 0) (nth vs 1) (nth vs 2) (er-mk-nil))))
|
||||
|
||||
(define
|
||||
er-ext-search
|
||||
(fn (pred lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-atom "false")
|
||||
(er-cons? lst)
|
||||
(if (er-truthy? (er-apply-fun pred (list (get lst :head))))
|
||||
(er-mk-tuple (list (er-mk-atom "value") (get lst :head)))
|
||||
(er-ext-search pred (get lst :tail)))
|
||||
:else (er-mk-atom "false"))))
|
||||
|
||||
(define
|
||||
er-bif-lists-search
|
||||
(fn (vs) (er-ext-search (nth vs 0) (nth vs 1))))
|
||||
|
||||
;; ── proplists module ──────────────────────────────────────────────
|
||||
;; A property list element is either a bare atom A (shorthand for
|
||||
;; {A, true}) or a tuple whose first element is the key (value = its
|
||||
;; second element, or true for a 1-tuple). Lookups use the FIRST match.
|
||||
(define
|
||||
er-ext-pl-key-of
|
||||
(fn (e)
|
||||
(cond
|
||||
(er-atom? e) e
|
||||
(and (er-tuple? e) (>= (len (get e :elements)) 1)) (nth (get e :elements) 0)
|
||||
:else nil)))
|
||||
|
||||
(define
|
||||
er-ext-pl-val-of
|
||||
(fn (e)
|
||||
(cond
|
||||
(and (er-tuple? e) (>= (len (get e :elements)) 2)) (nth (get e :elements) 1)
|
||||
:else (er-mk-atom "true"))))
|
||||
|
||||
(define
|
||||
er-ext-pl-match?
|
||||
(fn (key e)
|
||||
(let ((k (er-ext-pl-key-of e)))
|
||||
(and (not (= k nil)) (er-equal? key k)))))
|
||||
|
||||
(define
|
||||
er-ext-pl-get-value
|
||||
(fn (key lst default)
|
||||
(cond
|
||||
(er-nil? lst) default
|
||||
(er-cons? lst)
|
||||
(if (er-ext-pl-match? key (get lst :head))
|
||||
(er-ext-pl-val-of (get lst :head))
|
||||
(er-ext-pl-get-value key (get lst :tail) default))
|
||||
:else default)))
|
||||
|
||||
(define
|
||||
er-bif-pl-get-value
|
||||
(fn (vs)
|
||||
(cond
|
||||
(= (len vs) 2)
|
||||
(er-ext-pl-get-value (nth vs 0) (nth vs 1) (er-mk-atom "undefined"))
|
||||
(= (len vs) 3)
|
||||
(er-ext-pl-get-value (nth vs 0) (nth vs 1) (nth vs 2))
|
||||
:else (error "Erlang: proplists:get_value: wrong arity"))))
|
||||
|
||||
(define
|
||||
er-ext-pl-all
|
||||
(fn (key lst acc)
|
||||
(cond
|
||||
(er-nil? lst) (er-list-reverse-iter acc (er-mk-nil))
|
||||
(er-cons? lst)
|
||||
(er-ext-pl-all key (get lst :tail)
|
||||
(if (er-ext-pl-match? key (get lst :head))
|
||||
(er-mk-cons (er-ext-pl-val-of (get lst :head)) acc)
|
||||
acc))
|
||||
:else (er-list-reverse-iter acc (er-mk-nil)))))
|
||||
|
||||
(define
|
||||
er-bif-pl-get-all-values
|
||||
(fn (vs) (er-ext-pl-all (nth vs 0) (nth vs 1) (er-mk-nil))))
|
||||
|
||||
(define
|
||||
er-ext-pl-defined?
|
||||
(fn (key lst)
|
||||
(cond
|
||||
(er-nil? lst) false
|
||||
(er-cons? lst)
|
||||
(if (er-ext-pl-match? key (get lst :head))
|
||||
true
|
||||
(er-ext-pl-defined? key (get lst :tail)))
|
||||
:else false)))
|
||||
|
||||
(define
|
||||
er-bif-pl-is-defined
|
||||
(fn (vs) (er-bool (er-ext-pl-defined? (nth vs 0) (nth vs 1)))))
|
||||
|
||||
(define
|
||||
er-ext-pl-lookup
|
||||
(fn (key lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-atom "none")
|
||||
(er-cons? lst)
|
||||
(if (er-ext-pl-match? key (get lst :head))
|
||||
(let ((e (get lst :head)))
|
||||
(if (er-tuple? e) e (er-mk-tuple (list e (er-mk-atom "true")))))
|
||||
(er-ext-pl-lookup key (get lst :tail)))
|
||||
:else (er-mk-atom "none"))))
|
||||
|
||||
(define
|
||||
er-bif-pl-lookup
|
||||
(fn (vs) (er-ext-pl-lookup (nth vs 0) (nth vs 1))))
|
||||
|
||||
(define
|
||||
er-ext-pl-delete
|
||||
(fn (key lst)
|
||||
(cond
|
||||
(er-nil? lst) (er-mk-nil)
|
||||
(er-cons? lst)
|
||||
(if (er-ext-pl-match? key (get lst :head))
|
||||
(er-ext-pl-delete key (get lst :tail))
|
||||
(er-mk-cons (get lst :head) (er-ext-pl-delete key (get lst :tail))))
|
||||
:else lst)))
|
||||
|
||||
(define
|
||||
er-bif-pl-delete
|
||||
(fn (vs) (er-ext-pl-delete (nth vs 0) (nth vs 1))))
|
||||
|
||||
@@ -159,6 +159,24 @@ The Phase 9 opcodes are registered, tested, and bridged SX↔OCaml, but inert: n
|
||||
|
||||
_Newest first._
|
||||
|
||||
- **2026-06-30 retire separate `lists-ext.sx` — fold stdlib BIFs into canonical files** — The 8 stdlib commits had lived in a standalone `lib/erlang/lists-ext.sx` (a workaround for this worktree's broken sx-tree write tools). Folded the function bodies into `transpile.sx` (appended, alongside the existing `er-bif-lists-*`) and moved the registrations **directly inside `er-register-builtin-bifs!`** in `runtime.sx` — so they now reach every erlang consumer (fed-sx, identity, …) that loads the runtime, not just the conformance harness. The `er-register-builtin-bifs!` **wrapper trick is gone** (no longer needed once registrations live inside the function the registry-reset re-runs). Deleted `lists-ext.sx` and its `(load …)` from `conformance.sh`; kept the `lists_ext` test suite + wiring. Byte-exact splice (no transcription), `sx_validate` clean, conformance **874/874** unchanged. Mirrors the same fold-in landed on the `architecture` branch (commit `39dbb00c`, equivalence verified: identical test suite + function bodies). loops/erlang only.
|
||||
|
||||
- **2026-06-30 stdlib hardening — `proplists` module** — Added `proplists:get_value/2,3`, `get_all_values/2`, `is_defined/2`, `lookup/2`, `delete/2` to `lib/erlang/lists-ext.sx` (header widened to "lists + proplists"). Property-list semantics: a bare atom `A` is shorthand for `{A, true}`, a tuple's first element is the key, lookups use the first match. `lookup` returns the tuple (or `{Key,true}`) or `none`; `get_value` defaults to `undefined`. The `lists_ext` suite (counter trio `er-lx-*`, now spanning both modules) 91→**103** (+12). Conformance **862 → 874/874**. loops/erlang only.
|
||||
|
||||
- **2026-06-30 stdlib hardening — `lists` flatmap/filtermap/mapfoldl/search** — Added `flatmap/2`, `filtermap/2` (`true` keep / `false` drop / `{true, V}` transform), `mapfoldl/3` (returns `{MappedList, AccFinal}`), `search/2` (`{value, E}` | `false`) to `lib/erlang/lists-ext.sx`. `lists_ext` suite 83→**91** (+8). Conformance **854 → 862/862**. The `lists` module is now broadly covered (sort/usort/keylists/fold/partition/while/flatten/min/max/zip/slicing/flatmap/filtermap/mapfoldl/search on top of the originals). loops/erlang only.
|
||||
|
||||
- **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.
|
||||
|
||||
- **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: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-05-18 Phase 8 host-primitive BIFs wired (crypto / cid / file:list_dir)** — `loops/fed-prims` (merged at architecture `380bc69f`) delivered the platform primitives; wired the 3 previously-BLOCKED Phase 8 BIF groups in `lib/erlang/runtime.sx` as `er-register-pure-bif!`/`er-register-bif!` entries with term marshalling at the boundary. **`crypto:hash/2`** → `crypto-sha256`/`crypto-sha512`/`crypto-sha3-256`; atom `Type` dispatch, `er-source-to-string` for `Data`, host hex result → raw bytes via new `er-hexval`/`er-hex->bytes`, returns Erlang binary; bad type/arg → `error:badarg`. **`cid:from_bytes/1`** → `cid-from-bytes` with raw codec `0x55` + sha2-256 multihash assembled in SX (`[0x12,0x20]++digest`); **`cid:to_string/1`** → `cid-from-sx` of `er-format-value` (cbor-encode rejects `er-to-sx`-marshalled symbols; the canonical string form is total + deterministic). **`file:list_dir/1`** → `file-list-dir`, `{ok,[Binary]}` via `er-of-sx` / `{error,Reason}` reusing `er-classify-file-error`. Test gotcha caught + fixed: this Erlang port's binary parser only supports integer/var segments — `<<"abc">>` string-binary literals silently produce **empty** binaries, so the first-cut distinct-input tests compared two empty inputs and failed; rewrote ffi inputs to integer-segment binaries (`<<97,98,99>>`). ffi suite 14→**28** (3 BLOCKED negative-asserts flipped to positive+negative functional tests; `httpc`/`sqlite` kept as deferred unregistered-asserts per fed-prims handoff). Built `sx_server.exe` (dune, opam 5.2.0) at `380bc69f`; full conformance **729/729** (eval 385/385, vm 78/78, **ffi 28/28**, all process suites green). loops/erlang only — not merged, not pushed to architecture.
|
||||
|
||||
- **2026-05-18 FIXED merge-blocking regression: cyclic-env hang in `er-env-derived-from?`** — A trial merge of loops/erlang → architecture regressed Erlang **715/715 → 0/0** on the architecture binary. Bisected: not loader semantics, not a uniform slowdown — pinpointed to the *single* Phase 7 capstone test (eval.sx lines 1314-1346; prefix-1313 was byte-identical speed on both binaries, 27s, prefix-1346 was 28s on loops vs >5min/hung on architecture). Isolated further: spawn+reload alone 0.6s, reload+purge alone 0.3s, but spawn+reload+**purge over forever-blocked procs** hung. Root cause: `er-env-derived-from?` (transpile.sx, used by `code:purge`/`soft_purge` via `er-procs-on-env`) compared closure envs with `(= env target-env)`. loops/erlang's evaluator implements dict `=` as **object identity**; architecture's 131-commit-newer evaluator changed it to **structural deep equality**. Erlang closure envs are large and **cyclic** (a module fun's `:env` transitively references the fun), so structural `=` over them never terminates. Fix: use `identical?` (pointer-identity predicate, present + consistent `(true false)` on *both* binaries) — the actually-intended semantics and host-independent. Verified: full eval.sx on the architecture binary >200s/hung → **59s**; full 10-suite conformance on the architecture binary now **715/715** (eval 385/385, vm 78/78, ffi 14/14, all process suites green). loops/erlang behaviour unchanged (`identical?` ≡ its old `=`-identity). One-file change (`lib/erlang/transpile.sx`, +7/-2). The merge can now be re-attempted; this was the sole blocker.
|
||||
@@ -251,6 +269,8 @@ _Newest first._
|
||||
|
||||
## Blockers
|
||||
|
||||
- **sx-tree WRITE tools broken in this worktree** (2026-06-30). Every sx-tree edit/write tool (`sx_replace_node`, `sx_insert_child`, `sx_insert_near`, …) **and even `sx_write_file`** raise `Yojson.Util.Type_error("Expected string, got null")` against the `mcp_tree.exe` bound in `.mcp.json` (the `/root/rose-ash/...` main-worktree binary). Read/comprehension tools (`sx_validate`, `sx_find_all`, `sx_eval`, `sx_read_*`) work fine. **Workaround:** author/edit `.sx` files with the plain `Write` tool, then `sx_validate` — the same fallback other loops document (see `project_host_on_sx.md`, `project_content_on_sx.md` memory). This is why new `lib/erlang` BIFs land as fresh files (e.g. `lists-ext.sx`) rather than in-place edits to the large `transpile.sx`/`runtime.sx`. Real fix: rebuild `mcp_tree.exe` from current `hosts/ocaml` (out of this loop's binary-build scope) or repoint `.mcp.json` at a fixed binary.
|
||||
|
||||
- **Phase 10a — opcode emission requires `lib/compiler.sx` (out of scope)** (2026-05-15). Architecture fully traced this iteration: the OCaml JIT (`sx_vm.ml` `jit_compile_lambda`, ref-set at line 1206) invokes the SX-level `compile` from **`lib/compiler.sx`** via the CEK machine; that is the sole SX→bytecode producer. Erlang's hot helpers (`er-match-tuple`, `er-bif-*`, …) are SX functions in `transpile.sx` that get JIT-compiled through this path. To emit `erlang.OP_*` they must be recognized as intrinsics inside `compiler.sx`'s `compile-call` (the file's own docstring already anticipates this: "Compilers call `extension-opcode-id` to emit extension opcodes" — designed, not yet implemented). `lib/compiler.sx` is **lib-root**, excluded by the ground rules ("Don't edit lib/ root") and absent from the widened `lib/erlang/** + hosts/ocaml/** (extension only)` scope — editing it changes every guest language's JIT, so it must be owned by a shared-compiler session, not this loop. **Fix path:** that session implements 10a.1 (intrinsic registry in `compiler.sx`) + 10a.2 (`compile-call` emits the opcode when registered & `extension-opcode-id` non-nil, else generic CALL). Erlang's BIF handlers (10b, ids 230-239, all real) light up the instant emission exists — zero further work here. The control opcodes (222-229) additionally need 10a.3 (operand contract) + OCaml↔SX runtime-state bridging (Erlang scheduler/mailbox live in `lib/erlang/runtime.sx`, not OCaml).
|
||||
|
||||
- **Phase 9g — Perf bench gated on 9a** (2026-05-14). The conformance half of 9g (709/709 with stub VM loaded) is satisfied; the perf-bench half requires 9a's bytecode compiler to actually emit the new opcodes at hot call sites. Until then a benchmark would measure today's `er-bif-*` / `er-match-*` numbers unchanged (since the stub handlers wrap them 1-to-1). Re-fire 9g after 9a lands.
|
||||
|
||||
Reference in New Issue
Block a user