NeLisp = Emacs Lisp VM in pure Elisp + Rust syscall stub
  • Emacs Lisp 93.6%
  • Emacs-Lisp 2.7%
  • Shell 2.5%
  • PowerShell 0.7%
  • Makefile 0.4%
Find a file
zawatton 7f501835d3 Merge fix/llama-shorthand-value: lex ## as a fixed two-byte symbol token
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:17:27 +09:00
.github/workflows wip(aot): macOS/Darwin native target + standalone tooling (reconciled onto main) 2026-06-28 09:54:03 +09:00
bench feat(phase7.2): 3-tier ratio bench harness — Doc 29 §5.1 v2 LOCK-close 2026-04-27 13:45:09 +09:00
bin wip(aot): macOS/Darwin native target + standalone tooling (reconciled onto main) 2026-06-28 09:54:03 +09:00
docs fix(stdlib): give intern-soft a real native soft-fail primitive (Doc 163 Phase C) 2026-07-06 14:22:48 +09:00
examples feat(dired): directory browser (getdents64 + stat) with an X11 UI 2026-06-28 23:19:45 +09:00
lisp Merge fix/llama-shorthand-value: lex ## as a fixed two-byte symbol token 2026-07-06 21:17:27 +09:00
packages feat(dired): directory browser (getdents64 + stat) with an X11 UI 2026-06-28 23:19:45 +09:00
release chore(release): prepare v0.6.0 2026-06-26 14:04:19 +09:00
scripts fix: mod float parity, match-data/save-match-data, current-time 2026-07-06 21:07:46 +09:00
src fix(reader): stop eval-elisp-artifact from crashing on void string-match-p 2026-07-06 20:15:21 +09:00
test fix(standalone-reader): preserve throw exits through unwind-protect 2026-07-05 15:40:09 +09:00
tools feat(dired): directory browser (getdents64 + stat) with an X11 UI 2026-06-28 23:19:45 +09:00
.codex feat(package-split): nelisp-{base64,secure-hash,regex} extraction 2026-04-27 18:11:25 +09:00
.gitattributes chore: drop vestigial .image/cargo references after Rust removal 2026-06-02 14:59:19 +09:00
.gitignore Add standalone REPL development path 2026-06-02 19:48:08 +09:00
AGENTS.md docs: document nelisp worklog policy 2026-06-19 22:52:56 +09:00
BRIEFING.org docs: Phase 0 complete — 6 design docs + 4 Priority A paper summaries 2026-04-22 23:44:29 +09:00
FINDINGS.md Merge declare-scan diagnosis: defmacro/defun do not strip (declare ...) 2026-07-05 16:52:29 +09:00
install.sh feat(phase7.5.3): stage-d-v2.0 bundled-Emacs tarball MVP — bin/anvil works without system Emacs install 2026-04-27 14:16:12 +09:00
LICENSE scaffold: Phase 1 Week 1-2 project setup 2026-04-22 23:44:44 +09:00
Makefile fix: mod float parity, match-data/save-match-data, current-time 2026-07-06 21:07:46 +09:00
README-stage-d-v2.0.org chore(boundary): drop dangling bootsmoke scripts + refresh stage-d README 2026-04-29 19:23:08 +09:00
README-stage-d-v3.0.org chore(release): prepare v0.6.0 2026-06-26 14:04:19 +09:00
README-stage-d.org chore(boundary): drop dangling bootsmoke scripts + refresh stage-d README 2026-04-29 19:23:08 +09:00
README.org chore(release): prepare v0.6.0 2026-06-26 14:04:19 +09:00
RELEASE_NOTES.md chore(release): prepare v0.6.0 2026-06-26 14:04:19 +09:00

NeLisp — Self-Hosted Emacs Lisp VM (v0.6.0)

Note on language: I'm a non-native English speaker. The phrasing in this README is shaped with help from an LLM, but the design decisions and technical claims are mine. If something reads oddly, please ask and I'll clarify.

Note on source comments: Comments throughout lisp/, src/, and scripts/ sometimes reference paths such as build-tool/src/eval/builtins.rs when documenting what each pure-Elisp unit replaced. These are historical provenance for the Rust → pure-Elisp migration — the Rust build-tool/ tree no longer exists in this repository. Read such paths as a record of what was migrated, not as live source files.

TL;DR

NeLisp is a from-scratch Emacs Lisp runtime where essentially the entire language layer (JIT, allocator, GC, coding-system, evaluator, bytecode interp, reader, special forms, builtin dispatcher, object file writers, native code emitters, and standalone OS surface) is written in Elisp itself.

In the wider "replace Emacs" effort, this repo is Layer 1 (the runtime / reader / AOT compiler that produces target/nelisp). It is consumed by Layer 2 ../nelisp-emacs (Emacs 意味論) and Layer 3 ../nelisp-gui (X11 描画/入力). The 3-layer responsibility boundary, inter-layer contracts, and completion conditions are fixed in ../nelisp-gui/docs/design/00-three-layer-architecture.org.

