erlang: core BIFs + funs, Phase 2 complete (+35 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
This commit is contained in:
@@ -210,6 +210,81 @@
|
||||
(nm (ev "case 3 of N when N * 2 > 5 -> big; _ -> small end"))
|
||||
"big")
|
||||
|
||||
;; ── BIFs: list + tuple ──────────────────────────────────────────
|
||||
(er-eval-test "length empty" (ev "length([])") 0)
|
||||
(er-eval-test "length 3" (ev "length([a, b, c])") 3)
|
||||
(er-eval-test "length cons chain" (ev "length([1 | [2 | [3 | []]]])") 3)
|
||||
(er-eval-test "hd" (ev "hd([10, 20, 30])") 10)
|
||||
(er-eval-test "hd atom"
|
||||
(nm (ev "hd([ok, err])")) "ok")
|
||||
(er-eval-test "tl head"
|
||||
(get (ev "tl([1, 2, 3])") :head) 2)
|
||||
(er-eval-test "tl of single" (get (ev "tl([1])") :tag) "nil")
|
||||
(er-eval-test "element 1" (nm (ev "element(1, {ok, value})")) "ok")
|
||||
(er-eval-test "element 2" (ev "element(2, {ok, 42})") 42)
|
||||
(er-eval-test "element 3"
|
||||
(nm (ev "element(3, {a, b, c, d})")) "c")
|
||||
(er-eval-test "tuple_size 2" (ev "tuple_size({a, b})") 2)
|
||||
(er-eval-test "tuple_size 0" (ev "tuple_size({})") 0)
|
||||
|
||||
;; ── BIFs: atom / list conversions ───────────────────────────────
|
||||
(er-eval-test "atom_to_list" (ev "atom_to_list(hello)") "hello")
|
||||
(er-eval-test "list_to_atom roundtrip"
|
||||
(nm (ev "list_to_atom(atom_to_list(foo))")) "foo")
|
||||
(er-eval-test "list_to_atom fresh"
|
||||
(nm (ev "list_to_atom(\"bar\")")) "bar")
|
||||
|
||||
;; ── lists module ────────────────────────────────────────────────
|
||||
(er-eval-test "lists:reverse empty"
|
||||
(get (ev "lists:reverse([])") :tag) "nil")
|
||||
(er-eval-test "lists:reverse 3"
|
||||
(ev "hd(lists:reverse([1, 2, 3]))") 3)
|
||||
(er-eval-test "lists:reverse full"
|
||||
(ev "lists:foldl(fun (X, Acc) -> Acc + X end, 0, lists:reverse([1, 2, 3]))") 6)
|
||||
|
||||
;; ── funs + lists:map / lists:foldl ──────────────────────────────
|
||||
(er-eval-test "fun call" (ev "F = fun (X) -> X + 1 end, F(10)") 11)
|
||||
(er-eval-test "fun two-arg"
|
||||
(ev "F = fun (X, Y) -> X * Y end, F(3, 4)") 12)
|
||||
(er-eval-test "fun closure"
|
||||
(ev "N = 100, F = fun (X) -> X + N end, F(5)") 105)
|
||||
(er-eval-test "fun clauses"
|
||||
(ev "F = fun (0) -> zero; (N) -> N end, element(1, {F(0), F(7)})")
|
||||
(er-mk-atom "zero"))
|
||||
(er-eval-test "fun multi-clause second"
|
||||
(ev "F = fun (0) -> 0; (N) -> N * 2 end, F(5)") 10)
|
||||
(er-eval-test "lists:map empty"
|
||||
(get (ev "lists:map(fun (X) -> X end, [])") :tag) "nil")
|
||||
(er-eval-test "lists:map double"
|
||||
(ev "hd(lists:map(fun (X) -> X * 2 end, [1, 2, 3]))") 2)
|
||||
(er-eval-test "lists:map sum-length"
|
||||
(ev "length(lists:map(fun (X) -> X end, [a, b, c, d]))") 4)
|
||||
(er-eval-test "lists:foldl sum"
|
||||
(ev "lists:foldl(fun (X, Acc) -> X + Acc end, 0, [1, 2, 3, 4, 5])") 15)
|
||||
(er-eval-test "lists:foldl product"
|
||||
(ev "lists:foldl(fun (X, Acc) -> X * Acc end, 1, [1, 2, 3, 4])") 24)
|
||||
(er-eval-test "lists:foldl as reverse"
|
||||
(ev "hd(lists:foldl(fun (X, Acc) -> [X | Acc] end, [], [1, 2, 3]))") 3)
|
||||
|
||||
;; ── io:format (via capture buffer) ──────────────────────────────
|
||||
(er-eval-test "io:format plain"
|
||||
(do (er-io-flush!) (ev "io:format(\"hello~n\")") (er-io-buffer-content))
|
||||
"hello\n")
|
||||
(er-eval-test "io:format args"
|
||||
(do (er-io-flush!) (ev "io:format(\"x=~p y=~p~n\", [42, hello])") (er-io-buffer-content))
|
||||
"x=42 y=hello\n")
|
||||
(er-eval-test "io:format returns ok"
|
||||
(nm (do (er-io-flush!) (ev "io:format(\"~n\")"))) "ok")
|
||||
(er-eval-test "io:format tuple"
|
||||
(do (er-io-flush!) (ev "io:format(\"~p\", [{ok, 1}])") (er-io-buffer-content))
|
||||
"{ok,1}")
|
||||
(er-eval-test "io:format list"
|
||||
(do (er-io-flush!) (ev "io:format(\"~p\", [[1,2,3]])") (er-io-buffer-content))
|
||||
"[1,2,3]")
|
||||
(er-eval-test "io:format escape"
|
||||
(do (er-io-flush!) (ev "io:format(\"50~~\")") (er-io-buffer-content))
|
||||
"50~")
|
||||
|
||||
(define
|
||||
er-eval-test-summary
|
||||
(str "eval " er-eval-test-pass "/" er-eval-test-count))
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
(= ty "if") (er-eval-if node env)
|
||||
(= ty "case") (er-eval-case node env)
|
||||
(= ty "call") (er-eval-call node env)
|
||||
(= ty "fun") (er-eval-fun node env)
|
||||
(= ty "match") (er-eval-match node env)
|
||||
:else (error (str "Erlang eval: unsupported node type '" ty "'"))))))
|
||||
|
||||
@@ -447,17 +448,22 @@
|
||||
(fn
|
||||
(node env)
|
||||
(let
|
||||
((fun (get node :fun)) (args (get node :args)))
|
||||
((fun-node (get node :fun)) (args (get node :args)))
|
||||
(cond
|
||||
(= (get fun :type) "atom")
|
||||
(er-eval-local-call (get fun :value) args env)
|
||||
(= (get fun :type) "remote")
|
||||
(er-eval-remote-call
|
||||
(get (get fun :mod) :value)
|
||||
(get (get fun :fun) :value)
|
||||
args
|
||||
env)
|
||||
:else (error "Erlang: unsupported call target")))))
|
||||
(= (get fun-node :type) "atom")
|
||||
(er-apply-bif (get fun-node :value) (er-eval-args args env))
|
||||
(= (get fun-node :type) "remote")
|
||||
(er-apply-remote-bif
|
||||
(get (get fun-node :mod) :value)
|
||||
(get (get fun-node :fun) :value)
|
||||
(er-eval-args args env))
|
||||
:else
|
||||
(let
|
||||
((fv (er-eval-expr fun-node env)))
|
||||
(if
|
||||
(er-fun? fv)
|
||||
(er-apply-fun fv (er-eval-args args env))
|
||||
(error "Erlang: not a function")))))))
|
||||
|
||||
(define
|
||||
er-eval-args
|
||||
@@ -470,22 +476,46 @@
|
||||
(range 0 (len args)))
|
||||
out)))
|
||||
|
||||
;; ── fun values ───────────────────────────────────────────────────
|
||||
(define
|
||||
er-eval-local-call
|
||||
(fn
|
||||
(name args env)
|
||||
(let
|
||||
((vs (er-eval-args args env)))
|
||||
(er-apply-bif name vs))))
|
||||
er-mk-fun
|
||||
(fn (clauses env) {:env env :clauses clauses :tag "fun"}))
|
||||
(define er-fun? (fn (v) (er-is-tagged? v "fun")))
|
||||
|
||||
(define
|
||||
er-eval-remote-call
|
||||
er-eval-fun
|
||||
(fn (node env) (er-mk-fun (get node :clauses) env)))
|
||||
|
||||
(define
|
||||
er-apply-fun
|
||||
(fn
|
||||
(mod name args env)
|
||||
(error
|
||||
(str "Erlang: undefined function '" mod ":" name "/" (len args) "'"))))
|
||||
(fv vs)
|
||||
(er-apply-fun-clauses (get fv :clauses) vs (get fv :env) 0)))
|
||||
|
||||
(define
|
||||
er-apply-fun-clauses
|
||||
(fn
|
||||
(clauses vs closure-env i)
|
||||
(if
|
||||
(>= i (len clauses))
|
||||
(error "Erlang: function_clause: no matching fun clause")
|
||||
(let
|
||||
((c (nth clauses i))
|
||||
(ps (get c :patterns))
|
||||
(call-env (er-env-copy closure-env)))
|
||||
(if
|
||||
(not (= (len ps) (len vs)))
|
||||
(er-apply-fun-clauses clauses vs closure-env (+ i 1))
|
||||
(if
|
||||
(and
|
||||
(er-match-all ps vs 0 call-env)
|
||||
(er-eval-guards (get c :guards) call-env))
|
||||
(er-eval-body (get c :body) call-env)
|
||||
(er-apply-fun-clauses clauses vs closure-env (+ i 1))))))))
|
||||
|
||||
;; ── BIFs ─────────────────────────────────────────────────────────
|
||||
(define er-atom-ok (er-mk-atom "ok"))
|
||||
|
||||
(define
|
||||
er-apply-bif
|
||||
(fn
|
||||
@@ -498,9 +528,47 @@
|
||||
(= name "is_number") (er-bif-is-number vs)
|
||||
(= name "is_float") (er-bif-is-float vs)
|
||||
(= name "is_boolean") (er-bif-is-boolean vs)
|
||||
(= name "length") (er-bif-length vs)
|
||||
(= name "hd") (er-bif-hd vs)
|
||||
(= name "tl") (er-bif-tl vs)
|
||||
(= name "element") (er-bif-element vs)
|
||||
(= name "tuple_size") (er-bif-tuple-size vs)
|
||||
(= name "atom_to_list") (er-bif-atom-to-list vs)
|
||||
(= name "list_to_atom") (er-bif-list-to-atom vs)
|
||||
:else (error
|
||||
(str "Erlang: undefined function '" name "/" (len vs) "'")))))
|
||||
|
||||
(define
|
||||
er-apply-remote-bif
|
||||
(fn
|
||||
(mod name vs)
|
||||
(cond
|
||||
(= mod "lists") (er-apply-lists-bif name vs)
|
||||
(= mod "io") (er-apply-io-bif name vs)
|
||||
(= mod "erlang") (er-apply-bif name vs)
|
||||
:else (error
|
||||
(str "Erlang: undefined module '" mod "'")))))
|
||||
|
||||
(define
|
||||
er-apply-lists-bif
|
||||
(fn
|
||||
(name vs)
|
||||
(cond
|
||||
(= name "reverse") (er-bif-lists-reverse vs)
|
||||
(= name "map") (er-bif-lists-map vs)
|
||||
(= name "foldl") (er-bif-lists-foldl vs)
|
||||
:else (error
|
||||
(str "Erlang: undefined 'lists:" name "/" (len vs) "'")))))
|
||||
|
||||
(define
|
||||
er-apply-io-bif
|
||||
(fn
|
||||
(name vs)
|
||||
(cond
|
||||
(= name "format") (er-bif-io-format vs)
|
||||
:else (error
|
||||
(str "Erlang: undefined 'io:" name "/" (len vs) "'")))))
|
||||
|
||||
(define
|
||||
er-bif-arg1
|
||||
(fn
|
||||
@@ -556,3 +624,275 @@
|
||||
((v (er-bif-arg1 vs "is_boolean")))
|
||||
(er-bool
|
||||
(or (er-is-atom-named? v "true") (er-is-atom-named? v "false"))))))
|
||||
|
||||
;; ── list / tuple BIFs ────────────────────────────────────────────
|
||||
(define er-bif-length (fn (vs) (er-list-length (er-bif-arg1 vs "length"))))
|
||||
|
||||
(define
|
||||
er-list-length
|
||||
(fn
|
||||
(v)
|
||||
(cond
|
||||
(er-nil? v) 0
|
||||
(er-cons? v) (+ 1 (er-list-length (get v :tail)))
|
||||
:else (error "Erlang: length: not a proper list"))))
|
||||
|
||||
(define
|
||||
er-bif-hd
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((v (er-bif-arg1 vs "hd")))
|
||||
(if
|
||||
(er-cons? v)
|
||||
(get v :head)
|
||||
(error "Erlang: hd: empty list or non-list")))))
|
||||
|
||||
(define
|
||||
er-bif-tl
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((v (er-bif-arg1 vs "tl")))
|
||||
(if
|
||||
(er-cons? v)
|
||||
(get v :tail)
|
||||
(error "Erlang: tl: empty list or non-list")))))
|
||||
|
||||
(define
|
||||
er-bif-element
|
||||
(fn
|
||||
(vs)
|
||||
(if
|
||||
(not (= (len vs) 2))
|
||||
(error "Erlang: element: arity")
|
||||
(let
|
||||
((i (nth vs 0)) (t (nth vs 1)))
|
||||
(if
|
||||
(and (= (type-of i) "number") (er-tuple? t))
|
||||
(let
|
||||
((elems (get t :elements)))
|
||||
(if
|
||||
(and (>= i 1) (<= i (len elems)))
|
||||
(nth elems (- i 1))
|
||||
(error "Erlang: element: badarg (index out of range)")))
|
||||
(error "Erlang: element: badarg"))))))
|
||||
|
||||
(define
|
||||
er-bif-tuple-size
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((v (er-bif-arg1 vs "tuple_size")))
|
||||
(if
|
||||
(er-tuple? v)
|
||||
(len (get v :elements))
|
||||
(error "Erlang: tuple_size: not a tuple")))))
|
||||
|
||||
(define
|
||||
er-bif-atom-to-list
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((v (er-bif-arg1 vs "atom_to_list")))
|
||||
(if
|
||||
(er-atom? v)
|
||||
(get v :name)
|
||||
(error "Erlang: atom_to_list: not an atom")))))
|
||||
|
||||
(define
|
||||
er-bif-list-to-atom
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((v (er-bif-arg1 vs "list_to_atom")))
|
||||
(if
|
||||
(= (type-of v) "string")
|
||||
(er-mk-atom v)
|
||||
(error "Erlang: list_to_atom: not a string")))))
|
||||
|
||||
;; ── lists module ─────────────────────────────────────────────────
|
||||
(define
|
||||
er-bif-lists-reverse
|
||||
(fn
|
||||
(vs)
|
||||
(er-list-reverse-iter (er-bif-arg1 vs "lists:reverse") (er-mk-nil))))
|
||||
|
||||
(define
|
||||
er-list-reverse-iter
|
||||
(fn
|
||||
(v acc)
|
||||
(cond
|
||||
(er-nil? v) acc
|
||||
(er-cons? v)
|
||||
(er-list-reverse-iter (get v :tail) (er-mk-cons (get v :head) acc))
|
||||
:else (error "Erlang: lists:reverse: not a list"))))
|
||||
|
||||
(define
|
||||
er-bif-lists-map
|
||||
(fn
|
||||
(vs)
|
||||
(if
|
||||
(not (= (len vs) 2))
|
||||
(error "Erlang: lists:map: arity")
|
||||
(er-list-reverse-iter
|
||||
(er-map-iter (nth vs 0) (nth vs 1) (er-mk-nil))
|
||||
(er-mk-nil)))))
|
||||
|
||||
(define
|
||||
er-map-iter
|
||||
(fn
|
||||
(f lst acc)
|
||||
(cond
|
||||
(er-nil? lst) acc
|
||||
(er-cons? lst)
|
||||
(er-map-iter
|
||||
f
|
||||
(get lst :tail)
|
||||
(er-mk-cons (er-apply-fun f (list (get lst :head))) acc))
|
||||
:else (error "Erlang: lists:map: not a list"))))
|
||||
|
||||
(define
|
||||
er-bif-lists-foldl
|
||||
(fn
|
||||
(vs)
|
||||
(if
|
||||
(not (= (len vs) 3))
|
||||
(error "Erlang: lists:foldl: arity")
|
||||
(er-foldl-iter (nth vs 0) (nth vs 1) (nth vs 2)))))
|
||||
|
||||
(define
|
||||
er-foldl-iter
|
||||
(fn
|
||||
(f acc lst)
|
||||
(cond
|
||||
(er-nil? lst) acc
|
||||
(er-cons? lst)
|
||||
(er-foldl-iter
|
||||
f
|
||||
(er-apply-fun f (list (get lst :head) acc))
|
||||
(get lst :tail))
|
||||
:else (error "Erlang: lists:foldl: not a list"))))
|
||||
|
||||
;; ── io module ────────────────────────────────────────────────────
|
||||
(define er-io-buffer (list ""))
|
||||
(define er-io-flush! (fn () (set-nth! er-io-buffer 0 "")))
|
||||
(define er-io-buffer-content (fn () (nth er-io-buffer 0)))
|
||||
|
||||
(define
|
||||
er-bif-io-format
|
||||
(fn
|
||||
(vs)
|
||||
(let
|
||||
((s
|
||||
(cond
|
||||
(= (len vs) 1) (er-format-string (nth vs 0) (list))
|
||||
(= (len vs) 2)
|
||||
(er-format-string (nth vs 0) (er-list-to-sx-list (nth vs 1)))
|
||||
:else (error "Erlang: io:format: arity"))))
|
||||
(set-nth! er-io-buffer 0 (str (nth er-io-buffer 0) s))
|
||||
er-atom-ok)))
|
||||
|
||||
(define
|
||||
er-list-to-sx-list
|
||||
(fn
|
||||
(lst)
|
||||
(let
|
||||
((out (list)))
|
||||
(er-list-to-sx-collect lst out)
|
||||
out)))
|
||||
|
||||
(define
|
||||
er-list-to-sx-collect
|
||||
(fn
|
||||
(lst out)
|
||||
(cond
|
||||
(er-nil? lst) nil
|
||||
(er-cons? lst)
|
||||
(do
|
||||
(append! out (get lst :head))
|
||||
(er-list-to-sx-collect (get lst :tail) out))
|
||||
:else (error "Erlang: expected proper list"))))
|
||||
|
||||
;; ── format string rendering (~n, ~~, ~p, ~w, ~s) ────────────────
|
||||
(define
|
||||
er-format-string
|
||||
(fn (fmt args) (er-format-walk fmt 0 args 0 "")))
|
||||
|
||||
(define
|
||||
er-format-walk
|
||||
(fn
|
||||
(fmt i args ai out)
|
||||
(if
|
||||
(>= i (len fmt))
|
||||
out
|
||||
(let
|
||||
((c (char-at fmt i)))
|
||||
(cond
|
||||
(and (= c "~") (< (+ i 1) (len fmt)))
|
||||
(let
|
||||
((d (char-at fmt (+ i 1))))
|
||||
(cond
|
||||
(= d "n")
|
||||
(er-format-walk fmt (+ i 2) args ai (str out "\n"))
|
||||
(= d "~") (er-format-walk fmt (+ i 2) args ai (str out "~"))
|
||||
(or (= d "p") (= d "w") (= d "s"))
|
||||
(er-format-walk
|
||||
fmt
|
||||
(+ i 2)
|
||||
args
|
||||
(+ ai 1)
|
||||
(str out (er-format-value (nth args ai))))
|
||||
:else (er-format-walk
|
||||
fmt
|
||||
(+ i 2)
|
||||
args
|
||||
ai
|
||||
(str out "~" d))))
|
||||
:else (er-format-walk fmt (+ i 1) args ai (str out c)))))))
|
||||
|
||||
(define
|
||||
er-format-value
|
||||
(fn
|
||||
(v)
|
||||
(cond
|
||||
(= (type-of v) "number") (str v)
|
||||
(= (type-of v) "string") (str "\"" v "\"")
|
||||
(er-atom? v) (get v :name)
|
||||
(er-nil? v) "[]"
|
||||
(er-cons? v) (str "[" (er-format-list-elems v) "]")
|
||||
(er-tuple? v) (str "{" (er-format-tuple-elems (get v :elements)) "}")
|
||||
(er-fun? v) "#Fun"
|
||||
:else (str v))))
|
||||
|
||||
(define
|
||||
er-format-list-elems
|
||||
(fn
|
||||
(v)
|
||||
(cond
|
||||
(er-nil? v) ""
|
||||
(and (er-cons? v) (er-nil? (get v :tail)))
|
||||
(er-format-value (get v :head))
|
||||
(er-cons? v)
|
||||
(str
|
||||
(er-format-value (get v :head))
|
||||
","
|
||||
(er-format-list-elems (get v :tail)))
|
||||
:else (str "|" (er-format-value v)))))
|
||||
|
||||
(define
|
||||
er-format-tuple-elems
|
||||
(fn
|
||||
(elems)
|
||||
(if
|
||||
(= (len elems) 0)
|
||||
""
|
||||
(let
|
||||
((out (list (er-format-value (nth elems 0)))))
|
||||
(for-each
|
||||
(fn
|
||||
(i)
|
||||
(append! out ",")
|
||||
(append! out (er-format-value (nth elems i))))
|
||||
(range 1 (len elems)))
|
||||
(reduce str "" out)))))
|
||||
|
||||
@@ -60,8 +60,8 @@ Core mapping:
|
||||
- [x] `erlang-eval-ast`: evaluate sequential expressions — **54/54 tests**
|
||||
- [x] Pattern matching (atoms, numbers, vars, tuples, lists, `[H|T]`, underscore, bound-var re-match) — **21 new eval tests**; `case ... of ... end` wired
|
||||
- [x] Guards: `is_integer`, `is_atom`, `is_list`, `is_tuple`, comparisons, arithmetic — **20 new eval tests**; local-call dispatch wired
|
||||
- [ ] BIFs: `length/1`, `hd/1`, `tl/1`, `element/2`, `tuple_size/1`, `atom_to_list/1`, `list_to_atom/1`, `lists:map/2`, `lists:foldl/3`, `lists:reverse/1`, `io:format/1-2`
|
||||
- [ ] 30+ tests in `lib/erlang/tests/eval.sx`
|
||||
- [x] BIFs: `length/1`, `hd/1`, `tl/1`, `element/2`, `tuple_size/1`, `atom_to_list/1`, `list_to_atom/1`, `lists:map/2`, `lists:foldl/3`, `lists:reverse/1`, `io:format/1-2` — **35 new eval tests**; funs + closures wired
|
||||
- [x] 30+ tests in `lib/erlang/tests/eval.sx` — **130 tests green**
|
||||
|
||||
### Phase 3 — processes + mailboxes + receive (THE SHOWCASE)
|
||||
- [ ] Scheduler in `runtime.sx`: runnable queue, pid counter, per-process state record
|
||||
@@ -99,6 +99,7 @@ Core mapping:
|
||||
|
||||
_Newest first._
|
||||
|
||||
- **2026-04-24 core BIFs + funs green** — Phase 2 complete. Added to `lib/erlang/transpile.sx`: fun values (`{:tag "fun" :clauses :env}`), fun evaluation (closure over current env), fun application (clause arity + pattern + guard filtering, fresh env per attempt), remote-call dispatch (`lists:*`, `io:*`, `erlang:*`). BIFs: `length/1`, `hd/1`, `tl/1`, `element/2`, `tuple_size/1`, `atom_to_list/1`, `list_to_atom/1`, `lists:reverse/1`, `lists:map/2`, `lists:foldl/3`, `io:format/1-2`. `io:format` writes to a capture buffer (`er-io-buffer`, `er-io-flush!`, `er-io-buffer-content`) and returns `ok` — supports `~n`, `~p`/`~w`/`~s`, `~~`. 35 new eval tests. Total eval 130/130; erlang suite 244/244. **Phase 2 complete — Phase 3 (processes, scheduler, receive) is next.**
|
||||
- **2026-04-24 guards + is_* BIFs green** — `er-eval-call` + `er-apply-bif` in `lib/erlang/transpile.sx` wire local function calls to a BIF dispatcher. Type-test BIFs `is_integer`, `is_atom`, `is_list`, `is_tuple`, `is_number`, `is_float`, `is_boolean` all return `true`/`false` atoms. Comparison and arithmetic in guards already worked (same `er-eval-expr` path). 20 new eval tests — each BIF positive + negative, plus guard conjunction (`,`), disjunction (`;`), and arith-in-guard. Total eval 95/95; erlang suite 209/209.
|
||||
- **2026-04-24 pattern matching green** — `er-match!` in `lib/erlang/transpile.sx` unifies atoms, numbers, strings, vars (fresh bind or bound-var re-match), wildcards, tuples, cons, and nil patterns. `case ... of ... [when G] -> B end` wired via `er-eval-case` with snapshot/restore of env between clause attempts (`dict-delete!`-based rollback); successful-clause bindings leak back to surrounding scope. 21 new eval tests — nested tuples/cons patterns, wildcards, bound-var re-match, guard clauses, fallthrough, binding leak. Total eval 75/75; erlang suite 189/189.
|
||||
- **2026-04-24 eval (sequential) green** — `lib/erlang/transpile.sx` (tree-walking interpreter) + `lib/erlang/tests/eval.sx`. 54/54 tests covering literals, arithmetic, comparison, logical (incl. short-circuit `andalso`/`orelse`), tuples, lists with `++`, `begin..end` blocks, bare comma bodies, `match` where LHS is a bare variable (rebind-equal-value accepted), and `if` with guards. Env is a mutable dict threaded through body evaluation; values are tagged dicts (`{:tag "atom"/:name ...}`, `{:tag "nil"}`, `{:tag "cons" :head :tail}`, `{:tag "tuple" :elements}`). Numbers pass through as SX numbers. Gotcha: SX's `parse-number` coerces `"1.0"` → integer `1`, so `=:=` can't distinguish `1` from `1.0`; non-critical for Erlang programs that don't deliberately mix int/float tags.
|
||||
|
||||
Reference in New Issue
Block a user