Wire the real pipeline end to end: host/chez/driver.janet boots a compile-mode jolt ctx, runs the EXISTING Janet-hosted analyzer on actual Clojure source to real IR, feeds it to the Scheme emitter, and runs the result on Chez. Analysis stays on Janet (the analyzer ports to Chez in Phase 2); execution is on Chez. emit.janet now consumes live IR (pv/phm-normalized like the Janet backend) and covers what the analyzer actually emits, not the hand-built inc-1 shapes: - core ops arrive as :var clojure.core/+ etc., not :rt — lowered to native Scheme via a native-ops table (mirrors backend.janet's), `=` to jolt=. - var cells (host/chez/rt.ss): :def -> def-var!, :var -> var-deref. Late binding so cross-var calls (run -> count-point) and the entry crossing resolve at use. - named fns (defn / fn self-name) bind via letrec so self-recursion resolves. - unsupported stdlib/host refs (no core on Chez yet) are rejected at EMIT time (clean out-of-subset signal) instead of deref'ing to nil and failing at runtime. Number model: jolt is all-doubles (no ratios; (/ 1 2) is 0.5), so literals emit as flonums — matches the Janet host and keeps Chez out of exploding exact rationals (mandelbrot). jolt-num->string prints integer-valued without ".0". Two real bugs found via the corpus probe and fixed (regression rows added): - loop bound in parallel (Scheme named-let) but Clojure loop is sequential — a later init must see earlier bindings; wrap a let* around the loop. - #(...) shorthand gensyms params with a trailing `#`, invalid in Scheme — munge it to `_`. Gate: test/chez/emit-test.janet runs the real analyzer -> Chez for (+ 1 2), fib(30)=832040, mandelbrot run(40), and the two regressions, parity-checked against the Janet oracle (6/6). First parity number via the new subset probe (test/chez/run-corpus-chez.janet, JOLT_CHEZ_CORPUS=1): 182/182 compiled corpus cases pass, 0 divergences; 2473/2655 out of subset pending core on Chez. Full jpm/run-tests gate green (125 files). Chez tests skip cleanly without `chez`. Perf note (unchanged plan): emitted fib(30) ~23ms vs hand-Scheme ~5ms — the jolt-truthy? wrapper (~3x) plus flonum (not fixnum) arithmetic, both Phase-4 type-specialization levers.
56 lines
3.3 KiB
Markdown
56 lines
3.3 KiB
Markdown
# Chez port — Phase 0 test contract harness
|
|
|
|
The host-neutral correctness gate for the Chez re-host (epic jolt-cf1q). The
|
|
spec corpus is data, so the SAME contract validates every host.
|
|
|
|
## Files
|
|
- `extract-corpus.janet` — parses `test/spec/*.janet` `(defspec …)` tables as
|
|
data and writes `corpus.edn` (2655 `[label expected actual]` cases). The file
|
|
is valid as BOTH EDN (a future Chez-jolt runner) and Janet data (the runner
|
|
below). Regenerate: `janet test/chez/extract-corpus.janet`.
|
|
- `corpus.edn` — the extracted contract (generated; checked in for convenience).
|
|
- `run-corpus.janet` — drives a TARGET jolt binary, one fresh subprocess per case
|
|
(fresh ctx = per-case isolation), checking `(= expected actual)` prints `true`
|
|
at the CLI, or that a `:throws` case exits non-zero. Pluggable target:
|
|
- `janet test/chez/run-corpus.janet` # default build/jolt
|
|
- `JOLT_BIN=build/jolt-chez janet test/chez/run-corpus.janet` # Phase 1+
|
|
- `JOLT_CORPUS_LIMIT=400 …` # every-Nth stride, fast
|
|
- `known-divergences.edn` — allowlist of cases that diverge at the CLI boundary.
|
|
The gate fails only on a NEW divergence; known ones are reported but tolerated.
|
|
- `values-test.ss` / `../../host/chez/values.ss` — Phase 0a value model + tests.
|
|
|
|
## The reference baseline (2026-06-17, Janet `build/jolt`, compile mode)
|
|
2641/2655 pass; 14 known divergences. They split into:
|
|
- **interpret-vs-compile leniency** — `:throws` cases where interpret mode raises
|
|
but compile mode returns (`< nil`, `> with nil`, `neg? keyword`, `max`/`min-key`
|
|
on non-numbers). Several are also non-canonical vs JVM Clojure.
|
|
- **invoke-collection-as-fn** — the `transient / invokable lookup` suite invokes
|
|
transients/collections as fns (`((transient {:x 7}) :x)`); compile mode (and
|
|
JVM Clojure) reject it.
|
|
- **`xml-seq walks`** — one structural case.
|
|
|
|
The compile-only Chez host (JVM-canonical oracle) should MATCH OR FIX these. The
|
|
gate's job is to catch *regressions* the port introduces, not to bless these.
|
|
|
|
## Why the CLI boundary
|
|
The runner tests through `jolt -e`, exactly how the Chez host will be exercised —
|
|
not the in-process `eval-string` the Janet `defspec` harness uses. The two differ
|
|
on a handful of cases (the allowlist), and the CLI boundary is the portable one.
|
|
|
|
## Phase 1 — first parity number (subset probe)
|
|
The full `run-corpus.janet` gate drives an `-e`-capable jolt binary; the Chez
|
|
host can't answer arbitrary `-e` until all of clojure.core is bootstrapped onto
|
|
Chez (Phase 2). Until then, `run-corpus-chez.janet` reports parity for the subset
|
|
the Phase-1 back end (`host/chez/emit.janet`) can already compile: each case is
|
|
run through the live analyzer → Scheme emitter → Chez via `host/chez/driver`.
|
|
Cases that reference unimplemented stdlib/host fns fail to EMIT (a clean
|
|
compile-time signal) and are counted "out of subset", not as divergences.
|
|
|
|
JOLT_CHEZ_CORPUS=1 janet test/chez/run-corpus-chez.janet
|
|
|
|
Baseline (2026-06-17): **182/182 compiled cases pass, 0 divergences**; 2473/2655
|
|
out of subset (await core on Chez). It's a slow report (a Chez subprocess per
|
|
case), so it's gated behind `JOLT_CHEZ_CORPUS` out of the default suite, like the
|
|
benches. `test/chez/emit-test.janet` is the fast Phase-1 unit gate (real
|
|
analyzer → Chez parity for fib/mandelbrot + regressions); both skip cleanly when
|
|
`chez` isn't on PATH.
|