The important claim is not merely "Elisp can call into a runtime". NeLisp's runtime implementation is Elisp: the evaluator, reader, compiler, allocator, GC, object writers, and native code emitters live as Lisp source that the project can load, evaluate, redefine, compile, and eventually self-host. The OS-facing pieces are moving the same way: standalone file I/O, stdout/stderr, process exit, raw memory, mmap-backed allocation, atomics, clone(2), fork(2), and the syscall surface used by the standalone reader are expressed from Elisp via AOT codegen and syscall-direct.

Current stable release: v0.6.0 (2026-06-26). This release keeps the project versioned by SemVer git tags, in the same style as anvil.el: pin users to v0.6.0, let main carry active development, and use release notes for the exact gate status.

v0.6.0 ships:

  • Pure-Elisp runtime implementation — evaluator, reader, special forms, builtins, compiler, allocator, GC, object writers, codegen, and standalone OS interaction all live in Elisp source.
  • Cross-platform support on 5 targets — linux/x86_64, linux/aarch64, macos/x86_64, macos/aarch64, windows/x86_64 — with pure-elisp object-file writers (ELF, Mach-O, PE/COFF) and pure-elisp asm encoders (x86_64 with both SysV and Microsoft x64 ABIs, plus AArch64 AAPCS).
  • Self-host + parallel build — a pure-elisp AOT-AOT binary (target/nelisp, pure Elisp) loads its own compiler and compiles Elisp source → native ELF with no Emacs in the loop, including recursion ((fact 5) → exit 120), multi-threaded native programs (clone(2) + atomics), and N units compiled concurrently (fork(2) workers). All CI-gated. See Standalone self-host & parallel build.
  • REPL-driven runtime development — the same pure-Elisp standalone reader starts a REPL with no arguments; scripts can use --repl [--no-prompt]. It keeps one live evaluator environment, so redefining a function and calling it again takes effect immediately, including through persistent nelisp--eval-source-string.
  • Low-footprint standalone load (2026-06) — a hybrid refcount+tracing compacting GC (mmap-chunked arena, segregated free-list, a moving collector that forwards live objects and munmap=s reclaimed chunks) together with an *8-byte tagged-word container representation* (=NlConsBox 72→24B; vector / record / cell slots 32→8B) cut the full 319-file vendor-load peak RSS from 1.80GB to 118MB (93%) and the load time ≈13× (13.4s → 0.9s). A container slot (cons car/cdr, vector element, cell value) is either an immediate (=Nil=/=t=/fixnum) or an 8-byte pointer to a 32-byte child box; the GC marks, forwards, and moves them word-aware. Validated by a 50× load soak under compaction and the 4438-assertion suite.
  • Reader/core breadth since v0.5.1 — correctly-rounded float parse / print paths, broader Common Lisp / Emacs Lisp prelude coverage, additional raw syscall surface, AOT data/blob relocations, defined varargs, f64 extern returns, bucketed hash tables, incremental load, and native list-search hot paths for SMIE / Org-style loads.

Use cases: running Elisp on machines without Emacs, and as a substrate for Lisp Machine / new-dialect experiments.

Current Release Highlights

v0.6.0 is the current stable tag. It collects the large post-v0.5.1 develop line into one release and makes SemVer tags the user-facing versioning scheme.

Area What changed
Versioning Stable users should pin v0.6.0. main remains the active development branch; historical stage-d names are retained only for installer/runbook compatibility.
Standalone runtime target/nelisp is the primary no-Emacs runtime: REPL, --eval, --load, file execution, runtime-image replay, artifact loading, and self-hosting AOT compile.
Pure-Elisp OS/compiler surface AOT grammar and runtime gained data blobs, frame-alloc, shr, f64-bits, defined SysV varargs, libm/f64 return bridging, and additional raw syscall helpers.
Float / printer correctness Float parse/print work now covers Eisel-Lemire parsing, correctly-rounded power tables, shortest-round-trip printing, C99 hex-float, negative zero, inf/nan, and %f/%e/%g precision.
Stdlib breadth The bare reader prelude gained broader CL/Elisp coverage for package loading: rx/cl-generic helpers, setf places, cl-defstruct breadth, arithmetic gaps, hash-table introspection, list search, and incremental load.
Memory footprint Flat-arena cold-load + compacting GC + 8-byte container slots keep full vendor-load around 118MB peak RSS instead of the older 1.80GB path.
Release artifacts Standalone tarball defaults now use v0.6.0 names: anvil-v0.6.0-<platform>.tar.gz.

What NeLisp is

A research project to implement Emacs Lisp on a substrate other than the existing Emacs C core — modeled after SBCL (Common Lisp implemented in itself, on a small non-Lisp runtime) and deliberately not a Scheme rewrite or a rewrite of the entire Emacs C core in a different systems language.

