4 architecture diagrams (Graphviz DOT → PNG): python-architecture.dot — bytecode VM + continuations + portal c-architecture.dot — tree-walker + VM + JIT tiers asm-architecture.dot — syscalls-only, 13KB binary jit-pipeline.dot — AST → x86_64 machine code flow docs/README.md — full architecture docs with embedded diagrams and performance summary across all implementations. Makefile: add asm-repl, docs target, clean-docs. Header comments document all targets and test suites. CLAUDE.md: add "A diagram is worth 10,000 words" (russell@unturf.com), implementation table, test suite inventory. Assembly is 2.5-4x faster than C interpreter on recursive workloads. JIT remains 33x faster than hand-written assembly. |
||
|---|---|---|
| .. | ||
| asm-architecture.dot | ||
| asm-architecture.png | ||
| c-architecture.dot | ||
| c-architecture.png | ||
| gpu-architecture.md | ||
| jit-pipeline.dot | ||
| jit-pipeline.png | ||
| python-architecture.dot | ||
| python-architecture.png | ||
| README.md | ||
uncommonlisp 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 (uncommonlisp.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
| Implementation | ack(3,4) | sum-to(50k) | fib(35) | Binary |
|---|---|---|---|---|
| Python VM | 93ms | 515ms | 0.6ms | 3,324 lines |
| C interpreter | 22ms | 79ms | 0.09ms | 171KB |
| C + JIT | 0.2ms | 0.3ms | 0.09ms | 171KB |
| Assembly | ~5ms* | ~3ms* | ~0.1ms* | 13KB |
| CPython | 1.7ms | 7.8ms | 0.009ms | ~5MB |
*includes process startup + parse
Shared Test Suite
tests/functional.lsp — 114 tests that run identically in Python and C:
make test-all
Python: 571 tests
C: 76 tests (+ 114 functional)
Assembly: 75 tests
Total: 836 verified assertions



