Chez: runtime eval / load-string / defmacro on the zero-Janet spine

The compiler image is already resident at runtime on the Chez spine, so eval
and load-string are just wiring: make them clojure.core functions instead of
analyzer special forms.

- eval / load-string are now functions, not special forms. Dropped "eval" from
  the host-contract special-symbol lists so it resolves as an ordinary var, and
  def-var! both in compile-eval.ss. eval takes an already-read form (e.g. from
  quote/list) and compiles+evals it in the current ns; load-string reads every
  form from a source string and evals each, returning the last.
- Runtime defmacro: jolt-compile-eval-form intercepts a (defmacro ...) form
  before analysis, defs the expander fn + mark-macro!s the var, exactly as
  emit-image.ss does at build time. The two helpers (macro-form? / defmacro->fn)
  move to compile-eval.ss and emit-image.ss reuses them.
- Top-level (do ...) is now unrolled form-by-form, like Clojure, so a defmacro
  or def in an earlier subform is visible (macro flag set / var interned) before
  a later subform is analyzed. This is what makes multi-form -e with a macro work.

Seed is byte-identical (no source references eval), so no re-mint; bootstrap-test
still passes. Zero-Janet corpus 2534 -> 2544 (eval/load-string cases now run),
0 new divergences; floor raised. Prelude corpus, JVM cert, full Janet gate green.

jolt-r8ku
This commit is contained in:
Yogthos 2026-06-20 12:14:08 -04:00
parent a8b61283b3
commit a23095b502
5 changed files with 112 additions and 41 deletions

View file

@ -30,31 +30,8 @@
(and (pair? items) (symbol-t? (car items))
(string=? (symbol-t-name (car items)) "ns")))))
;; Is `f` a (defmacro ...) / (definline ...) form?
(define (ei-macro-form? f)
(and (cseq? f) (cseq-list? f)
(let ((items (seq->list f)))
(and (pair? items) (symbol-t? (car items))
(let ((h (symbol-t-name (car items))))
(or (string=? h "defmacro") (string=? h "definline")))))))
;; (defmacro NAME [docstring] [attr-map] params body...) -> (values "NAME" (fn ...)).
;; Mirrors driver.janet defmacro->fn: strip a leading docstring (native string) and
;; an attr-map (a pmap that isn't a symbol), then re-head the rest with `fn` so a
;; destructured macro arglist desugars before lowering. We emit the BARE fn (the
;; caller wraps it in def-var! + mark-macro!), never a (def NAME ...) — interning
;; NAME would make require skip the real macro (jolt-r9lm).
(define (ei-defmacro->fn f)
(let* ((items (seq->list f))
(name-sym (cadr items))
(after-name (cddr items))
(a1 (if (and (pair? after-name) (string? (car after-name)))
(cdr after-name) after-name))
(after-meta (if (and (pair? a1) (pmap? (car a1)))
(cdr a1) a1))
(fn-sym (jolt-symbol #f "fn")))
(values (symbol-t-name name-sym)
(apply jolt-list (cons fn-sym after-meta)))))
;; ei-macro-form? / ei-defmacro->fn moved to compile-eval.ss (ce-macro-form? /
;; ce-defmacro->fn, loaded before this) — shared with the runtime defmacro spine.
;; Cross-compile one namespace's source to a list of guard-wrapped Scheme strings.
;; Mirrors driver.janet emit-ns-forms-list/emit-core-prelude + emit-form-scheme.
@ -70,8 +47,8 @@
(ce-scan-requires! f ns-name)
(cond
((ei-ns-form? f) (loop (cdr forms) acc))
((ei-macro-form? f)
(let-values (((nm fn-form) (ei-defmacro->fn f)))
((ce-macro-form? f)
(let-values (((nm fn-form) (ce-defmacro->fn f)))
(let ((scm (guard (e (#t #f))
(let ((ctx (make-analyze-ctx ns-name)))
(jolt-ce-emit (jolt-ce-analyze ctx fn-form))))))