The runtime is implemented in Elisp itself. NeLisp keeps the language surface, compiler, memory manager, object writers, OS-facing standalone driver, and development REPL in Lisp source so the system can inspect and modify itself while it is running.

Current source is centered in lisp/, src/, packages/, and scripts/, with the AOT compiler emitting native object code from Elisp forms.

Elisp — the language runtime (v1.0 snapshot: 45 files, 54,511 LoC)

The breakdown below is the 2026-04-27 v1.0 snapshot and is retained for reference. Current totals (2026-05-14) are lisp/ 46 files ~20k LOC

  • packages/ 27 files ~14k LOC; see Pure-Elisp Migration Progress

for what shifted.

Subsystem Files LoC Phase
JIT compiler (SSA + linear-scan, x86_64 + arm64; written in Elisp itself) 13 12,196 7.1
Memory management (allocator + GC) 5 6,290 7.27.3
Lisp evaluator (reader, eval, special forms, closures, macros) 7 4,035
Module loading and bootstrap 3 2,147 7.5
Coding-system (logic) 1 1,928 7.4
Bytecode interpreter 1 1,845
Emacs compatibility shims 3 1,680
Multi-threading / parallel worker pool (process-level concurrency, file-notify) 5 1,405
Buffer model 2 1,076
Other infrastructure (dev-audit, heap-types, EC bridge, entry point) 4 1,094
Character tables (JIS, data) 1 20,815

v2.0 ships the four cores Emacs's C core normally gives you — native compile, allocator, GC, coding-system — all written in Elisp itself. Object file writers, asm encoders, ABI-aware codegen for 5 OS×arch targets are also pure Elisp.

Two things Emacs proper doesn't ship in the same form

  • A JIT compiler written in Elisp itself. Emacs 28+ has native-comp, but it depends on libgccjit (an external C library) and is used as AOT in practice. NeLisp's JIT is ~12k LoC of Elisp source you can read end-to-end and modify, with runtime codegen via mmap + PROT_EXEC.
  • Real parallel execution. Emacs's make-thread is cooperative — one Lisp thread runs at a time. NeLisp's worker pool gives process-level concurrency under the same Lisp runtime, which is what made the orchestrator and the long-running MCP handler design tractable.

Standalone self-host & parallel build (2026-06)

The pure-Elisp AOT AOT toolchain now produces a standalone interpreter binarytarget/nelisp, built by make standalone-reader — that runs with no Emacs in the loop at all: it starts a REPL when launched with no arguments, loads an .el file via argv[1], evaluates it, and exits with the low byte of the last form's integer value. It also exposes --repl [--no-prompt] for line-oriented interactive development against one live evaluator environment, plus short CLI options:

target/nelisp --help
target/nelisp                         # starts the REPL
target/nelisp --eval '(+ 40 2)'       # prints 42, exits 0
target/nelisp --load /tmp/prog.el     # prints the last value, exits 0
target/nelisp /tmp/prog.el           # exits with the last integer value

Its OS connection is also written on the Elisp side: opening and reading files, reading stdin, writing stdout/stderr, exiting, allocating memory, spawning OS threads, and forking compile workers all flow through Elisp-generated native code and raw syscall forms.

Fed its own compiler toolchain as source (nelisp-aot-compiler + nelisp-asm-x86_64 + nelisp-elf-write + nelisp-static-linker, ~20k lines of Elisp), this binary self-hosts: it compiles Elisp source to a native x86_64 ELF executable, with no Emacs anywhere. The following are all CI-gated (.github/workflows/ci.yml):

Make target What it proves
make standalone-selfhost-test compiles (seq (defun fact (n) ...) (exit (fact 5))) → native ELF → run → exit 120 (recursion)
make standalone-selfhost-mt-test compiles a multi-threaded program (clone(2) + SeqCst atomics, 3 OS threads) → native ELF → 42
make standalone-parallel-compile-test compiles 4 units concurrently — 4 fork(2) workers each running the full compiler → 11,22,33,44

The incremental standalone eval build also has a multi-process worker entry. On POSIX use tools/build-standalone-parallel.sh [NJOBS]; on native Windows PowerShell use .\tools\build-standalone-parallel.ps1 -Jobs 4. Both routes drive nelisp-standalone-compile-chunk and then link once serially, using Emacs and pure elisp only.

REPL-driven runtime development

The standalone reader is also the development REPL for NeLisp itself. The REPL runs in the AOT standalone interpreter and evaluates forms through the same reader, evaluator, builtin dispatcher, environment, and GC path used by normal file execution.

That makes the core workflow Lisp-native:

  1. Start one standalone NeLisp process.
  2. Define or redefine functions in that process.
  3. Call them immediately without restarting the runtime.
  4. Use nelisp--eval-source-string to evaluate generated source into the same live environment.
