Replaces header format from [size:63 | mark:1] with [size:48 | type:8 | flags:8 (mark in bit 0)]. Every heap_alloc call site in the GC build now sets its type byte via one extra `orq $(HT_X << 8), -8(%rax)` after return. Ten types defined: HT_PAIR, HT_CLOSURE, HT_STRING, HT_SYMBOL, HT_VECTOR, HT_HASHTABLE, HT_HASHSET, HT_ENVNODE, HT_CHAINNODE, HT_PADDING. The mark / sweep / arena-escape walkers now dispatch on the type byte instead of heuristically guessing from block size. Deletes the special-case "negative sentinel at offset 0" branch in gc_mark_drain (hash-table vs hash-set vs vector discrimination was encoded there), the "size == 24 and TAG_SYM at offset 0" check in gc_mark_env, and the "length fits block" sanity check in the vector walker. All that logic collapses into a single compare on the type byte. Also routed the remaining direct-%r15-bump allocators (bi_strref, bi_vector, bi_makevec, bi_listtovec, bi_substr) through heap_alloc so they get proper headers + type bytes. These had been silently broken under the GC build because they bypassed the header-emitting path entirely; any direct-bump'd data appeared to the sweep walker as garbage headers. §6.6.4 cell 4 (asm GC + no snapshot) was crashing at first GC before this change. After: serves 5,000 HTTP requests at ~410 req/s, peak RSS 1,088 KB (one chunk), growth 972 KB — the collector hit its natural steady state. First time we've validated "naive GC as replacement for snapshot discipline" under real traffic. New §6.6.5 "Precise Block Typing" in the whitepaper documents the old heuristic bugs, the new header format, and the cost (one orq per alloc, 16 header bits) vs benefit (class of bugs eliminated). Updated §6.6.4 to reflect cell 4 passing. Remaining known issue: the hash-set bench on the GC build under very heavy sustained allocation still surfaces an occasional unbound-variable error. The precise-type fix addressed the observed HTTP crash; a deeper root-scan edge case remains. Tracked for Fix 2 work. 137 asm no-GC + 137 asm GC + 189 shared functional tests all pass. |
||
|---|---|---|
| asm | ||
| c | ||
| docs | ||
| examples | ||
| proof | ||
| tests | ||
| whitepaper | ||
| .gitignore | ||
| bench.py | ||
| CLAUDE.md | ||
| friction.sh | ||
| Makefile | ||
| README.md | ||
| stdlib.lsp | ||
| tests.py | ||
| uncommonlisp.py | ||
Lumbda
A Lisp/Scheme-derived, just-in-time lambda language. Four implementation tiers with MOAD defect isolation. Workloads migrate across basic UNIX systems.
Four implementation tiers sharing one wire format — Scheme source itself:
- Python bytecode VM — reference, full first-class continuations
- C tree-walker + bytecode VM — portable C, JSON portal
- C + x86_64 JIT — pattern-matched native code, 7–10× faster than CPython
- Pure x86_64 assembly — 22 KB stripped, zero libc, 14 syscalls
Feedback is the primitive across four scopes: continuations within a process, portal files across processes, S-expressions across implementations, TCP sockets across machines.
Home: lumbda.com
λ> (define (fib n)
(let loop ((a 0) (b 1) (i 0))
(if (= i n) a (loop b (+ a b) (+ i 1)))))
λ> (map fib (iota 10))
(0 1 1 2 3 5 8 13 21 34)
Usage
python3 uncommonlisp.py # interactive REPL
python3 uncommonlisp.py script.lsp # run a file
python3 uncommonlisp.py -e '(+ 1 2)' # eval an expression
python3 uncommonlisp.py --fast script.lsp # auto-compile (7-19x faster)
Bytecode compiler
Lumbda includes a stack-based bytecode compiler and VM. Enable it with --fast or (auto-compile! #t):
python3 uncommonlisp.py --fast examples/fibonacci.lsp
(auto-compile! #t)
(define (ack m n)
(cond ((= m 0) (+ n 1))
((= n 0) (ack (- m 1) 1))
(else (ack (- m 1) (ack m (- n 1))))))
(compiled? ack) ; => #t
(ack 3 4) ; => 125
The compiler handles: if, begin, and, or, when, unless, cond, define, set!, lambda, let, named-let, let*, letrec, do, call/cc, function calls with tail-call optimization. Macros are expanded at compile time. 20 specialized opcodes for hot builtins (+, -, *, =, <, car, cdr, cons, null?, etc.) avoid function call overhead.
Features:
- Explicit frame stack — compiled-to-compiled calls don't grow the Python stack
- Full continuations —
call/ccsupports upward continuations; generators work - Constant folding —
(+ 1 2)folds to3at compile time - Peephole optimizer — eliminates dead code (VOID+POP, JUMP-to-next)
(disassemble proc)— inspect generated bytecode
What's implemented
Core language
- Full lexical scoping and closures
- Tail-call optimization (TCO) — deep recursion never blows the stack
- Hygienic macros via
syntax-ruleswith ellipsis (...) support define-macro/defmacrofor procedural macroscall/cc— full continuations (escape + upward) in compiled codevalues/call-with-valuesdynamic-wind,guard,with-exception-handlerquasiquote/unquote/unquote-splicingwith proper nesting- R7RS internal defines with letrec* body semantics
- R7RS error objects
- Exact rational arithmetic —
(/ 1 3)→1/3,(+ 1/4 3/4)→1 - String ports —
open-input-stringopen-output-stringreadon ports - Mutable strings —
string-set!string-fill!string-copy! - Module system —
module/importwith export lists define-record-typewith(inherit parent)for single-inheritance- Pretty-print —
pp/pretty-print - Tracing —
(trace fn)/(untrace fn)
Special forms
define set! lambda λ if cond case and or when unless
begin let let* letrec letrec* named-let do
quasiquote define-macro define-syntax syntax-rules
let-syntax letrec-syntax apply eval values call/cc
dynamic-wind guard parameterize load error
module import define-record-type
Built-ins
- Arithmetic:
+-*/quotientremaindermoduloexptsqrtabsfloorceilingroundtruncateminmaxgcdlcmlogexptrig functions,numeratordenominator - Rationals:
(/ 1 3)→1/3, literal1/3syntax,exact/inexactconversion - Comparison:
=<><=>=zero?positive?negative?odd?even? - Pairs & lists:
conscarcdrlistlengthappendreversemapfor-eachfilterfold-leftfold-rightreduceanyeverysortpartitionfindtakedropzipflattenand more - SRFI-1:
lastfirst–fifthdeletelset-unionlset-intersectionlset-differenceunfoldlist-tabulate - Strings:
string-lengthstring-refstring-set!substringstring-appendstring-copystring-copy!string-fill!string->liststring->numberformatand more - Characters:
char->integerinteger->charchar-alphabetic?char-upcasechar-downcase - Vectors:
make-vectorvectorvector-refvector-set!vector-copyvector-copy! - Hash tables:
make-hash-tablehash-table-set!hash-table-refhash-table-keyshash-table-valueshash-table-walkand more - I/O:
displaywritenewlinereadread-charread-lineopen-input-stringopen-output-stringwith-output-to-string - File system:
file-exists?delete-filerename-filedirectory-filescurrent-directory - System:
command-lineget-environment-variablecurrent-timeexit - Python interop:
py-evalpy-execpy-importpy-callpy-attr - Compiler:
compilecompiled?disassembleauto-compile!
Standard library (stdlib.lsp)
Additional macros, string/list/numeric/tree utilities, alist/hash helpers, simple object system, SRFI-2/8/64 test framework.
Examples
python3 uncommonlisp.py --fast examples/fibonacci.lsp
python3 uncommonlisp.py --fast examples/generator.lsp
python3 uncommonlisp.py --fast examples/mergesort.lsp
python3 uncommonlisp.py examples/objects.lsp
;; Generator using full continuations
(auto-compile! #t)
(define (make-gen thunk)
(let ((k #f) (done #f))
(lambda ()
(if done 'done
(call/cc (lambda (return)
(if k (k return)
(begin (thunk (lambda (val)
(call/cc (lambda (next)
(set! k next) (return val)))))
(set! done #t) (return 'done)))))))))
(define counter (make-gen (lambda (yield)
(let loop ((i 0)) (yield i) (loop (+ i 1))))))
(counter) ; => 0
(counter) ; => 1
(counter) ; => 2
Running tests & benchmarks
make test # run 529 tests
make test-verbose # verbose output
make bench # compare interpreter vs bytecode vs CPython
make lint # syntax check all Python files
Portal — machine state migration
Serialize a running VM mid-computation, transfer to another machine, resume:
# Machine A: start a long computation with checkpoints
python3 uncommonlisp.py --fast examples/portal-prime.lsp
# saves prime-state.portal at checkpoint
# Machine B: resume from checkpoint
python3 uncommonlisp.py --portal-resume prime-state.portal
# continues from exact instruction
The portal captures the full env chain, compiled procedures, continuations, and frame stack as JSON. 16KB for a primality test in progress.
EML universality proof
The proof/ directory contains a formal verification that eml(x,y) = exp(x) - ln(y)
with constant 1 generates all elementary functions (arXiv:2603.21852v2).
Three approaches, benchmarked:
| Approach | Time | Guarantee |
|---|---|---|
| Python (numerical) | 0.04s | 1e-10 tolerance |
| Lumbda (numerical) | 59s | 1e-10 tolerance |
| Lean 4 (formal proof) | 1.5s | kernel-verified |
The formal proof is 40x faster than brute-force search with infinitely stronger
guarantees. See proof/benchmark_results.md for the full analysis — including
why this is MOAD-0001 (the sedimentary defect) at the proof methodology layer.
File layout
uncommonlisp.py interpreter + bytecode compiler (one file, ~3200 lines)
stdlib.lsp extended standard library
tests.py test suite (571 tests)
bench.py benchmarks vs CPython
examples/ example programs
proof/ EML universality proof (Python, Scheme, Lean 4)
Makefile make test / make bench / make repl