Chez Phase 3 inc6a: zero-Janet spine (analyzer + emitter on Chez), macro-free
Cross-compile jolt.ir + jolt.analyzer + jolt.backend-scheme to Scheme def-var! forms via the existing Janet emit pipeline (driver/emit-compiler-image) and run them ON CHEZ over a Scheme jolt.host impl. A macro-free Clojure expression now compiles and runs with no Janet in the loop: read (reader.ss) -> analyze (jolt.analyzer on Chez) -> IR -> emit (jolt.backend-scheme on Chez) -> eval. host/chez/host-contract.ss is the jolt.host contract on Chez (the portable seam the cross-compiled analyzer/emitter call): form-* over the Chez reader's forms, resolve-global/compile-ns/host-intern!/late-bind? over the var-cell registry. ctx is an opaque record carrying the compile ns. Native-op names are declare-var!'d into clojure.core so +, *, <, ... classify as :var and the emitter's native-op path lowers them. form-macro? is a #f stub and macro expansion / syntax-quote / record hints are stubs for inc6b (runtime macros, jolt-r8ku). host/chez/compile-eval.ss is the spine entry (read-string -> analyze -> emit -> eval). driver gains emit-compiler-image / ensure-compiler-image (image caching) and program-zero-janet / eval-zero-janet. Two bugs fixed in the keeper, not reproduced: - the Chez reader stores an unqualified symbol's ns as #f, but the analyzer tests (nil? ns); hc-sym-ns normalizes #f/'() -> jolt-nil. Without it every handled special (if/do/fn*) misanalyzed as a plain invoke. - char (int->char) was missing from clojure.core on Chez; the emitter's chez-str-lit needs it for keyword/string consts. Added jolt-char to converters.ss. Gate: test/chez/spine-test.janet 15/15 (Chez-hosted analyzer value == Janet-hosted oracle through the same emitter/RT; only the analysis host differs). Full Janet gate green (150 files). driver.janet is in the prelude fingerprint so the cache key moved; prelude content is unchanged. jolt-chez fingerprint/ensure-prelude made public for the test harness.
This commit is contained in:
parent
c47ae7fd22
commit
c2d977cadf
6 changed files with 398 additions and 2 deletions
|
|
@ -229,6 +229,101 @@
|
|||
(cset-prelude! ctx false)
|
||||
[(string/join out "\n") emitted total])
|
||||
|
||||
# --- analyzer/emitter cross-compile (jolt-hs9n, the zero-Janet spine) ---------
|
||||
# Phase 3 inc6: cross-compile the PORTABLE compiler (jolt.ir + jolt.analyzer +
|
||||
# jolt.backend-scheme) to Scheme def-var! forms so analyze->IR->emit runs ON CHEZ.
|
||||
# Same emit pipeline as the core prelude, but for jolt-core/jolt/* namespaces
|
||||
# rather than clojure.core: jolt.* refs lower to var-deref (the prelude-mode gate
|
||||
# only rejects clojure.* refs), clojure.core refs resolve from the loaded prelude,
|
||||
# and the jolt.host form-*/resolve-global/... refs resolve from host-contract.ss.
|
||||
|
||||
(defn- emit-ns-forms-list
|
||||
"Cross-compile one namespace's source to a list of guard-wrapped def-var! Scheme
|
||||
strings (prelude mode must already be ON). Registers the ns' requires/aliases in
|
||||
ctx first so cross-ns refs resolve at emit time; skips ns + macro forms (macros
|
||||
are analyze-time only, already expanded at their use sites)."
|
||||
[ctx ns-name src]
|
||||
(tctx/create-ns ctx ns-name)
|
||||
(tctx/ctx-set-current-ns ctx ns-name)
|
||||
(def out @[])
|
||||
(each f (parse-all src)
|
||||
(def ns-form? (and (indexed? f) (> (length f) 0) (= "ns" (sym-name (in f 0)))))
|
||||
(if ns-form? (protect (api/eval-one ctx f)) (scan-eval-requires! ctx f))
|
||||
(unless (or ns-form? (macro-form? f))
|
||||
(def res (protect (cemit ctx (backend/analyze-form ctx f))))
|
||||
(when (res 0)
|
||||
(array/push out (string "(guard (e (#t #f))\n " (res 1) ")")))))
|
||||
out)
|
||||
|
||||
(def compiler-ns-files
|
||||
[["jolt.ir" "jolt-core/jolt/ir.clj"]
|
||||
["jolt.analyzer" "jolt-core/jolt/analyzer.clj"]
|
||||
["jolt.backend-scheme" "jolt-core/jolt/backend_scheme.clj"]])
|
||||
|
||||
(defn emit-compiler-image
|
||||
"Cross-compile the analyzer pipeline (jolt.ir + jolt.analyzer +
|
||||
jolt.backend-scheme) to a Scheme string of prelude-mode def-var! forms — the
|
||||
analyze->IR->emit spine running ON CHEZ (jolt-hs9n). Load AFTER rt.ss +
|
||||
host-contract.ss + the core prelude. Returns [scheme total]."
|
||||
[ctx]
|
||||
(ensure-clj-emitter ctx)
|
||||
# ensure-analyzer is lazy; a trivial analyze builds jolt.ir/jolt.analyzer/
|
||||
# jolt.passes in the Janet ctx so their vars resolve while we emit their source.
|
||||
(protect (backend/analyze-form ctx (in (r/parse-next "nil") 0)))
|
||||
(cset-prelude! ctx true)
|
||||
(def prev-ns (tctx/ctx-current-ns ctx))
|
||||
(def out @[])
|
||||
(each [ns-name path] compiler-ns-files
|
||||
(array/concat out (emit-ns-forms-list ctx ns-name (slurp path))))
|
||||
(tctx/ctx-set-current-ns ctx prev-ns)
|
||||
(cset-prelude! ctx false)
|
||||
[(string/join out "\n") (length out)])
|
||||
|
||||
(defn ensure-compiler-image
|
||||
"Build (once) and return the path to the cross-compiled compiler image — the
|
||||
jolt.ir/jolt.analyzer/jolt.backend-scheme def-var! forms (jolt-hs9n). Cached on
|
||||
disk keyed by the same fingerprint scheme as the prelude; pass an explicit path
|
||||
to control caching from the test harness."
|
||||
[ctx path]
|
||||
(unless (os/stat path)
|
||||
(def [img _] (emit-compiler-image ctx))
|
||||
(spit path img))
|
||||
path)
|
||||
|
||||
(defn program-zero-janet
|
||||
"Assemble a fully self-hosted Chez program: rt.ss + the core prelude +
|
||||
host-contract.ss + the cross-compiled compiler image + compile-eval.ss, then
|
||||
compile AND eval `src` ON CHEZ (read->analyze->emit->eval, no Janet). The
|
||||
zero-Janet spine (jolt-hs9n)."
|
||||
[prelude-path image-path src ns]
|
||||
(string
|
||||
"(import (chezscheme))\n"
|
||||
"(load \"host/chez/rt.ss\")\n"
|
||||
"(set-chez-ns! \"clojure.core\")\n"
|
||||
"(load " (string/format "%j" prelude-path) ")\n"
|
||||
"(load \"host/chez/post-prelude.ss\")\n"
|
||||
"(set-chez-ns! \"user\")\n"
|
||||
"(load \"host/chez/host-contract.ss\")\n"
|
||||
"(load " (string/format "%j" image-path) ")\n"
|
||||
"(load \"host/chez/compile-eval.ss\")\n"
|
||||
"(printf \"~a\\n\" (jolt-final-str (jolt-compile-eval "
|
||||
(string/format "%j" src) " " (string/format "%j" ns) ")))\n"))
|
||||
|
||||
(defn eval-zero-janet
|
||||
"Compile+run `src` through the ON-CHEZ analyzer/emitter (zero Janet). Needs a
|
||||
prebuilt core prelude (`prelude-path`) and compiler image (`image-path`).
|
||||
Returns [code stdout stderr]."
|
||||
[prelude-path image-path src &opt ns scheme-out]
|
||||
(default ns "user")
|
||||
(def prog (program-zero-janet prelude-path image-path src ns))
|
||||
(def path (or scheme-out (string "/tmp/jolt-zero-janet-" (os/getpid) ".ss")))
|
||||
(spit path prog)
|
||||
(def proc (os/spawn ["chez" "--script" path] :p {:out :pipe :err :pipe}))
|
||||
(def out (drain (proc :out)))
|
||||
(def err (drain (proc :err)))
|
||||
(def code (os/proc-wait proc))
|
||||
[code (string/trim out) (string/trim err)])
|
||||
|
||||
(defn program-with-prelude
|
||||
"Assemble a runnable Chez program that loads rt.ss, loads the assembled core
|
||||
prelude from `prelude-path` (a file written once), then prints `final-scm`."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue