feat(compile): Phase 1 — wire shared persistent env (compile mode now works across forms)

Compile mode was broken: compiled defns interned only into the jolt namespace,
which Janet's eval couldn't see, so calling a compiled function threw 'unknown
symbol'. And load-string ignored :compile? entirely.

- Each context gets a persistent Janet env (ctx-janet-env), a child of the
  compiler module env (so core-* resolve) holding the context's user defs.
  compile-and-eval evals into it, so def/defn persist and resolve across forms;
  contexts stay isolated. nil ctx (one-off eval) gets a fresh child.
- Emit a named fn for defn ((def f (fn f [..] (f ..)))) so recursion resolves
  lexically (the anonymous form couldn't forward-reference f at compile time).
- Extract eval-one (per-form routing) and use it in both eval-string and
  load-string, so load-string honors :compile?. Stateful forms still interpret.

compiled fib(30): ~50s (interpreted) -> 3.4s (~15x). spec: compile-mode-test
gains cross-form/recursion/def + context-isolation cases. jpm test green.
Fast operator emission (the rest of the win) is Phase 2.
This commit is contained in:
Yogthos 2026-06-05 17:50:22 -04:00
parent b6e66e53d2
commit f74af5dbc5
3 changed files with 90 additions and 40 deletions

View file

@ -77,6 +77,23 @@
(print " complex...")
(assert (= 6 (ct-eval ctx "(let [f (fn [n] (loop [i 0 acc 0] (if (< i n) (recur (inc i) (+ acc i)) acc)))] (f 4))")) "nested")
(assert (= 15 (ct-eval ctx "(reduce + (map inc [0 1 2 3 4]))")) "reduce+map"))
(assert (= 15 (ct-eval ctx "(reduce + (map inc [0 1 2 3 4]))")) "reduce+map")
# Phase 1 wiring: compiled defns persist across forms (the per-context Janet
# env) and recurse correctly (named-fn self-reference).
(print " cross-form defns + recursion...")
(eval-string ctx "(defn fib [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))")
(assert (= 832040 (ct-eval ctx "(fib 30)")) "recursive fib across forms")
(eval-string ctx "(defn sq [x] (* x x))")
(eval-string ctx "(defn sum-sq [a b] (+ (sq a) (sq b)))")
(assert (= 25 (ct-eval ctx "(sum-sq 3 4)")) "defn calling earlier defn")
(eval-string ctx "(def base 100)")
(assert (= 142 (ct-eval ctx "(+ base 42)")) "compiled def referenced later"))
# Context isolation: a def in one compiled context is invisible in another.
(let [a (init {:compile? true}) b (init {:compile? true})]
(eval-string a "(def secret 7)")
(assert (= 7 (ct-eval a "secret")) "def visible in its own ctx")
(assert (not ((protect (ct-eval b "secret")) 0)) "def isolated to its ctx"))
(print "\nAll Phase 6 tests passed!")