Historical internal name "uncommonlisp" retired in favor of the
public name "lumbda" ahead of lumbda.com going live. Scope of
this commit:
Source files renamed:
uncommonlisp.py -> lumbda.py
asm/uncommonlisp.s -> asm/lumbda.s
c/uncommonlisp.h -> c/lumbda.h
whitepaper/uncommonlisp-whitepaper -> whitepaper/lumbda-whitepaper (.rst + .pdf)
Binaries renamed (tracked ones; c/ was always gitignored):
asm/uncommonlisp, asm/uncommonlisp-gc, asm/uncommonlisp.o,
asm/uncommonlisp-gc.o -> asm/lumbda(-gc)(.o)
c/.gitignore -> ignores lumbda
Internal string updates (sed pass ordered longest-first):
asm/uncommonlisp -> asm/lumbda
c/uncommonlisp -> c/lumbda
uncommonlisp.py -> lumbda.py
UNCOMMONLISP_BIN -> LUMBDA_BIN (asm/test.sh env var)
"uncommonlisp> " -> "lumbda> " (asm REPL prompt baked into binary)
UNCOMMONLISP -> LUMBDA (macros, comments)
uncommonlisp -> lumbda (prose)
Binary portal magic updated:
"ULPORTAL" -> "LUMBDAB1" # "Lumbda Binary v1"
Old portal files are not backward-compatible — this is a deliberate
break since it's the rename moment. S-expression portals already
carry their own ";; lumbda-portal v1" header and remain cleanly
versioned.
WHITEPAPER.pdf / WHITEPAPER.rst symlinks repointed to the renamed
files. Makefile's whitepaper target targets lumbda-whitepaper.pdf.
Not changed (intentional, separate phases):
- Filesystem directory /home/fox/git/uncommonlisp itself
(fox renames locally and the gitlab repo URL in a follow-up)
- tests.py hardcoded cwd=/home/fox/git/uncommonlisp
(matches the current on-disk location; will flip when the
directory rename ships)
- Git history (immutable; old commits still say uncommonlisp,
which is correct — that's what they were)
Verified:
137 asm no-GC + 137 asm GC + 571 Python + 83 C + 189 shared
functional tests all pass under the new names.
bench-gc-http (2000 req): all 4 cells behave as expected
(cells 1/2 flat, 3 leaks, 4 bounded at 1 chunk).
Python REPL, C REPL, asm REPL all start cleanly.
122 lines
3.7 KiB
Markdown
122 lines
3.7 KiB
Markdown
# lumbda Architecture Documentation
|
|
|
|
> "A diagram is worth 10,000 words." — russell@unturf.com
|
|
|
|
Three implementations of the same Scheme language, sharing the same .lsp test files.
|
|
|
|
## Python Implementation (lumbda.py)
|
|
|
|
3,324 lines. Bytecode compiler + stack VM + full continuations + portal.
|
|
|
|

|
|
|
|
**Execution tiers:**
|
|
- Tree-walker (`leval`): default, handles all forms including macros
|
|
- Bytecode VM (`--fast`): 40 opcodes + superinstructions, 7-19x faster
|
|
- Python JIT prototype: exec()-based transpilation (labeled as prototype)
|
|
|
|
**Key features:**
|
|
- Full multi-shot continuations via explicit frame stack
|
|
- Portal: serialize VM state to JSON, resume on another machine
|
|
- Inline cache, constant folding, peephole optimizer
|
|
- Source maps for error reporting with line numbers
|
|
- Bytecode serialization (.lspc files)
|
|
|
|
**Tests:** 571 unit + integration tests (tests.py)
|
|
|
|
---
|
|
|
|
## C Implementation (c/)
|
|
|
|
8,120 lines. Tree-walker + bytecode VM + x86_64 JIT.
|
|
|
|

|
|
|
|
**Execution tiers:**
|
|
- Tree-walker: default, full special form support
|
|
- Bytecode VM (`--fast`): matching Python's opcodes
|
|
- x86_64 JIT (`--jit`): **10-24x faster than CPython**
|
|
|
|
**Key features:**
|
|
- NaN-boxed 64-bit values (zero-alloc numbers)
|
|
- Hash-map environments with parent chain + global shortcut
|
|
- Interned symbols
|
|
- Real JIT: mmap(PROT_EXEC) + raw x86_64 bytes
|
|
|
|
**Tests:** 76 unit + integration + JIT tests (test.c)
|
|
|
|
---
|
|
|
|
## Assembly Implementation (asm/)
|
|
|
|
2,592 lines of GNU assembler. 13KB binary. Zero dependencies.
|
|
|
|

|
|
|
|
**Design:**
|
|
- No C. No libc. Only Linux syscalls (read, write, mmap, exit)
|
|
- Tag-in-low-3-bits value representation
|
|
- Bump allocator on 64MB mmap'd page
|
|
- TCO via `jmp .eval_top` (never grows the stack)
|
|
- 34 builtins, all special forms
|
|
|
|
**Tests:** 75 unit + integration + functional tests (test.sh)
|
|
|
|
---
|
|
|
|
## JIT Pipeline (c/jit.c)
|
|
|
|
1,309 lines. Compiles Scheme AST directly to x86_64 machine code.
|
|
|
|

|
|
|
|
**What gets JIT'd:**
|
|
- if, cond, and, or (conditional jumps)
|
|
- +, -, *, =, <, >, <=, >= (native integer ops)
|
|
- let, let* (stack-allocated locals)
|
|
- Named-let loops (native jmp, zero call overhead)
|
|
- car, cdr, cons, null?, pair? (NaN-box pointer ops)
|
|
- Self-recursive calls (call/ret) and tail calls (jmp)
|
|
|
|
**What falls back to interpreter:**
|
|
- call/cc, macros, syntax-rules, quasiquote, modules
|
|
- String/vector/hash-table operations
|
|
- Any form the AST analyzer can't verify as integer-safe
|
|
|
|
---
|
|
|
|
## Performance Summary
|
|
|
|
All benchmarks measured in-process (no startup overhead) on the same machine.
|
|
|
|
| Implementation | ack(3,4) | fib(35) | sum-to(50k) | Binary |
|
|
|---------------|----------|---------|-------------|--------|
|
|
| **C + x86_64 JIT** | **0.19ms** | **0.09ms** | **0.55ms** | 171KB |
|
|
| CPython (native) | 1.3ms | 0.006ms | 5.5ms | ~5MB |
|
|
| C interpreter | 20ms | 0.06ms | 109ms | 171KB |
|
|
| Python bytecode VM | 149ms | 0.75ms | 437ms | 3,324 lines |
|
|
| Assembly (13KB) | ~8ms* | ~0.6ms* | ~43ms* | **13KB** |
|
|
|
|
*Assembly times include process startup + tokenizer + parser.
|
|
|
|
**The JIT runs Scheme faster than CPython runs Python** on recursive workloads:
|
|
ack(3,4) is 7x faster, sum-to(50k) is 10x faster. The JIT compiles Scheme AST
|
|
directly to x86_64 machine code via mmap(PROT_EXEC).
|
|
|
|
---
|
|
|
|
## Test Coverage
|
|
|
|
943 verified assertions across all implementations:
|
|
|
|
```
|
|
make test-all
|
|
Python unit/integration: 571 tests
|
|
C unit/integration/JIT: 83 tests
|
|
Assembly unit/int/func: 108 tests
|
|
Shared functional: 181 tests (Python + C)
|
|
Total: 943 assertions
|
|
```
|
|
|
|
The language is **R7RS Scheme**. lumbda is the project name — a play on
|
|
Common Lisp, since this is decidedly uncommon.
|