Commit Graph

5 Commits

Author SHA1 Message Date
ebfd8c3652 examples: lisp — own STR bytes; dotted-pair literals
vstr now copies the input bytes into the trans arena and promote_v
does the same into perm at the top-level boundary. STR cells used
to borrow the lexer's input slice; the REPL's buf-shift between
forms overwrote those bytes, so a top-level (define x "...") would
print garbage after the next read. Mirror of Hare's strings::dup,
arena-routed so the bytes share the cell's lifetime.

Parser learns dotted-pair literals: '(a b . c) splices the tail
into the cdr of the last cons. A bare '.' inside a list lexes as
tkind.DOT; outside a list it's still a parser error. Pre-fix the
'.' lexed as a one-byte SYM, producing a 3-element proper list.

Drop the unused args_to_slice — eval inlines on purpose (the
wwstage cgen drops slice.len through a tagged-union return).

Tests: 18 new probes (str-survives-3-defines, str-from-lambda,
dotted-pair walk + error edges) + a check_str helper. 101/101.
2026-05-13 01:41:47 +09:00
1c184ee6aa examples: lisp — perm/trans split, promote-on-define, slice free
Two bump arenas. arena_reset_trans() runs between top-level forms;
top-level define / set! deep-copy the bound value graph into perm
via Cheney-style forwarding (pin = -1 + stashed fwd pointer in
.car/.val) so no perm cell ever points into trans. Args slice in
eval's apply path also gets explicit os.free per dispatch — without
that the rt_ensure page-per-call leak dominated and masked the
reset. test_huge peaks at ~2.6 MB under massif --pages-as-heap=yes,
down from ~525 MB pre-arena (~200x).
2026-05-12 23:27:04 +09:00
fa33357821 examples: lisp — chunked bump arena for value/env cells
Replaces rt_alloc-per-cell (one 4 KiB mmap each) with arena_alloc
over 64 KiB chunks. test_huge peak under massif --pages-as-heap=yes
drops from ~525 MB to ~253 MB. Same lifetime semantics; remaining
bulk is per-call append() in eval's arg slice (rt_ensure still
mmaps page-per-call).
2026-05-12 23:12:40 +09:00
78b1cbfb6a examples: lisp — proper tail calls in eval 2026-05-12 22:54:34 +09:00
ab173b095a examples: lisp — pure-ww Lisp interpreter, REPL, in-process tests
Demo program that lives entirely on lib/* and libwwrt.a — no @symbol
FFI of its own. The interpreter sits in lispcore.ww (exports for the
test driver); lisp.ww is a 3-line entry that calls lispcore.repl().

Language surface: integers, floats, symbols, strings, lists, lambdas
with closures, define / set! / if / quote / let / begin, recursion
(fact / fib / ackermann / gcd), map / filter / reduce as user code.

REPL is line-buffered: each read tries to parse one top-level form,
asks for more on "unterminated list", evaluates and prints, then
shifts consumed bytes off the front of the buffer. Lookahead-aware —
the parser primes one extra token so we shift to L.curstart, not
L.pos, otherwise the first byte of the next form gets eaten.

lisp_test.ww exec'd as a regular binary (ww test drops -I in single-
file mode); 66 probes cover arithmetic, lists, closures, recursion,
errors. test_*.lisp drive the live REPL through `make demo`.

The wwstage cgen still mis-lowers a handful of patterns at this
shape of program — top-level array indexing, global-ptr deref,
two-level field stores, f64 routing through *T, alloc(structlit{})
for f64/str fields, (slice | E) returns, xs[i].kind chains, f64
compound assigns. Each workaround is annotated at its use site;
the full taxonomy is in examples/lisp/CLAUDE.md.
2026-05-12 22:33:24 +09:00