erlang: guard BIFs + call dispatch (+20 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-24 18:08:48 +00:00
parent 4965be71ca
commit 7f4fb9c3ed
3 changed files with 155 additions and 1 deletions

View File

@@ -174,6 +174,42 @@
(ev "case {ok, 7} of {ok, X} -> X end + 1")
8)
;; ── guard BIFs (is_*) ────────────────────────────────────────────
(er-eval-test "is_integer 42" (nm (ev "is_integer(42)")) "true")
(er-eval-test "is_integer ok" (nm (ev "is_integer(ok)")) "false")
(er-eval-test "is_atom ok" (nm (ev "is_atom(ok)")) "true")
(er-eval-test "is_atom int" (nm (ev "is_atom(42)")) "false")
(er-eval-test "is_list cons" (nm (ev "is_list([1,2])")) "true")
(er-eval-test "is_list nil" (nm (ev "is_list([])")) "true")
(er-eval-test "is_list tuple" (nm (ev "is_list({1,2})")) "false")
(er-eval-test "is_tuple tuple" (nm (ev "is_tuple({ok,1})")) "true")
(er-eval-test "is_tuple list" (nm (ev "is_tuple([1])")) "false")
(er-eval-test "is_number int" (nm (ev "is_number(42)")) "true")
(er-eval-test "is_number atom" (nm (ev "is_number(foo)")) "false")
(er-eval-test "is_boolean true" (nm (ev "is_boolean(true)")) "true")
(er-eval-test "is_boolean false" (nm (ev "is_boolean(false)")) "true")
(er-eval-test "is_boolean atom" (nm (ev "is_boolean(foo)")) "false")
;; ── guard BIFs wired into case / if ─────────────────────────────
(er-eval-test "guard is_integer pick"
(nm (ev "case 5 of N when is_integer(N) -> int; _ -> other end"))
"int")
(er-eval-test "guard is_integer reject"
(nm (ev "case foo of N when is_integer(N) -> int; _ -> other end"))
"other")
(er-eval-test "guard is_atom"
(nm (ev "case foo of X when is_atom(X) -> atom_yes; _ -> no end"))
"atom_yes")
(er-eval-test "guard conjunction"
(nm (ev "case 5 of N when is_integer(N), N > 0 -> pos; _ -> np end"))
"pos")
(er-eval-test "guard disjunction (if)"
(nm (ev "X = foo, if is_integer(X); is_atom(X) -> yes; true -> no end"))
"yes")
(er-eval-test "guard arith"
(nm (ev "case 3 of N when N * 2 > 5 -> big; _ -> small end"))
"big")
(define
er-eval-test-summary
(str "eval " er-eval-test-pass "/" er-eval-test-count))

View File

@@ -96,6 +96,7 @@
(= ty "block") (er-eval-body (get node :exprs) env)
(= ty "if") (er-eval-if node env)
(= ty "case") (er-eval-case node env)
(= ty "call") (er-eval-call node env)
(= ty "match") (er-eval-match node env)
:else (error (str "Erlang eval: unsupported node type '" ty "'"))))))
@@ -439,3 +440,119 @@
(er-truthy? (er-eval-expr (nth conj i) env))
(er-eval-guard-conj-iter conj (+ i 1) env)
false))))
;; ── function calls ───────────────────────────────────────────────
(define
er-eval-call
(fn
(node env)
(let
((fun (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")))))
(define
er-eval-args
(fn
(args env)
(let
((out (list)))
(for-each
(fn (i) (append! out (er-eval-expr (nth args i) env)))
(range 0 (len args)))
out)))
(define
er-eval-local-call
(fn
(name args env)
(let
((vs (er-eval-args args env)))
(er-apply-bif name vs))))
(define
er-eval-remote-call
(fn
(mod name args env)
(error
(str "Erlang: undefined function '" mod ":" name "/" (len args) "'"))))
;; ── BIFs ─────────────────────────────────────────────────────────
(define
er-apply-bif
(fn
(name vs)
(cond
(= name "is_integer") (er-bif-is-integer vs)
(= name "is_atom") (er-bif-is-atom vs)
(= name "is_list") (er-bif-is-list vs)
(= name "is_tuple") (er-bif-is-tuple vs)
(= name "is_number") (er-bif-is-number vs)
(= name "is_float") (er-bif-is-float vs)
(= name "is_boolean") (er-bif-is-boolean vs)
:else (error
(str "Erlang: undefined function '" name "/" (len vs) "'")))))
(define
er-bif-arg1
(fn
(vs name)
(if
(= (len vs) 1)
(nth vs 0)
(error (str "Erlang: " name ": wrong arity")))))
(define
er-bif-is-integer
(fn
(vs)
(let
((v (er-bif-arg1 vs "is_integer")))
(er-bool (and (= (type-of v) "number") (integer? v))))))
(define
er-bif-is-atom
(fn (vs) (er-bool (er-atom? (er-bif-arg1 vs "is_atom")))))
(define
er-bif-is-list
(fn
(vs)
(let
((v (er-bif-arg1 vs "is_list")))
(er-bool (or (er-nil? v) (er-cons? v))))))
(define
er-bif-is-tuple
(fn (vs) (er-bool (er-tuple? (er-bif-arg1 vs "is_tuple")))))
(define
er-bif-is-number
(fn
(vs)
(er-bool (= (type-of (er-bif-arg1 vs "is_number")) "number"))))
(define
er-bif-is-float
(fn
(vs)
(let
((v (er-bif-arg1 vs "is_float")))
(er-bool (and (= (type-of v) "number") (not (integer? v)))))))
(define
er-bif-is-boolean
(fn
(vs)
(let
((v (er-bif-arg1 vs "is_boolean")))
(er-bool
(or (er-is-atom-named? v "true") (er-is-atom-named? v "false"))))))