Emacs Internal #01: is a Lisp Runtime in C, Not an Editor

February 26, 2026 [c, project, compiler, emacs] #c #system-design #history #lisp #compiler

I tried to move to an LLM-friendly platform like VSCode or Cursor, but I kept returning to GNU Emacs. After reading other users' stories, I realized this is a common pattern. Very few tools survive 40 years and still feel hard to leave.

I read parts of the source code and discovered that Emacs is not just a code editor. There is design philosophy, system software trade-offs, and code that still feels like a treasure today. I want to record some personal discoveries that might be worth sharing.

Before we dive into the implementation, here is the "why" and the history I looked up.

The Church: why people cannot leave GNU Emacs

Starting with the long-standing joke:

Emacs, "a great operating system, lacking only a decent editor" -- Editor War

Joshua Blias's Returning to the Church (of Emacs) is a true story about switching to Neovim and coming back to GNU Emacs.

Here are common reasons why, in this modern world, people still use GNU Emacs, from a Reddit post.

So here is my first big question:

The history: Why Emacs Embeds an Elisp Interpreter in C

In the 1970s, hackers at the MIT AI Lab used a text editor called TECO. Unlike modern editors with a cursor, users had to input a sequence of password-like strings to cast the magic that edits text (TECO manual). To reduce the pain, people started writing "macros" to speed up the process.

TECO layout

As macros grew larger and more complicated, they needed variables, if-else control flow, and loops. At that point, Richard Stallman and Guy Steele (the creator of Scheme) made a decision: "If the macro is complicated enough to act like a programming language, why not give it a real Turing-complete programming language?"

This is the birth of Emacs (Editor MACroS). An interpreter made the editor itself programmable, so users could extend and evolve it live without recompiling or waiting for upstream changes. In the earliest Emacs, the "interpreter" was just TECO's macro language. Later, GNU Emacs adopted Emacs Lisp. Lisp was a natural choice because its syntax is simple, its macros are powerful, and the interpreter is small and flexible, which makes live customization easy.

Later, Lisp machines were a commercial failure, and C on von Neumann architecture dominated the industry. When Richard Stallman and the Free Software Foundation wanted a free Emacs on Unix, there was no Lisp environment there. So he wrote a Lisp virtual machine and interpreter core in C, effectively reviving the spirit of Lisp machines in a Unix ecosystem, because it was the path of least resistance to a complete Unix toolchain.

This helps explain why GNU Emacs' source code looks the way it does, and why jokes like "a great operating system" evolved.


Things learned from the Story

GNU Emacs source code directly

After understanding more about Emacs' history, the code and directory layout feel more reasonable. In this series, we'll discuss how C implements the Lisp interpreter, memory allocation, dynamic binding, and more.

Worse is better: human nature

As to why C became dominant and not Lisp, a classic articulation of this "less elegant but more successful" outcome is Richard P. Gabriel's The Rise of Worse is Better. Sometimes the real world works this way too...

Greenspun's Tenth Rule

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. -- Philip Greenspun

In more direct words: "Don't fool yourself: it starts as a simple config, but it won't stop there." Humans have unlimited desire, and long-lived software eventually evolves into a DSL and needs an embedded virtual machine.

Vimscript is a perfect victim of this Greenspun's Rule. On the other side, Richard Stallman's vision already foresaw this curse.

So Vim eventually needed a fork: Neovim using a Lua runtime (still an interpreter inside). They chose Lua because LuaJIT is a modern, fast runtime. Now Lua turns Neovim into a "Lua virtual machine text editor platform." Richard Stallman would probably laugh and say, "we did this 40 years ago."

Although the idea looks similar, Neovim often feels faster than Emacs in practice because it leans on newer runtimes and techniques (e.g., LuaJIT, async jobs, and RPC). The following are some ways Neovim outperforms Emacs.

Emacs LispNeovim
architectureMonolithic C core + embedded Elisp interpreterC core + embedded LuaJIT + Msgpack RPC host/guest plugins
concurrencysingle-threaded main loopevent loop; async jobs and RPC-based plugins
union taggingtagged pointers + cons cells/immediatesNaN-tagging in LuaJIT

For modern editors like VSCode, an interpreter is still inside. VSCode is essentially a web browser for code, built on Electron (Chromium + V8). That means a VM is part of the editor's core. From a language-design viewpoint it may look less elegant (JavaScript is slow), but in practice it feels fast because of JIT, SIMD, and async IPC that keeps the UI responsive. Either way, the pattern repeats: a VM sits at the core of the editor.

Appendix (PS): Other Greenspun's Tenth Rule victims

This is also why my cache simulator Stratum uses Racket to create a DSL for cache configuration.

Next step: How to build a tiny Emacs Lisp interpreter in C with only seven elements

  1. quote
  2. atom
  3. eq
  4. car
  5. cdr
  6. cons
  7. cond

Emacs Internal Series: