|
|
|
|
@@ -853,112 +853,6 @@
|
|
|
|
|
(define er-modules-get (fn () (nth er-modules 0)))
|
|
|
|
|
(define er-modules-reset! (fn () (set-nth! er-modules 0 {})))
|
|
|
|
|
|
|
|
|
|
(define er-mk-module-slot
|
|
|
|
|
(fn (mod-env old-env version)
|
|
|
|
|
{:current mod-env :old old-env :version version :tag "module"}))
|
|
|
|
|
|
|
|
|
|
(define er-module-current-env (fn (slot) (get slot :current)))
|
|
|
|
|
(define er-module-old-env (fn (slot) (get slot :old)))
|
|
|
|
|
(define er-module-version (fn (slot) (get slot :version)))
|
|
|
|
|
|
|
|
|
|
;; ── FFI BIF registry (Phase 8) ───────────────────────────────────
|
|
|
|
|
;; Global dict from "Module/Name/Arity" key to {:module :name :arity :fn :pure?}.
|
|
|
|
|
;; Replaces the giant cond chain in transpile.sx#er-apply-remote-bif over time —
|
|
|
|
|
;; Phase 8 BIFs (crypto / cid / file / httpc / sqlite) all register here.
|
|
|
|
|
(define er-bif-registry (list {}))
|
|
|
|
|
(define er-bif-registry-get (fn () (nth er-bif-registry 0)))
|
|
|
|
|
(define er-bif-registry-reset! (fn () (set-nth! er-bif-registry 0 {})))
|
|
|
|
|
|
|
|
|
|
(define er-bif-key
|
|
|
|
|
(fn (module name arity)
|
|
|
|
|
(str module "/" name "/" arity)))
|
|
|
|
|
|
|
|
|
|
(define er-register-bif!
|
|
|
|
|
(fn (module name arity sx-fn)
|
|
|
|
|
(dict-set! (er-bif-registry-get) (er-bif-key module name arity)
|
|
|
|
|
{:module module :name name :arity arity :fn sx-fn :pure? false})
|
|
|
|
|
(er-mk-atom "ok")))
|
|
|
|
|
|
|
|
|
|
(define er-register-pure-bif!
|
|
|
|
|
(fn (module name arity sx-fn)
|
|
|
|
|
(dict-set! (er-bif-registry-get) (er-bif-key module name arity)
|
|
|
|
|
{:module module :name name :arity arity :fn sx-fn :pure? true})
|
|
|
|
|
(er-mk-atom "ok")))
|
|
|
|
|
|
|
|
|
|
(define er-lookup-bif
|
|
|
|
|
(fn (module name arity)
|
|
|
|
|
(let ((reg (er-bif-registry-get)) (k (er-bif-key module name arity)))
|
|
|
|
|
(if (dict-has? reg k) (get reg k) nil))))
|
|
|
|
|
|
|
|
|
|
(define er-list-bifs
|
|
|
|
|
(fn () (keys (er-bif-registry-get))))
|
|
|
|
|
|
|
|
|
|
;; ── term marshalling (Phase 8) ───────────────────────────────────
|
|
|
|
|
;; Bridge Erlang term values (tagged dicts) and SX-native values for
|
|
|
|
|
;; FFI BIFs to call out into platform primitives. Conversions:
|
|
|
|
|
;;
|
|
|
|
|
;; Erlang SX-native
|
|
|
|
|
;; ───────────────────────── ────────────────
|
|
|
|
|
;; atom {:tag "atom" :name S} ↔ symbol (make-symbol S)
|
|
|
|
|
;; nil {:tag "nil"} ↔ '()
|
|
|
|
|
;; cons {:tag "cons" :head :tail} → list of marshalled elements
|
|
|
|
|
;; tuple {:tag "tuple" :elements} → list of marshalled elements
|
|
|
|
|
;; binary {:tag "binary" :bytes} ↔ SX string
|
|
|
|
|
;; integer / float / boolean ↔ passthrough
|
|
|
|
|
;; SX string on the way back → binary
|
|
|
|
|
;;
|
|
|
|
|
;; Pids, refs, funs pass through unchanged — they have no SX-native
|
|
|
|
|
;; equivalent and are opaque to FFI primitives.
|
|
|
|
|
|
|
|
|
|
(define er-cons-to-sx-list
|
|
|
|
|
(fn (v)
|
|
|
|
|
(cond
|
|
|
|
|
(er-nil? v) (list)
|
|
|
|
|
(er-cons? v)
|
|
|
|
|
(let ((tail (er-cons-to-sx-list (get v :tail)))
|
|
|
|
|
(head (er-to-sx (get v :head))))
|
|
|
|
|
(let ((out (list head)))
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i) (append! out (nth tail i)))
|
|
|
|
|
(range 0 (len tail)))
|
|
|
|
|
out))
|
|
|
|
|
:else (list v))))
|
|
|
|
|
|
|
|
|
|
(define er-to-sx
|
|
|
|
|
(fn (v)
|
|
|
|
|
(cond
|
|
|
|
|
(er-atom? v) (make-symbol (get v :name))
|
|
|
|
|
(er-nil? v) (list)
|
|
|
|
|
(er-cons? v) (er-cons-to-sx-list v)
|
|
|
|
|
(er-tuple? v)
|
|
|
|
|
(let ((out (list)) (es (get v :elements)))
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i) (append! out (er-to-sx (nth es i))))
|
|
|
|
|
(range 0 (len es)))
|
|
|
|
|
out)
|
|
|
|
|
(er-binary? v) (list->string (map integer->char (get v :bytes)))
|
|
|
|
|
:else v)))
|
|
|
|
|
|
|
|
|
|
(define er-of-sx
|
|
|
|
|
(fn (v)
|
|
|
|
|
(let ((ty (type-of v)))
|
|
|
|
|
(cond
|
|
|
|
|
(= ty "symbol") (er-mk-atom (str v))
|
|
|
|
|
(= ty "string") (er-mk-binary (map char->integer (string->list v)))
|
|
|
|
|
(= ty "list")
|
|
|
|
|
(let ((out (er-mk-nil)))
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i)
|
|
|
|
|
(set! out
|
|
|
|
|
(er-mk-cons (er-of-sx (nth v (- (- (len v) 1) i))) out)))
|
|
|
|
|
(range 0 (len v)))
|
|
|
|
|
out)
|
|
|
|
|
(= ty "nil") (er-mk-nil)
|
|
|
|
|
:else v))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Load an Erlang module declaration. Source must start with
|
|
|
|
|
;; `-module(Name).` and contain function definitions. Functions
|
|
|
|
|
;; sharing a name (different arities) get their clauses concatenated
|
|
|
|
|
@@ -1003,15 +897,7 @@
|
|
|
|
|
((all-clauses (get by-name k)))
|
|
|
|
|
(er-env-bind! mod-env k (er-mk-fun all-clauses mod-env))))
|
|
|
|
|
(keys by-name))
|
|
|
|
|
(let ((registry (er-modules-get)))
|
|
|
|
|
(if (dict-has? registry mod-name)
|
|
|
|
|
(let ((existing-slot (get registry mod-name)))
|
|
|
|
|
(dict-set! registry mod-name
|
|
|
|
|
(er-mk-module-slot mod-env
|
|
|
|
|
(er-module-current-env existing-slot)
|
|
|
|
|
(+ (er-module-version existing-slot) 1))))
|
|
|
|
|
(dict-set! registry mod-name
|
|
|
|
|
(er-mk-module-slot mod-env nil 1))))
|
|
|
|
|
(dict-set! (er-modules-get) mod-name mod-env)
|
|
|
|
|
(er-mk-atom mod-name)))))
|
|
|
|
|
|
|
|
|
|
(define
|
|
|
|
|
@@ -1019,7 +905,7 @@
|
|
|
|
|
(fn
|
|
|
|
|
(mod name vs)
|
|
|
|
|
(let
|
|
|
|
|
((mod-env (er-module-current-env (get (er-modules-get) mod))))
|
|
|
|
|
((mod-env (get (er-modules-get) mod)))
|
|
|
|
|
(if
|
|
|
|
|
(not (dict-has? mod-env name))
|
|
|
|
|
(raise
|
|
|
|
|
@@ -1303,325 +1189,24 @@
|
|
|
|
|
:else (er-mk-atom "undefined")))
|
|
|
|
|
:else (error "Erlang: ets:info: arity"))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; ── file module (Phase 8 FFI) ────────────────────────────────────
|
|
|
|
|
;; Synchronous file IO. Filenames must be SX strings (or Erlang
|
|
|
|
|
;; binaries/char-code lists coercible to strings via er-source-to-string).
|
|
|
|
|
;; Returns `{ok, Binary}` / `ok` on success, `{error, Reason}` on failure
|
|
|
|
|
;; where Reason is one of `enoent`, `eacces`, `enotdir`, `posix_error`.
|
|
|
|
|
|
|
|
|
|
(define er-classify-file-error
|
|
|
|
|
(fn (msg)
|
|
|
|
|
(let ((s (str msg)))
|
|
|
|
|
(cond
|
|
|
|
|
(string-contains? s "No such") (er-mk-atom "enoent")
|
|
|
|
|
(string-contains? s "Permission denied") (er-mk-atom "eacces")
|
|
|
|
|
(string-contains? s "Not a directory") (er-mk-atom "enotdir")
|
|
|
|
|
(string-contains? s "Is a directory") (er-mk-atom "eisdir")
|
|
|
|
|
:else (er-mk-atom "posix_error")))))
|
|
|
|
|
|
|
|
|
|
(define er-bif-file-read-file
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((path (er-source-to-string (nth vs 0))))
|
|
|
|
|
(cond
|
|
|
|
|
(= path nil)
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error") (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((res (list nil)) (err (list nil)))
|
|
|
|
|
(guard (c (:else (set-nth! err 0 c)))
|
|
|
|
|
(set-nth! res 0 (file-read path)))
|
|
|
|
|
(cond
|
|
|
|
|
(not (= (nth err 0) nil))
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error")
|
|
|
|
|
(er-classify-file-error (nth err 0))))
|
|
|
|
|
:else
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "ok")
|
|
|
|
|
(er-mk-binary (map char->integer (string->list (nth res 0))))))))))))
|
|
|
|
|
|
|
|
|
|
(define er-bif-file-write-file
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((path (er-source-to-string (nth vs 0)))
|
|
|
|
|
(data (er-source-to-string (nth vs 1))))
|
|
|
|
|
(cond
|
|
|
|
|
(or (= path nil) (= data nil))
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error") (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((err (list nil)))
|
|
|
|
|
(guard (c (:else (set-nth! err 0 c)))
|
|
|
|
|
(file-write path data))
|
|
|
|
|
(cond
|
|
|
|
|
(not (= (nth err 0) nil))
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error")
|
|
|
|
|
(er-classify-file-error (nth err 0))))
|
|
|
|
|
:else (er-mk-atom "ok")))))))
|
|
|
|
|
|
|
|
|
|
(define er-bif-file-delete
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((path (er-source-to-string (nth vs 0))))
|
|
|
|
|
(cond
|
|
|
|
|
(= path nil)
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error") (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((err (list nil)))
|
|
|
|
|
(guard (c (:else (set-nth! err 0 c)))
|
|
|
|
|
(file-delete path))
|
|
|
|
|
(cond
|
|
|
|
|
(not (= (nth err 0) nil))
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error")
|
|
|
|
|
(er-classify-file-error (nth err 0))))
|
|
|
|
|
:else (er-mk-atom "ok")))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; ── crypto / cid / file:list_dir (Phase 8 FFI — host primitives) ──
|
|
|
|
|
;; Wired against loops/fed-prims host primitives (see plans Blockers
|
|
|
|
|
;; "RESOLVED 2026-05-18"). Term marshalling at the boundary:
|
|
|
|
|
;; Erlang binary/string/charlist -> SX byte-string via er-source-to-string;
|
|
|
|
|
;; results -> Erlang binary via er-mk-binary.
|
|
|
|
|
|
|
|
|
|
(define er-hexval
|
|
|
|
|
(fn (c)
|
|
|
|
|
(let ((v (char->integer c)))
|
|
|
|
|
(cond
|
|
|
|
|
(and (>= v 48) (<= v 57)) (- v 48) ;; 0-9
|
|
|
|
|
(and (>= v 97) (<= v 102)) (+ 10 (- v 97)) ;; a-f
|
|
|
|
|
(and (>= v 65) (<= v 70)) (+ 10 (- v 65)) ;; A-F
|
|
|
|
|
:else 0))))
|
|
|
|
|
|
|
|
|
|
(define er-hex->bytes
|
|
|
|
|
(fn (hex)
|
|
|
|
|
(let ((cs (string->list hex)) (out (list)) (n (string-length hex)))
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i)
|
|
|
|
|
(append! out
|
|
|
|
|
(+ (* 16 (er-hexval (nth cs (* i 2))))
|
|
|
|
|
(er-hexval (nth cs (+ (* i 2) 1))))))
|
|
|
|
|
(range 0 (truncate (/ n 2))))
|
|
|
|
|
out)))
|
|
|
|
|
|
|
|
|
|
;; crypto:hash(Type, Data) -> raw digest binary. Type is an Erlang
|
|
|
|
|
;; atom (sha256 | sha512 | sha3_256). Bad type / non-binary -> badarg.
|
|
|
|
|
(define er-bif-crypto-hash
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((ty (nth vs 0)) (data (er-source-to-string (nth vs 1))))
|
|
|
|
|
(cond
|
|
|
|
|
(or (not (er-atom? ty)) (= data nil))
|
|
|
|
|
(raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((name (get ty :name)))
|
|
|
|
|
(let ((hex (cond
|
|
|
|
|
(= name "sha256") (crypto-sha256 data)
|
|
|
|
|
(= name "sha512") (crypto-sha512 data)
|
|
|
|
|
(= name "sha3_256") (crypto-sha3-256 data)
|
|
|
|
|
:else nil)))
|
|
|
|
|
(cond
|
|
|
|
|
(= hex nil) (raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else (er-mk-binary (er-hex->bytes hex)))))))))
|
|
|
|
|
|
|
|
|
|
;; cid:from_bytes(Bin) -> CIDv1 (raw codec 0x55, sha2-256 multihash)
|
|
|
|
|
;; as an Erlang binary string.
|
|
|
|
|
(define er-bif-cid-from-bytes
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((data (er-source-to-string (nth vs 0))))
|
|
|
|
|
(cond
|
|
|
|
|
(= data nil) (raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((digest (er-hex->bytes (crypto-sha256 data))))
|
|
|
|
|
(let ((mh (list->string
|
|
|
|
|
(map integer->char (append (list 18 32) digest)))))
|
|
|
|
|
(er-mk-binary
|
|
|
|
|
(map char->integer
|
|
|
|
|
(string->list (cid-from-bytes 85 mh))))))))))
|
|
|
|
|
|
|
|
|
|
;; cid:to_string(Term) -> canonical CIDv1 (dag-cbor) of the term,
|
|
|
|
|
;; as an Erlang binary string.
|
|
|
|
|
(define er-bif-cid-to-string
|
|
|
|
|
(fn (vs)
|
|
|
|
|
;; Canonical CID of the term's stable string form. (cbor-encode
|
|
|
|
|
;; rejects symbols, so er-to-sx of compound terms is unencodable;
|
|
|
|
|
;; er-format-value yields a canonical SX string per term value.)
|
|
|
|
|
(er-mk-binary
|
|
|
|
|
(map char->integer
|
|
|
|
|
(string->list (cid-from-sx (er-format-value (nth vs 0))))))))
|
|
|
|
|
|
|
|
|
|
;; file:list_dir(Path) -> {ok, [Binary]} | {error, Reason}
|
|
|
|
|
(define er-bif-file-list-dir
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((path (er-source-to-string (nth vs 0))))
|
|
|
|
|
(cond
|
|
|
|
|
(= path nil)
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error") (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((res (list nil)) (err (list nil)))
|
|
|
|
|
(guard (c (:else (set-nth! err 0 c)))
|
|
|
|
|
(set-nth! res 0 (file-list-dir path)))
|
|
|
|
|
(cond
|
|
|
|
|
(not (= (nth err 0) nil))
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "error")
|
|
|
|
|
(er-classify-file-error (nth err 0))))
|
|
|
|
|
:else
|
|
|
|
|
(er-mk-tuple (list (er-mk-atom "ok")
|
|
|
|
|
(er-of-sx (nth res 0))))))))))
|
|
|
|
|
|
|
|
|
|
;; ── builtin BIF registrations (Phase 8 migration) ────────────────
|
|
|
|
|
;; Populates `er-bif-registry` with every existing built-in BIF. Each
|
|
|
|
|
;; entry is keyed by "Module/Name/Arity"; multi-arity BIFs register
|
|
|
|
|
;; once per arity. Called eagerly at the end of runtime.sx so the
|
|
|
|
|
;; registry is ready before any erlang-eval-ast call.
|
|
|
|
|
(define er-register-builtin-bifs!
|
|
|
|
|
(fn ()
|
|
|
|
|
;; erlang module — type predicates (all pure)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_integer" 1 er-bif-is-integer)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_atom" 1 er-bif-is-atom)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_list" 1 er-bif-is-list)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_tuple" 1 er-bif-is-tuple)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_number" 1 er-bif-is-number)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_float" 1 er-bif-is-float)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_boolean" 1 er-bif-is-boolean)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_pid" 1 er-bif-is-pid)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_reference" 1 er-bif-is-reference)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_binary" 1 er-bif-is-binary)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_function" 1 er-bif-is-function)
|
|
|
|
|
(er-register-pure-bif! "erlang" "is_function" 2 er-bif-is-function)
|
|
|
|
|
;; erlang module — pure data ops
|
|
|
|
|
(er-register-pure-bif! "erlang" "length" 1 er-bif-length)
|
|
|
|
|
(er-register-pure-bif! "erlang" "hd" 1 er-bif-hd)
|
|
|
|
|
(er-register-pure-bif! "erlang" "tl" 1 er-bif-tl)
|
|
|
|
|
(er-register-pure-bif! "erlang" "element" 2 er-bif-element)
|
|
|
|
|
(er-register-pure-bif! "erlang" "tuple_size" 1 er-bif-tuple-size)
|
|
|
|
|
(er-register-pure-bif! "erlang" "byte_size" 1 er-bif-byte-size)
|
|
|
|
|
(er-register-pure-bif! "erlang" "atom_to_list" 1 er-bif-atom-to-list)
|
|
|
|
|
(er-register-pure-bif! "erlang" "list_to_atom" 1 er-bif-list-to-atom)
|
|
|
|
|
(er-register-pure-bif! "erlang" "abs" 1 er-bif-abs)
|
|
|
|
|
(er-register-pure-bif! "erlang" "min" 2 er-bif-min)
|
|
|
|
|
(er-register-pure-bif! "erlang" "max" 2 er-bif-max)
|
|
|
|
|
(er-register-pure-bif! "erlang" "tuple_to_list" 1 er-bif-tuple-to-list)
|
|
|
|
|
(er-register-pure-bif! "erlang" "list_to_tuple" 1 er-bif-list-to-tuple)
|
|
|
|
|
(er-register-pure-bif! "erlang" "integer_to_list" 1 er-bif-integer-to-list)
|
|
|
|
|
(er-register-pure-bif! "erlang" "list_to_integer" 1 er-bif-list-to-integer)
|
|
|
|
|
;; erlang module — process / runtime (side-effecting)
|
|
|
|
|
(er-register-bif! "erlang" "self" 0 er-bif-self)
|
|
|
|
|
(er-register-bif! "erlang" "spawn" 1 er-bif-spawn)
|
|
|
|
|
(er-register-bif! "erlang" "spawn" 3 er-bif-spawn)
|
|
|
|
|
(er-register-bif! "erlang" "exit" 1 er-bif-exit)
|
|
|
|
|
(er-register-bif! "erlang" "exit" 2 er-bif-exit)
|
|
|
|
|
(er-register-bif! "erlang" "make_ref" 0 er-bif-make-ref)
|
|
|
|
|
(er-register-bif! "erlang" "link" 1 er-bif-link)
|
|
|
|
|
(er-register-bif! "erlang" "unlink" 1 er-bif-unlink)
|
|
|
|
|
(er-register-bif! "erlang" "monitor" 2 er-bif-monitor)
|
|
|
|
|
(er-register-bif! "erlang" "demonitor" 1 er-bif-demonitor)
|
|
|
|
|
(er-register-bif! "erlang" "process_flag" 2 er-bif-process-flag)
|
|
|
|
|
(er-register-bif! "erlang" "register" 2 er-bif-register)
|
|
|
|
|
(er-register-bif! "erlang" "unregister" 1 er-bif-unregister)
|
|
|
|
|
(er-register-bif! "erlang" "whereis" 1 er-bif-whereis)
|
|
|
|
|
(er-register-bif! "erlang" "registered" 0 er-bif-registered)
|
|
|
|
|
;; erlang module — exception raising (modelled as side-effecting)
|
|
|
|
|
(er-register-bif! "erlang" "throw" 1
|
|
|
|
|
(fn (vs) (raise (er-mk-throw-marker (er-bif-arg1 vs "throw")))))
|
|
|
|
|
(er-register-bif! "erlang" "error" 1
|
|
|
|
|
(fn (vs) (raise (er-mk-error-marker (er-bif-arg1 vs "error")))))
|
|
|
|
|
;; lists module — all pure
|
|
|
|
|
(er-register-pure-bif! "lists" "reverse" 1 er-bif-lists-reverse)
|
|
|
|
|
(er-register-pure-bif! "lists" "map" 2 er-bif-lists-map)
|
|
|
|
|
(er-register-pure-bif! "lists" "foldl" 3 er-bif-lists-foldl)
|
|
|
|
|
(er-register-pure-bif! "lists" "seq" 2 er-bif-lists-seq)
|
|
|
|
|
(er-register-pure-bif! "lists" "seq" 3 er-bif-lists-seq)
|
|
|
|
|
(er-register-pure-bif! "lists" "sum" 1 er-bif-lists-sum)
|
|
|
|
|
(er-register-pure-bif! "lists" "nth" 2 er-bif-lists-nth)
|
|
|
|
|
(er-register-pure-bif! "lists" "last" 1 er-bif-lists-last)
|
|
|
|
|
(er-register-pure-bif! "lists" "member" 2 er-bif-lists-member)
|
|
|
|
|
(er-register-pure-bif! "lists" "append" 2 er-bif-lists-append)
|
|
|
|
|
(er-register-pure-bif! "lists" "filter" 2 er-bif-lists-filter)
|
|
|
|
|
(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)
|
|
|
|
|
;; 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)
|
|
|
|
|
;; ets module — side-effecting (mutates table state)
|
|
|
|
|
(er-register-bif! "ets" "new" 2 er-bif-ets-new)
|
|
|
|
|
(er-register-bif! "ets" "insert" 2 er-bif-ets-insert)
|
|
|
|
|
(er-register-bif! "ets" "lookup" 2 er-bif-ets-lookup)
|
|
|
|
|
(er-register-bif! "ets" "delete" 1 er-bif-ets-delete)
|
|
|
|
|
(er-register-bif! "ets" "delete" 2 er-bif-ets-delete)
|
|
|
|
|
(er-register-bif! "ets" "tab2list" 1 er-bif-ets-tab2list)
|
|
|
|
|
(er-register-bif! "ets" "info" 2 er-bif-ets-info)
|
|
|
|
|
;; code module — side-effecting (mutates module registry, kills procs)
|
|
|
|
|
(er-register-bif! "code" "load_binary" 3 er-bif-code-load-binary)
|
|
|
|
|
(er-register-bif! "code" "purge" 1 er-bif-code-purge)
|
|
|
|
|
(er-register-bif! "code" "soft_purge" 1 er-bif-code-soft-purge)
|
|
|
|
|
(er-register-bif! "code" "which" 1 er-bif-code-which)
|
|
|
|
|
(er-register-bif! "code" "is_loaded" 1 er-bif-code-is-loaded)
|
|
|
|
|
(er-register-bif! "code" "all_loaded" 0 er-bif-code-all-loaded)
|
|
|
|
|
;; file module
|
|
|
|
|
(er-register-bif! "file" "read_file" 1 er-bif-file-read-file)
|
|
|
|
|
(er-register-bif! "file" "write_file" 2 er-bif-file-write-file)
|
|
|
|
|
(er-register-bif! "file" "delete" 1 er-bif-file-delete)
|
|
|
|
|
;; Phase 8 FFI — host-primitive BIFs (loops/fed-prims)
|
|
|
|
|
(er-register-pure-bif! "crypto" "hash" 2 er-bif-crypto-hash)
|
|
|
|
|
(er-register-pure-bif! "cid" "from_bytes" 1 er-bif-cid-from-bytes)
|
|
|
|
|
(er-register-pure-bif! "cid" "to_string" 1 er-bif-cid-to-string)
|
|
|
|
|
|
|
|
|
|
;; ── binary_to_list / list_to_binary (Step 3b — term codec) ──────
|
|
|
|
|
;; Standard Erlang semantics:
|
|
|
|
|
;; binary_to_list(<<B1,B2,...>>) -> [B1, B2, ...] (Erlang cons of ints)
|
|
|
|
|
;; list_to_binary(IoList) -> <<...>> (flattens nested
|
|
|
|
|
;; iolists; elements are byte ints 0-255 or binaries)
|
|
|
|
|
;; Bad arg / out-of-range byte / non-iolist element -> error:badarg.
|
|
|
|
|
|
|
|
|
|
(define er-bif-binary-to-list
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((v (nth vs 0)))
|
|
|
|
|
(cond
|
|
|
|
|
(not (er-binary? v))
|
|
|
|
|
(raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(let ((bs (get v :bytes)) (out (er-mk-nil)))
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i)
|
|
|
|
|
(set! out (er-mk-cons (nth bs (- (- (len bs) 1) i)) out)))
|
|
|
|
|
(range 0 (len bs)))
|
|
|
|
|
out)))))
|
|
|
|
|
|
|
|
|
|
;; Walk an Erlang iolist, appending bytes to `acc` (a mutable SX list).
|
|
|
|
|
;; Accepts: nil, cons-of-X, binary, integer in 0..255. Anything else
|
|
|
|
|
;; signals failure by setting (nth fail 0) to true.
|
|
|
|
|
(define er-iolist-walk!
|
|
|
|
|
(fn (v acc fail)
|
|
|
|
|
(define
|
|
|
|
|
er-apply-ets-bif
|
|
|
|
|
(fn
|
|
|
|
|
(name vs)
|
|
|
|
|
(cond
|
|
|
|
|
(nth fail 0) nil
|
|
|
|
|
(er-nil? v) nil
|
|
|
|
|
(er-cons? v)
|
|
|
|
|
(do (er-iolist-walk! (get v :head) acc fail)
|
|
|
|
|
(er-iolist-walk! (get v :tail) acc fail))
|
|
|
|
|
(er-binary? v)
|
|
|
|
|
(for-each
|
|
|
|
|
(fn (i) (append! acc (nth (get v :bytes) i)))
|
|
|
|
|
(range 0 (len (get v :bytes))))
|
|
|
|
|
(= (type-of v) "number")
|
|
|
|
|
(cond
|
|
|
|
|
(and (>= v 0) (<= v 255)) (append! acc v)
|
|
|
|
|
:else (set-nth! fail 0 true))
|
|
|
|
|
:else (set-nth! fail 0 true))))
|
|
|
|
|
(= name "new") (er-bif-ets-new vs)
|
|
|
|
|
(= name "insert") (er-bif-ets-insert vs)
|
|
|
|
|
(= name "lookup") (er-bif-ets-lookup vs)
|
|
|
|
|
(= name "delete") (er-bif-ets-delete vs)
|
|
|
|
|
(= name "tab2list") (er-bif-ets-tab2list vs)
|
|
|
|
|
(= name "info") (er-bif-ets-info vs)
|
|
|
|
|
:else (error
|
|
|
|
|
(str "Erlang: undefined 'ets:" name "/" (len vs) "'")))))
|
|
|
|
|
|
|
|
|
|
(define er-bif-list-to-binary
|
|
|
|
|
(fn (vs)
|
|
|
|
|
(let ((v (nth vs 0)) (acc (list)) (fail (list false)))
|
|
|
|
|
(cond
|
|
|
|
|
(not (or (er-nil? v) (er-cons? v) (er-binary? v)))
|
|
|
|
|
(raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else
|
|
|
|
|
(do
|
|
|
|
|
(er-iolist-walk! v acc fail)
|
|
|
|
|
(cond
|
|
|
|
|
(nth fail 0)
|
|
|
|
|
(raise (er-mk-error-marker (er-mk-atom "badarg")))
|
|
|
|
|
:else (er-mk-binary acc)))))))
|
|
|
|
|
|
|
|
|
|
(er-register-bif! "file" "list_dir" 1 er-bif-file-list-dir)
|
|
|
|
|
(er-register-pure-bif! "erlang" "binary_to_list" 1 er-bif-binary-to-list)
|
|
|
|
|
(er-register-pure-bif! "erlang" "list_to_binary" 1 er-bif-list-to-binary)
|
|
|
|
|
(er-mk-atom "ok")))
|
|
|
|
|
|
|
|
|
|
;; Register everything at load time.
|
|
|
|
|
(er-register-builtin-bifs!)
|
|
|
|
|
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
|
|
|
|
;; The Erlang evaluator (er-eval-* in transpile.sx + the vm/dispatcher) recurses
|
|
|
|
|
;; over the AST and the scheduler/receive path captures call/cc continuations.
|
|
|
|
|
;; Under JIT the recursive eval miscompiles into a non-terminating loop and the
|
|
|
|
|
;; continuation path cannot transfer control. Exclude the whole er-/erlang-
|
|
|
|
|
;; namespace (covers transpile, runtime, and vm/dispatcher in one declaration).
|
|
|
|
|
(jit-exclude! "er-*" "erlang-*")
|
|
|
|
|
|