make standalone-reader

target/nelisp
nelisp> (defun hot () 1)
hot
nelisp> (hot)
1
nelisp> (defun hot () 42)
hot
nelisp> (hot)
42

Runtime sources can also be pre-bundled into a replay image. This is the portable bootstrap cache used before the native artifact layer:

printf '(defun cached-hot () 42)\n' >/tmp/runtime-src.el
target/nelisp dump-runtime-image /tmp/runtime.nlri --load /tmp/runtime-src.el
target/nelisp eval-runtime-image /tmp/runtime.nlri '(cached-hot)'

For repeated command execution, compile the replay image into a NeLisp artifact cache. .nelc removes repeated source parsing/replay from the command path. The .neln native hot path uses the same flattened runtime-image input in the host/dev native compiler gate; wiring that compiler into the standalone command path is tracked separately.

When a .neln artifact is loaded, native-eligible functions are registered as ordinary NeLisp functions with a native-first wrapper and bytecode fallback. The integer native proof path also caches the linked driver executable under the user cache directory, so repeated calls do not pay object extraction and link cost every time.

Generic source loading can use the same artifact layer. Compile a tree with compile-elisp-artifacts --kind neln FILE.el DIR... to create adjacent FILE.el.neln artifacts; nelisp-load-file probes fresh adjacent .neln then .nelc before source replay. If nelisp-load-auto-compile-artifacts is enabled, a missing or stale adjacent artifact is rebuilt on demand. inspect-elisp-artifact shows :native-report so unsupported native bodies are visible while the portable fallback remains usable.

target/nelisp compile-runtime-image \
  --kind nelc \
  --input /tmp/runtime.nlri \
  --output /tmp/runtime.nelc

target/nelisp eval-elisp-artifact /tmp/runtime.nelc '(cached-hot)'

For editor integration or scripted probes, use --no-prompt:

printf '(defun hot () 7)\n(hot)\n(nelisp--eval-source-string "(defun hot () 9)")\n(hot)\n(exit)\n' \
  | target/nelisp --repl --no-prompt
# => hot
# => 7
# => hot
# => 9

The last example proves both parts of the development surface: top-level REPL input mutates the live environment, and persistent source-string evaluation mutates that same environment.

Two concurrency paths, exposed to interpreted code

