// ELISPRS — EMACS LISP IN RUST

elisprs v0.1.2 · Emacs Lisp · Lisp-2 obarray + lexical & dynamic binding · elisp-correct reader · compiled to fusevm bytecode (JIT/AOT) · MIT

Report GitHub Issues
// Color scheme

>_ELISPRS REFERENCE

An Emacs Lisp runtime in Rust that runs .el outside Emacs. A Lisp-2 obarray (separate value/function cells) with lexical and dynamic binding and an elisp-correct reader that lowers to fusevm::Chunk and executes on the fusevm bytecode VM — the same Cranelift-JIT/AOT engine behind zshrs, stryke, awkrs, and vimlrs. There is no bespoke VM or JIT in elisprs.

What it is

Emacs Lisp has only ever run inside Emacs. elisprs takes the language out of the editor and runs .el as ordinary programs from the command line. It is the fifth language on the shared fusevm VM — elisp executes on the same Cranelift-JIT/AOT engine as the sibling languages, not a bespoke interpreter.

elisprs is a pure fusevm frontend: the reader (src/reader.rs) builds forms, macros expand, and the compiler (src/compiler.rs) lowers every top-level form to a fusevm::Chunk that runs on the VM (src/lib.rs::eval_formshost::run_chunk). The elisp object heap (cons, symbol, vector, closure, hash-table cells) lives in ElispHost and rides through the VM as Value::Obj(u32) handles, reached from fusevm's extension handler. There is no tree-walk evaluator.

Architecture

Emacs Lisp is a Lisp-2 (separate value and function cells per symbol) with both lexical and dynamic scope — exactly what an off-the-shelf Lisp won't give you. elisprs owns those semantics and delegates execution to fusevm: reader → compiler → bytecode on the shared VM.

.el source  →  reader.rs  →  Value (fusevm Value; heap objects as Value::Obj handles)
            →  macroexpand  →  compiler.rs → fusevm::Chunk  →  fusevm VM + Cranelift JIT / AOT

Lowers to fusevm

compiler.rs compiles each form to a fusevm::Chunk and host::run_chunk runs it on the VM — there is no interpreter. Hot numeric paths lower to native fusevm ops so they JIT/AOT-compile.

Reader is ours

A small elisp-correct reader handles core syntax other Lisps mis-tokenize — 1+/1-, #'foo, ?c, dotted pairs, and backquote (`/,/,@).

Lisp-2 + lexical/dynamic

A custom obarray gives every symbol a value cell and a function cell. let/closures bind lexically through captured Scope frames; special (defvar/defconst) vars bind dynamically through a specstack.

Heap as Value::Obj

Cons, symbol, vector, closure, subr, and hash-table cells live in the ElispHost arena and ride through fusevm as Value::Obj(u32) handles, resolved by the extension handler — no fork of the value enum.

Status & roadmap

The table reflects the current state of the tree, not aspiration.

ComponentStateNotes
Elisp-correct reader (1+/#'/?c/:kw/dotted/backquote)WorkingHand-written (src/reader.rs).
Lisp-2 obarray + lexical & dynamic bindingWorkingValue + function cells; Scope frames + specstack.
Special forms (22) + defmacroWorkingMacros expand and re-evaluate before lowering.
Subr standard library (234)WorkingLists, numbers, strings, predicates, IO, functional + an elisp prelude.
Standalone elisp binary + REPLWorkingFile / -e / REPL.
Form → fusevm::Chunk loweringWorkingcompiler.rs; runs on fusevm via host::run_chunk.
--aot / --aot-exefusevm::aot::compile_objectWorkingNative object / standalone executable (src/aot.rs).
LSP / DAP servers (--lsp / --dap)WorkingFull stdio servers (src/lsp.rs, src/dap.rs).
Dotted pairs · backquote · lexical binding · setcarWorkingAll implemented in the reader / host.
rkyv bytecode cache (~/.elisprs/scripts.rkyv)Workingsrc/cache.rs; warm runs skip read/lower.

A taste

(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)"

Why elisprs

Elisp, standalone

Run .el as a program from the shell, with a REPL — no Emacs process required.

Faithful semantics

A real Lisp-2 with lexical and dynamic binding and separate function/value cells, not a Scheme wearing an Emacs hat.

One shared engine

elisp lowers to bytecode and inherits fusevm's three-tier Cranelift JIT and AOT — the same speedups that land for zshrs, stryke, awkrs, and vimlrs.

Ahead-of-time native

elisp --aot-exe links a .el against the elisprs runtime into a standalone native binary — no Emacs, no interpreter at run time.

Building from source

elisprs builds as a standalone Rust crate on top of fusevm (plus the LSP/regexp/cache support crates):

git clone https://github.com/MenkeTechnologies/elisprs
cd elisprs

cargo build --release
cargo test            # reader unit tests + end-to-end eval suite

./target/release/elisp examples/demo.el

License

elisprs is MIT OR Apache-2.0 — free and open source. See LICENSE.

Repository & links