Loading repository data…
Loading repository data…
MenkeTechnologies / repository
Emacs Lisp interpreter in Rust — a fusevm language frontend (sibling of awkrs / vimlrs / zshrs), built on the rust_lisp reader with a Lisp-2 obarray, dynamic binding, and an owned evaluator + standard library.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
███████╗██╗ ██╗███████╗██████╗ ██████╗ ███████╗
██╔════╝██║ ██║██╔════╝██╔══██╗██╔══██╗██╔════╝
█████╗ ██║ ██║███████╗██████╔╝██████╔╝███████╗
██╔══╝ ██║ ██║╚════██║██╔═══╝ ██╔══██╗╚════██║
███████╗███████╗██║███████║██║ ██║ ██║███████║
╚══════╝╚══════╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝╚══════╝
[EMACS LISP // RUN .EL OUTSIDE EMACS // LISP-2 + DYNAMIC SCOPE // RUST CORE]"The editor's language — without the editor."
elisprs runs Emacs Lisp (.el) as standalone programs from the command line: a Lisp-2 obarray (separate value/function cells) with lexical and dynamic binding and an elisp-correct reader, compiled to — and run on — the fusevm bytecode VM, the same engine behind zshrs, stryke, awkrs, and vimlrs. elisprs is a pure frontend: no bespoke VM or JIT — each form lowers to a fusevm::Chunk, hot arithmetic/comparison lowers to native fusevm ops (JIT/AOT-able), and the elisp object heap rides the VM as Value::Obj handles reached through fusevm's extension handler. It AOT-compiles to standalone native binaries (--aot-exe) and caches lowered bytecode in an rkyv shard at ~/.elisprs.
┌──────────────────────────────────────────────────────────────┐ │ ENGINE: FUSEVM FRONTEND: PURE AOT: STANDALONE BIN CACHE: RKYV │ └──────────────────────────────────────────────────────────────┘
Read the Docs · Engineering Report · Builtin Reference · strykelang · zshrs · fusevmPositioning: Emacs Lisp has only ever run inside Emacs. elisprs takes the language out of the editor and runs .el as ordinary programs — with a REPL, no Emacs process required. It is built to become the fifth language hosted on fusevm, after zshrs, stryke, awkrs, and vimlrs.
Why it's built this way: Emacs Lisp is a Lisp-2 (every symbol carries a separate value cell and function cell) and supports both lexical and dynamic scoping. Those facts are the whole personality of the language, so elisprs owns the value model and the semantics, and leans on fusevm purely for execution:
| Layer | Where |
|---|---|
| Value model — interned symbols, real cons cells (dotted), vectors, hash tables, closures | ours — an ElispHost object heap; objects ride the VM as Value::Obj(u32) handles |
Reader (1+/1-, #'foo, ?c, :kw, backquote, dotted pairs) | ours — an elisp-correct S-expression reader |
| Lisp-2 obarray, lexical+dynamic binding, special forms, macros, subrs | ours — src/host.rs + src/compiler.rs |
| Bytecode execution, JIT, AOT | fusevm — elisprs has no VM/JIT of its own |
Status: self-hosting elisp on fusevm. Each top-level form is read, macro-expanded, and lowered to a fusevm::Chunk (src/compiler.rs); fusevm executes it and calls back into the object heap (src/host.rs) through a registered extension handler. Core arithmetic/comparison lower to native fusevm ops so hot loops are JIT/AOT-able; --aot-exe emits standalone native binaries; lowered bytecode + a heap image are cached in an rkyv shard at ~/.elisprs. (An earlier bootstrap built on the rust_lisp crate; it was replaced by this own value model — rust_lisp is no longer a dependency.)
rustc 1.96+.fusevm (the bytecode VM elisp executes on, with jit-disk-cache + aot), rkyv + bincode (the ~/.elisprs bytecode cache), serde/serde_json, and lsp-server/lsp-types (the --lsp server). No rust_lisp.git clone https://github.com/MenkeTechnologies/elisprs # from source
cd elisprs && cargo build --release
The build produces the elisp binary:
elisp FILE.el # evaluate a file
elisp -e "(+ 1 2)" # evaluate an expression, print its value
elisp # REPL — reedline editor on a TTY, plain line reader when piped
elisp --repl # force the reedline REPL: Tab-completion, live stats banner, ~/.elisprs/history
elisp --lsp # language server over stdio (completion/hover/diagnostics/signature help)
elisp --dap # debug adapter over stdio (breakpoints/stepping/variables)
elisp --aot FILE -o a.o # AOT-compile to a native object via fusevm::aot
elisp --aot-exe FILE -o a.out # AOT-compile to a standalone native executable
elisp --version
Reader syntax. integers, floats, strings (with escapes), symbols (including 1+ / 1- / <= / :keywords), nil / t, 'quote, #'function, ?c char literals, ; comments.
Special forms (21). quote function lambda progn prog1 if when unless cond and or while setq let let* defun defmacro defvar defconst condition-case unwind-protect.
Subrs (~90).
| Group | Functions |
|---|---|
| Arithmetic | + - * / % mod 1+ 1- abs max min = /= < > <= >= |
| Lists | car cdr cons list append nth nthcdr reverse length member memq assoc assq member-ignore-case |
| c*r combinators | caar cadr cdar cddr caadr cadar cdaar cdadr cddar (+ cl-caar cl-cadr cl-cdar cl-cddr) |
| Mutation | setcar setcdr aset |
| Vectors | vector make-vector aref vectorp |
| Records | record make-record recordp (a distinct type — slot 0 is the type symbol, vectorp is nil; backs cl-defstruct) |
| Bool-vectors | make-bool-vector bool-vector bool-vector-p bool-vector-count-population bool-vector-subsetp bool-vector-not (#&N"…" syntax) |
| Advice (nadvice) | advice-add advice-remove add-function remove-function define-advice advice-member-p (all :around/:before/:after/:override/:filter-*/:*-while/:*-until combinators) |
| Hash tables | make-hash-table gethash puthash remhash clrhash maphash hash-table-count hash-table-keys hash-table-values hash-table-p |
| Predicates | eq eql equal null not numberp integerp floatp stringp symbolp consp listp atom functionp |
| Symbols/cells | set symbol-value symbol-function fset boundp fboundp symbol-name intern make-symbol |
| Strings | concat string= string-equal string< upcase downcase number-to-string string-to-number string-split |
| IO/format | format message princ prin1 prin1-to-string print terpri |
| Functional | funcall apply mapcar mapc sort identity |
| Regexp | string-match string-match-p match-beginning match-end match-string match-data set-match-data replace-regexp-in-string regexp-quote (+ save-match-data) |
| Markers | make-marker point-marker copy-marker set-marker move-marker marker-position marker-buffer markerp marker-insertion-type set-marker-insertion-type |
defun/defmacro/lambda support &optional and &rest; macros expand and re-evaluate; condition-case matches the error umbrella and specific error symbols.
A taste (the examples/ directory has runnable, self-testing ERT versions — elisp examples/demo.el):
(defun fact (n) (if (<= n 1) 1 (* n (fact (1- n)))))
(fact 6) ; => 720
(mapcar (lambda (x) (* x x)) '(1 2 3 4)) ; => (1 4 9 16)
(mapcar #'1+ '(10 20 30)) ; => (11 21 31)
(let ((x 10) (y 20)) (+ x y)) ; => 30
(format "%s = %d (hex %x)" 'count 255 255); => "count = 255 (hex ff)"
(condition-case e (/ 1 0)
(arith-error (format "caught %s" e))) ; => "caught (arith-error division by zero)"
Now supported (own cons model — Obj::Cons(Value, Value) heap cells, not rust_lisp's list-only cdr):
(cons 1 2) / (a . b) read, print ((1 . 2)), and round-trip; alists may use (key . value).`, ,, and ,@ are read and expanded.setcar / setcdr mutate cons cells in place.pcase. Structural dispatch over _, literals, 'x, symbol binders, (pred FN), (guard EXPR), (and …), (or …), and backquote patterns `(,a ,b) / `(,a . ,rest) (incl. nested), recognized from the reader's eager backquote expansion.string-match & friends translate elisp regexp syntax (\( \| \{, \</\>, \w/\s-, backreferences \1..\9) to a backing engine, honor case-fold-search, and record char-indexed match data; replace-regexp-in-string is the subr.el Lisp definition (function-valued REP, \&/\N templates, FIXEDCASE/LITERAL/SUBEXP/START).[1 2 3] reads as a self-evaluating vector (elements unevaluated); aref / elt / length / append / sort operate on it.setf over the common places: car, cdr, nth, elt, aref, gethash, symbol-value, plus plain variables and multiple place/value pairs.format field specs. %[-][0][width][.prec] with s S d o x X c e f g, e.g. (format "%05d" 42) → 00042.Scope. Both lexical (lexical-binding: t) and dynamic binding are honored — lexical closures capture their defining environment, while defvar / special variables bind dynamically.
Buffers & text editing. A global registry of named live buffers, each with its own text, 1-based point, mark, and narrowing bounds. current-buffer / set-buffer / get-buffer-create / generate-new-buffer / kill-buffer / rename-buffer / buffer-list manage the registry; insert / delete-region / delete-char / point motion (forward-char, forward-line, beginning-of-line, …) / narrow-to-region / widen edit the current buffer. save-excursion restores point via a marker that tracks intervening edits; save-restriction / with-temp-buffer / with-current-buffer scope the buffer and its restriction. Buffer-local variables key off the current buffer.
Markers & text properties. First-class markers (make-marker / point-marker / copy-marker / set-marker / marker-position / marker-buffer / marker-insertion-type) auto-adjust when text is inserted or deleted, honoring insertion type, and serve as buffer positions. String and buffer text carry property intervals: propertize / `put-text-
| Text properties | propertize put-text-property get-text-property set-text-properties add-text-properties remove-text-properties text-properties-at next-single-property-change next-property-change previous-single-property-change get-char-property |