Both are built only from existing AOT grammar ops (syscall-direct / atomic-fetch-add / raw memory access).

  • (thread-spawn 'FORM) / (thread-join ADDR N) — true multi-threaded eval via clone(2) (CLONE_VM): OS threads sharing one address space, each evaluating FORM with its own per-thread context (own recursion counter), joined through a shared atomic counter. The shared-memory model (12/12 stable for parallel eval).
  • (fork-spawn 'FORM)fork(2) workers with COW-isolated address spaces (each its own arena / globals / env), so N workers compiling concurrently share no mutable eval state and cannot race. This is the production parallel build path (4-way and 5-way verified 100%).

The standalone arena allocator advances its bump pointer with a SeqCst atomic-fetch-add, so concurrent allocation is thread-safe by construction.

The same pattern is used for ordinary OS interaction in the standalone reader: file loading, REPL stdin, stdout/stderr writes, process exit, thread creation, and worker forking are runtime behaviors driven from Elisp-generated native code. The point is not merely that Elisp logic sits above an OS bridge; the OS bridge itself is now part of the Elisp runtime surface for the standalone path.

This is the third rung of the self-host ladder, end to end and with no Emacs: text → (the standalone interpreter running the AOT compiler) → native ELF, scaled to concurrent multi-unit compilation.

Pure-Elisp Substrate (v2.0, 2026-05-22)

The implementation center is Elisp source. The standalone reader runs that substrate without a host Emacs process: source text is read, parsed, evaluated, compiled, linked, and used to drive OS interaction through Elisp-generated native code.

Shipped substrate

  • AOT self-emission (Doc 85 / 9197): ~7,550+ LoC of Elisp for the x86_64 + arm64 assemblers, ELF + Mach-O + PE/COFF writers, static linker, crt0, the Sexp DSL, and the AOT compiler. 600+ ERT. (fact 5) compiles end-to-end to a native .o, links, and runs.
  • Cross-platform native emit (May 2026 — see Cross-Platform Support below): 5 OS×arch targets supported entirely from elisp.
  • GC pure-elisp (Doc 79 v7 Phase C Stage 5.3): Bacon-Rajan cycle collector at lisp/nelisp-stdlib-gc.el ( 806 LoC ).
  • All special forms, builtins, env / mirror / frame ops, NlBox Clone/Drop, reader (lexer + parser), JIT trampolines, syscall handlers — now live in elisp via AOT .o.

Test gate

  • ERT coverage for elisp .o emission: 600+ tests including Win64-ABI-specific assertions (Wave 5).
  • Standalone self-host gates (zero-Emacs, CI, 2026-06): standalone-selfhost-test (recursion → native ELF → 120), standalone-selfhost-mt-test (clone(2) + atomics multi-thread → 42), standalone-parallel-compile-test (4 units compiled concurrently via fork(2) → 11,22,33,44).
  • Standalone parallel eval build: tools/build-standalone-parallel.sh [NJOBS] on POSIX, .\tools\build-standalone-parallel.ps1 -Jobs 4 on Windows.

Cross-Platform Support (v2.0, 2026-05-22)

NeLisp v2.0 builds and runs across 5 OS × architecture targets, with all object-file writers and asm codegen written in pure Elisp:

Target Object writer Asm encoder ABI gate
linux/x86_64 nelisp-elf-write.el nelisp-asm-x86_64.el SysV AMD64
linux/aarch64 nelisp-elf-write.el nelisp-asm-arm64.el AAPCS
macos/aarch64 nelisp-mach-o-write.el nelisp-asm-arm64.el AAPCS
macos/x86_64 nelisp-mach-o-write.el nelisp-asm-x86_64.el SysV AMD64
windows/x86_64 nelisp-pe-write.el nelisp-asm-x86_64.el Microsoft x64

The PE/COFF writer (nelisp-pe-write.el, 765 LoC) and the Windows-specific codegen path (AOT compiler --abi 'win64 mode with shadow space allocation + RCX/RDX/R8/R9 arg-reg remapping + RBP/RBX/RDI/RSI/R12-R15 callee-save) are pure elisp.

Deferred: aarch64 Windows (arm64ec) — currently x86_64 Windows only.

Status — v0.6.0 (2026-06-26)

NeLisp v0.6.0 centers the runtime in Elisp, keeps the standalone path as the primary user surface, and adds cross-platform native emit across 5 OS×arch targets.

  1. Standalone readertarget/nelisp loads and evaluates .el files with no host Emacs process.
  2. Runtime development REPLtarget/nelisp keeps a live evaluator environment for immediate redefinition.
  3. Standalone self-host + parallel build (2026-06) — the pure-elisp AOT AOT toolchain loads its own compiler and compiles Elisp source → native ELF with no Emacs in the loop: recursion ((fact 5) → exit 120), multi-threaded native programs (clone(2)

    • atomics), and N units compiled concurrently via fork(2) workers.

    CI-gated. See Standalone self-host & parallel build.

Install

Two paths.

Stable tag — v0.6.0 (recommended)

For source users, pin the current stable tag:

git clone https://github.com/zawatton/nelisp ~/nelisp
cd ~/nelisp
git checkout v0.6.0
make standalone-reader
make standalone-reader-test

For release artifacts, v0.6.0 tarballs use:

anvil-v0.6.0-linux-x86_64.tar.gz
anvil-v0.6.0-macos-aarch64.tar.gz
anvil-v0.6.0-windows-x86_64.tar.gz

main is the development branch. Use it when hacking on NeLisp itself; pin v0.6.0 when you want reproducible runtime behavior.

Prerequisites by platform

The standalone-reader build is driven by host Emacs and the pure-Elisp AOT toolchain. Install a normal native build environment for your platform, then run make standalone-reader.

Tier guarantees (v2.0):

  • linux-x86_64 — 1st-class (CI blocker, every release must pass).
  • macos-aarch64 / windows-x86_64 — end-to-end CI gated on real runners (macos-14 / windows-latest) via .github/workflows/stage-d-v3.0-standalone.yml: native self-host (Mach-O / PE32+), OS-compat ERT, native eval/reader, cache identity, parallel build, and tarball/install of the native CLI. macOS uses ad-hoc codesign for native execution. Not release blockers — linux-x86_64 remains the hard gate.
  • linux-aarch64 / macos-x86_64 — supported by the elisp object-file writers (nelisp-elf-write.el / nelisp-mach-o-write.el), but no dedicated CI runner job yet (best-effort).
  • windows-aarch64 (arm64ec) — deferred.

Linux (Debian / Ubuntu):

sudo apt install build-essential git emacs

Linux (Fedora / RHEL):

sudo dnf install gcc make git emacs

macOS (arm64 or x86_64):

xcode-select --install              # one-time
brew install emacs

Windows — native x86_64 (v2.0 supported): The elisp-side PE/COFF writer + Win64 ABI codegen produce native COFF .o files. Use a native toolchain with host Emacs, or use WSL2/MSYS2 with the Linux-style commands above.

From source — recommended for hacking and using the Elisp API

git clone https://github.com/zawatton/nelisp ~/nelisp
cd ~/nelisp
make standalone-reader
make standalone-reader-test

Build outputs land under target/, with target/nelisp as the primary no-Emacs runtime.

Pre-built standalone tarball — single-binary deployment

For users who want NeLisp without cloning / building. No Emacs install required at runtime:

curl -fsSL https://github.com/zawatton/nelisp/releases/latest/download/install-v3.sh | bash

On Windows PowerShell:

iwr https://github.com/zawatton/nelisp/releases/latest/download/install-v3.ps1 -OutFile install-v3.ps1
.\install-v3.ps1

Default install root: $HOME/.local/share/anvil-v0.6.0/. On Windows the default root is under $env:LOCALAPPDATA\anvil-v0.6.0. (The anvil- prefix is historical packaging — the tarball is the NeLisp runtime; use bin/nelisp or bin/nelisp.exe directly.) Full install-to-run runbook for Linux x86_64, macOS arm64, and Windows x86_64 (release install, local tarball install, PATH setup, eval, file loading, REPL, runtime images, verification, and uninstall): README-stage-d-v3.0.org.

Older release lines, for reference only:

  • README-stage-d-v2.0.org — bundled-Emacs tarball, ~25 MB
  • README-stage-d.org — legacy host-Emacs-required path

Cross-PC verification

After clone + build, run the smoke script for your platform:

# Linux / macOS:
scripts/verify-cross-platform.sh --parallel-jobs 2 --include-tarball

# Windows (PowerShell):
.\scripts\verify-cross-platform.ps1 -IncludeTarball

Expected output ends with Cross-platform verify PASS. With --include-tarball, verification also installs from the freshly built dist tarball and executes the installed bin/nelisp or bin/nelisp.exe. The same standalone/tarball/install gate is wired into .github/workflows/stage-d-v3.0-standalone.yml for Linux x86_64, macOS arm64, and Windows x86_64. macOS runs the full native Mach-O self-host, OS-compat, eval, reader, REPL, tarball, install, and installed-binary gate.

Launch

For current source checkouts, the primary no-Emacs runtime is the pure-Elisp standalone reader:

make standalone-reader

# load + run a file; process exit is the low byte of the last integer value
printf '(+ 40 2)\n' >/tmp/prog.el
target/nelisp /tmp/prog.el
echo $?                                # => 42

# evaluate or load and print the result
target/nelisp --eval '(concat "hello" " " "world")'
target/nelisp --load /tmp/prog.el

# bundle runtime sources once, then evaluate forms against the image
printf '(defun cached-hot () 42)\n' >/tmp/runtime-src.el
target/nelisp dump-runtime-image /tmp/runtime.nlri --load /tmp/runtime-src.el
target/nelisp eval-runtime-image /tmp/runtime.nlri '(cached-hot)'
target/nelisp compile-runtime-image --kind nelc --input /tmp/runtime.nlri --output /tmp/runtime.nelc
target/nelisp eval-elisp-artifact /tmp/runtime.nelc '(cached-hot)'

# interactive runtime development
target/nelisp

The REPL keeps one live evaluator environment. Redefinitions are visible immediately, and generated source can be evaluated into that same environment:

printf '(defun hot () 7)\n(hot)\n(nelisp--eval-source-string "(defun hot () 9)")\n(hot)\n(exit)\n' \
  | target/nelisp --repl --no-prompt
                                       # => hot
                                       # => 7
                                       # => hot
                                       # => 9

Older install tarballs may also ship a nelisp command. Current source checkouts use the pure-Elisp standalone reader above for REPL development and file execution.

nelisp --version                       # nelisp v0.6.0
nelisp --eval "(+ 1 2 3)"              # => 6
nelisp --load ~/nelisp/examples/hello/hello.el

Quick start

For larger workflows the evaluator is reachable three ways; pick the one that matches your context.

From Emacs Lisp — REPL straight into NeLisp's own evaluator

(add-to-list 'load-path "~/nelisp/src")        ; or the install root's src/
(require 'nelisp-bootstrap)
(nelisp-bootstrap-init)             ; one-time self-host load

(nelisp-eval-string "(+ 1 2 3)")    ; => 6
(nelisp-eval-string
 "(mapcar (lambda (n) (* n n)) '(1 2 3 4))")
;; => (1 4 9 16)

(nelisp-load-file "~/nelisp/examples/hello/hello.el")
(nelisp-eval-string "(hello-world)")
;; => "Hello, World!"

The forms go through NeLisp's own reader + evaluator. Host Emacs is a launcher only — result and host-evaluator state stay separate.

Downstream MCP-server front ends can wrap this CLI, but the standalone tarball ships the self-contained NeLisp runtime directly as bin/nelisp or bin/nelisp.exe.

Architecture

  +--------------------------------------------------+
  | Optional front ends / tools                      |
  +-----------------+--------------------------------+
                    | source / stdio / files
  +-----------------v--------------------------------+
  | target/nelisp                  |
  |   reader / evaluator / macro / compiler / loader |
  |   REPL / persistent eval / runtime-image replay  |
  +--------------------------------------------------+
  | AOT Elisp substrate                         |
  |   allocator / GC / builtins / syscall-direct     |
  |   asm encoders / object writers / static linker  |
  +--------------------------------------------------+
  | OS substrate                                     |
  |   files / stdio / exit / mmap / atomics / fork   |
  +--------------------------------------------------+

Two boundaries are pinned:

  • Language boundary reader, evaluator, macro expansion, compiler, loader, special forms, builtins, allocator, and GC are runtime behavior implemented as Elisp.
  • OS boundary file loading, REPL stdin, stdout/stderr writes, process exit, memory mapping, atomics, thread spawning, and worker forking are exposed through Elisp-generated code and raw syscall forms on the standalone path.

Soak gate (Doc 32 v2 §2.7)

Tier Duration RSS growth ceiling Status (2026-04-27)
blocker (CI) 1h < 5 MB PASS (linux-x86_64)
post-ship audit 24h < 10 MB / 24h scheduled, weekly

Standalone native CLI matrix (current HEAD):

Platform Status
linux-x86_64 gated by native eval, reader, REPL, cache identity, parallel, tarball/install
macos-aarch64 gated by Mach-O self-host, OS ERT, signed CLI tarball/install, eval/reader/REPL
windows-x86_64 gated by PE32+ self-host, OS ERT, CLI tarball/install, eval/reader/REPL
linux-arm64 best-effort

The older v1.0 arm64 time-box language is retained only in the v1.0 release templates and tests. The current standalone path is verified by scripts/verify-cross-platform.sh --include-tarball on POSIX and scripts/verify-cross-platform.ps1 -IncludeTarball on Windows. The tarball gate includes a real install smoke from the freshly built dist directory.

What is not in v1.0

Said plainly so expectations are calibrated:

  • No buffer / marker / window primitives. NeLisp is the language runtime, not the editor. emacs-nox is the closest existing comparison if scripting is all you need; what's different here is the 624 KB standalone tarball and a runtime small enough to read end-to-end.
  • Phase 8.0 evaluator is a minimal subset (25 special forms + 75 builtins): GC bridge, bignum, full backquote semantics, full save-excursion are deferred to Phase 8.x follow-up patches.
  • Default --no-emacs still falls back to emacs --batch for cold-init failures (3-year safety window).
  • Linux x86_64, macOS arm64, and Windows x86_64 native standalone CLI paths are gated by platform-specific eval, reader, REPL, cache identity, parallel build, and tarball smokes.
  • Linux arm64 remains best-effort.

Test status (HEAD, 2026-05-14)

  • make test = 3604 tests / 3562 expected / 5 unexpected (all pre-existing flake) / 37 skipped on linux-x86_64. See Pure-Elisp Migration Progress section above for the flake breakdown.
  • Standalone self-host gates (2026-06, all green, zero-Emacs): standalone-{reader,eval,reader-prelude,selfhost,selfhost-mt,parallel-compile}-test — the standalone target/nelisp binary self-hosts the AOT compiler (recursion → native ELF → 120) and compiles N units concurrently via fork(2) workers (4-/5-way verified 100%).
  • The previous 2026-04-29 baseline was 2452 / 2409 / 0 unexpected. The ~1.5× growth in test count reflects the new sub-systems: AOT self-emission (600+ ERT), Doc 79 v7 GC, Doc 102 mirror, Doc 100/110 ABI / arithmetic, Doc 81 cons cluster, Doc 86/87 Tier 1+2 builtins.
  • Cross-platform standalone gates (Linux x86_64 / macOS M1 arm64 / Windows x86_64) now cover native reader/eval execution, REPL command handling, cache identity, parallel build, OS compatibility ERT, and signed standalone tarball verification. macOS arm64 uses ad-hoc codesign for native Mach-O execution.

Key design documents

Doc Topic Status
18 Stage D launcher charter LOCKED
27 Phase 7+ self-implemented runtime LOCKED-2026-04-25-v2
28 Phase 7.1 NeLisp native compiler LOCKED-2026-04-25-v2
32 Phase 7.5 integration + standalone E2E LOCKED-2026-04-25-v2
76 Phase 7 elisp-side OS surface migration IN PROGRESS
79 v7 Pure-elisp GC (Bacon-Rajan cycle collector) Phase C Stage 5.3 SHIPPED
81 nelisp-cc primitive trampoline extension Stage 81.2 SHIPPED
85 AOT feasibility spike VALIDATED
91-97 AOT ELF writer / asm / linker / DSL / build SHIPPED
100 Runtime artifact contract §B/C SHIPPED
102 Environment model migration to Elisp Phase 8 Sprint A SHIPPED, Sprint B/C deferred
110 AOT f64 ABI + float/math migration to Elisp SHIPPED

Earlier status (Phase 0 — 2026-04-22) — preserved for reference

Phase 0 (design documents) was completed 2026-04-22. Key decisions locked at that time and still in force:

  • Bootstrap strategy: Host-Emacs bootstrap (Candidate A), transferring SBCL's genesis methodology (Rhodes 2008) to Emacs as the host
  • Scope: Stage A only (= Elisp substrate). Stage B (AI-native Emacs) and Stage C (AI/Emacs boundary fusion) belong to anvil.el successors
  • Concurrency: Actor model as primary (Phase 4), STM experimental (Phase 4b), fiber (Phase 3), threads deferred (Phase 5)
  • Division of labor: NeLisp core team focuses on AI-critical paths (buffer/event/concurrency/eval/FFI); community handles long tail (display render, primitive utilities) post-v1.0 — Linux kernel / SBCL governance model

The roadmap is in docs/05-roadmap.org.

Philosophy (short form)

Emacs Lisp is not philosophically cleaner than Scheme or Common Lisp — but decades of accumulated, lived-in Elisp is a cultural asset worth self-hosting on its own terms, rather than porting it to someone else's substrate.

Esperanto is a constructed language of philosophical elegance, but English is the international language. Elisp is English here.

The long form lives in docs/00-motivation.org.

Non-goals

  • Scheme rewrite (Guile-Emacs direction) — rejected
  • A replacement editor for Emacs — out of scope for NeLisp itself (Stage B/C handled by anvil.el or successors)
  • Replacing all 3000 C functions — non-goal. NeLisp core team rewrites AI-critical paths, community fills the long tail

The three-stage vision

  ┌─────────────────────────────────────────────────┐
  │ Stage C: AI and Emacs as one                    │ ← anvil.el (and beyond)
  │ ┌─────────────────────────────────────────────┐ │
  │ │ Stage B: Emacs made AI-native               │ │ ← anvil.el (and beyond)
  │ │ ┌─────────────────────────────────────────┐ │ │
  │ │ │ Stage A: Emacs internals in pure Elisp  │ │ │ ← *NeLisp (this project)*
  │ │ │ (no C to traverse — everything editable) │ │ │
  │ │ └─────────────────────────────────────────┘ │ │
  │ └─────────────────────────────────────────────┘ │
  └─────────────────────────────────────────────────┘

NeLisp builds the substrate (Stage A). Stage B and C are built on top of NeLisp, as extension packages.

Repository layout

nelisp/
├── BRIEFING.org             # First-session design instructions
├── README.org               # This file
├── .gitignore
├── packages/                # Extracted optional libraries
│   ├── README.org           # Package split convention
│   └── nelisp-json/         # Pilot extracted library package
│       ├── README.org
│       ├── src/
│       └── test/
└── docs/
    ├── 00-motivation.org    # Why this, why now, why not Scheme (USER voice)
    ├── 01-related-work.org  # 7 projects audited + paper priorities
    ├── 02-scoping.org       # Stage A/B/C + AI-critical vs long tail
    ├── 03-architecture.org  # Bootstrap A, primitive boundary, Phase 1 subset
    ├── 04-concurrency.org   # Actor primary Phase 4, novelty claim
    ├── 05-roadmap.org       # Phase 1-5 timeline, release schedule
    ├── 90-ideas-backlog.org # Deferred interesting ideas
    └── papers/              # Academic paper reading stubs
        ├── README.org
        ├── rhodes-2008-sbcl.org           # Priority A: bootstrap methodology
        ├── corallo-2020-native-comp.org   # Priority A: AOT hybrid
        ├── bolz-2009-meta-tracing.org     # Priority A: JIT for Phase 3-4
        └── maclachlan-1992-python.org     # Priority A: CL-in-CL precedent

Optional packages

NeLisp v1.0 now supports an SBCL/Quicklisp-style split between the core src/ tree and individually extractable libraries under packages/*/. The nelisp-json package is the pilot extraction and defines the canonical layout for future package moves:

  • packages/<name>/src/<name>.el
  • packages/<name>/test/<name>-test.el
  • packages/<name>/README.org

The build keeps these packages on the Emacs load-path, so existing require forms continue to work unchanged. See packages/README.org and packages/nelisp-json/README.org.

Related work (audited)

Project Approach Status / diff with NeLisp
Guile-Emacs Elisp on Guile (Scheme) 12-year stall, relaunched EmacsConf 2024
native-comp Elisp → libgccjit → .eln Shipped in Emacs 28+, does not rewrite VM
SBCL CL in CL (CMU CL fork) Methodology source (Rhodes 2008)
PyPy Python in RPython + JIT Successful self-host, STM branch failed
PicoLisp Tiny Lisp, pil21 LLVM pil21 self-hosts LLVM phase
NeLisp Elisp in Elisp, editor-native First editor-integrated self-host

See docs/01-related-work.org §10 for the synthesis.

License

TBD. Leading candidate: GPLv3+ (to match Emacs) with per-module reconsideration if needed. Decided before Phase 1 push.

Contact

zawatton — kurozawawo@gmail.com