Fe — Embedded Lisp by rxi
What it is
Section titled “What it is”Fe is a Lisp-family scripting language by rxi (same author as lite text editor, microui immediate-mode UI, json.lua, and a dozen other “small focused thing in a tiny implementation” projects). The implementation is ~800 source lines of ANSI C with no dependencies and no malloc — all allocations come from a single fixed-size memory arena the embedder provides at init time.
The dialect is intentionally minimal:
- Numbers, symbols, strings, pairs, lambdas, macros
- Lexical scoping with first-class closures
- Tail-call optimization (TCO)
- Mark-and-sweep GC over the embedder-provided arena
- Embeddable via a small C API (
fe_open,fe_eval,fe_pushnumber,fe_call, etc.) - No standard library beyond core primitives — the embedder provides whatever I/O, FFI, or domain primitives the application needs
The deliberate omissions: no file I/O (embedder supplies), no FFI scaffolding (embedder supplies), no module system, no exceptions (errors call a handler the embedder set up). Everything not in the 800 lines is the embedder’s job.
Why KN-86 adopted Fe
Section titled “Why KN-86 adopted Fe”Per ADR-0004: the KN-86 cart runtime needed an embeddable bytecode-or-tree-walker Lisp with arena memory discipline (no malloc, hard memory budget per cart), deterministic teardown (arena reset = cart unload, nothing leaks), small code footprint (the runtime ships on a 32 GB SD card and runs on a Pi Zero 2 W; every megabyte counts), and C embedding friendliness (NoshAPI per ADR-0005 is 57+ C primitives exposed to cart Lisp as builtins). Fe hit every requirement.
The alternatives considered in ADR-0004:
- MicroPython / CircuitPython — too big, runtime cost too high, GC behavior too opaque for memory budgeting
- Lua — bigger than Fe (~10× LoC), great C embedding, but the not-quite-Lisp dialect doesn’t carry the cartridge-as-Lisp-program identity KN-86 wants (
ADR-0001) - mal — Make a Lisp (
mal.md) — pedagogical, not embedding-grade; valuable as the curriculum for learning Fe-style implementation, not as the runtime itself - Scheme (S7, TinyScheme, Chibi) — production-grade but each carries 5-30× more LoC than Fe, and the SRFI / call-cc machinery is overkill
- Janet — modern, beautiful, but larger and with its own runtime expectations that don’t fit the Fe-style arena-reset model
- Custom from scratch — building a Lisp from scratch (
build-your-own-lisp.mdis the canonical book) is months of work to reach Fe’s stability; not justified when Fe is sitting on the shelf
Fe won on every axis that mattered, and the rxi authorship signals long-term not aiming for feature creep — the project is stable because the author is famously disciplined about scope (lite is ~2k LoC; microui is ~1.5k; the pattern holds).
Where Fe lives in the KN-86 source tree
Section titled “Where Fe lives in the KN-86 source tree”| Path | Role |
|---|---|
kn86-emulator/src/fe/ | Vendored Fe source — fe.c, fe.h, plus the KN-86 embedder glue. |
kn86-emulator/src/nosh.c | NoshAPI C primitive registrations into the Fe VM. |
kn86-emulator/src/cartridge.c | Cart loader — opens a cart’s .kn86 container (ADR-0006), allocates the arena, loads the Lisp source + static data (tree-walked on load; no bytecode), runs the cart’s init thunk. |
kn86-emulator/src/cart_unload.c | Cart teardown — fe_close + arena reset; deterministic, no leaks possible. |
The runtime budget per cart is set per the cart’s .kn86 manifest; typical carts get ~64 KB arena which is enormous relative to Fe’s overhead (~4 KB for the VM itself).
What we borrow from the rxi project that’s not Fe specifically
Section titled “What we borrow from the rxi project that’s not Fe specifically”The rxi authoring philosophy is worth borrowing wholesale:
- Small enough to vendor. Fe lives in our source tree (
kn86-emulator/src/fe/) as vendored copies, not as a submodule, not as a package dependency. We can read it, understand it, modify it if we have to. The 800 LoC is small enough that this is practical. - No GitHub Actions / npm / CMake complexity. Fe builds with a single
cc fe.c -c -o fe.o. Our build chain handles it as one more source file. - No “framework” — just a library. Fe doesn’t impose application structure. NoshAPI gets to be NoshAPI; Fe doesn’t have opinions about what FFI primitives the embedder exposes.
- The README is the spec. rxi’s docs are minimal-and-correct rather than exhaustive-and-aspirational. KN-86’s own runtime docs should match this posture.
What we are explicitly NOT doing
Section titled “What we are explicitly NOT doing”- Not modifying Fe upstream. Fe-the-language is stable. If KN-86 needs something Fe doesn’t have (additional core primitive, different number type), we add it in our embedder layer (
nosh.c), not by forking Fe. This keeps Fe upgradeable from rxi’s upstream if a security fix or perf improvement lands. - Not adopting the full rxi library set.
liteis a text editor, not a runtime substrate;microuiis an immediate-mode UI library that overlaps awkwardly with our 80×25-grid model. We borrow Fe specifically.
Related rxi projects worth glancing at
Section titled “Related rxi projects worth glancing at”lite— minimal text editor in C+Lua. Pattern: tiny C core + Lua scripted UI. Architecturally interesting; not adopted.microui— immediate-mode UI library, ~1500 LoC C. Pattern: tiny scope, frame-by-frame UI rebuild. The IM-style isn’t a fit for KN-86’s 80×25 grid + cell-state model, but the discipline is exemplary.json.lua— JSON parser in 300 LoC of Lua. Pattern: one job, done right.
- Cross-link
ADR-0001— the decision to use Lisp for cart authoring. - Cross-link
ADR-0004— the decision to use Fe specifically. - Cross-link
ADR-0005— NoshAPI: the FFI surface exposed to cart Lisp. - Cross-link
ADR-0006— the.kn86container format that ships Lisp source + static data (AOT bytecode deferred). - Cross-link
ADR-0012— the Lisp slot table widening for the 14-Lisp-primitive function block. - Cross-link
build-your-own-lisp.md+mal.md** — pedagogical references; useful as background reading for anyone trying to understand what Fe is by understanding what implementing a Lisp looks like. - License: MIT. Permissive; commercial use fine; no attribution-in-binary requirement (though we credit rxi anyway in
_archive/and ADR-0004).