examples: lisp — proper tail calls in eval

This commit is contained in:
2026-05-12 22:54:34 +09:00
parent ab173b095a
commit 78b1cbfb6a
5 changed files with 282 additions and 166 deletions

View File

@@ -94,6 +94,25 @@ always one of the above and shows up under valgrind/gdb the same
way it did the first time: silently dropped store, missing field
read, garbage payload after a tagged-union return.
## Tail-call optimization
`eval` is a single `for(true)` trampoline; a tail position rewrites
`v` (current expr) and `cure` (current env) in place and `continue`s
instead of recursing. Tail positions are:
- the chosen branch of `if`
- the last form of `begin` / `let` / a lambda body
- a direct call in any of the above
A `tailed` flag flips on the first jump into a fresh scope (LET
binding, lambda body) so subsequent `define`/`set!` mutate the local
`cure` chain rather than the caller's `ein` slot — same scoping the
old recursive `run_body(_, &pe)` path gave.
Mutual recursion still doesn't work — that's a `define` tie-back
limitation, not a TCO one. Defining `evn?` before `od?` captures an
env where `od?` is unbound; the tie-back only adds the self-binding.
## Interpreter limitations (design, not bug)
- **No GC.** Every cons / value / env frame is `mmap`'d via
@@ -101,10 +120,9 @@ read, garbage payload after a tagged-union return.
the process exits. The test_huge demo peaks at ~595 MB under
`--pages-as-heap=yes`. Per-top-level-form arena reset would cut
this by ~100× — see the closing note in `repl()` for the hook
point.
- **No tail-call optimization.** Recursion grows the C-side stack
one frame per Lisp call. `(spin 100000 0)` will eventually stack-
overflow even though it's tail-recursive in source.
point. Note: `rt_alloc` is one mmap syscall per call returning a
whole 4KB page, so even tail-recursive loops are bottlenecked on
allocation, not on Lisp work — `(spin 100000 0)` runs in ~4s.
- **No bigints.** `i64` wraps silently on overflow. `(fact 21)`
rolls over.
- **Float printing is fixed `%.6f`.** `1.0` prints as `1.000000`.