Brings up the seq layer on the Chez runtime. host/chez/seq.ss adds one lazy-capable node (cseq) that models Clojure's list, cons, and lazy seq - all print as (...), all sequential-= to each other and to vectors. seq coerces any seqable (vector/map/set/string/list/seq/nil) to a cseq or nil; the empty seq is a distinct value printing () (rest of a 1-elem coll is () not nil, seq of empty is nil). Leaf ops: first/rest/next/seq/cons/list, reverse/last, map/filter/remove/reduce/into, range/take/drop/concat/apply, keys/vals, plus nth/peek/pop extended over seqs. map/filter/reduce apply their fn arg through jolt-invoke, so a procedure, keyword, or collection all work as the fn. Dynamic IFn dispatch: a keyword/vector/coll held in a local (let binding or fn param) and called as a fn now routes through the jolt-invoke fallback (procedure? -> apply; keyword/coll -> lookup). The emitter only routes a :local callee that isn't a known procedure - a named fn's self-recursion name stays a direct call, so the fib hot path is untouched. Closes the 3 ex-known IFn divergences. emit.janet: seq/pred ops added to native-ops with arity gates; value-position clojure.core refs resolve to the RT procedure (native-ops names one for each), with +/-/*// routed to flonum-coercing wrappers so higher-order arithmetic ((reduce + [])) keeps the all-double model. values.ss: cross-type sequential =/hash so a vector and a list of the same elements are jolt= and hash alike. rt.ss: printer learns seqs; top-level nil prints as the empty string (jolt -e str-style). Fixed latent bug: (conj nil ...) now builds a list, not a vector. Gates: emit-test 69/69 (fib/mandelbrot/collections/seq/IFn parity vs the jolt oracle, fib(30) ~24ms unchanged). Subset probe 433/436 -> 595/595 compiled, 0 divergences (was 3 known), 2060/2655 out of subset. Full run-tests green (125 files, conformance + suites included).
70 lines
3.2 KiB
Text
70 lines
3.2 KiB
Text
# Phase 1 (jolt-cf1q.2) — FIRST parity number for the Chez back end.
|
|
#
|
|
# The full 0b gate (test/chez/run-corpus.janet) drives an `-e`-capable jolt
|
|
# binary; that needs all of clojure.core bootstrapped onto Chez, which is Phase 2.
|
|
# Until then, this probe reports parity for the subset the back end can ALREADY
|
|
# compile: each corpus case `(= expected actual)` is run through the live
|
|
# analyzer -> Scheme emitter -> Chez. Cases that reference unimplemented core fns
|
|
# fail to EMIT (a clean compile-time signal) and are counted "out of subset",
|
|
# not as divergences. The number to watch is parity WITHIN the compiled subset.
|
|
# janet test/chez/run-corpus-chez.janet
|
|
# JOLT_CORPUS_LIMIT=400 … (every-Nth stride, fast)
|
|
(import ../../host/chez/driver :as d)
|
|
|
|
# Slow reporting tool (~20s: a Chez subprocess per compiled case), not a pass/fail
|
|
# unit test — gate it out of the default suite like the benches (JOLT_BENCH).
|
|
(unless (os/getenv "JOLT_CHEZ_CORPUS")
|
|
(print "skip: set JOLT_CHEZ_CORPUS=1 to run the Chez subset parity probe")
|
|
(os/exit 0))
|
|
(unless (d/chez-available?)
|
|
(print "skip: chez not on PATH")
|
|
(os/exit 0))
|
|
|
|
(def corpus (parse (slurp "test/chez/corpus.edn")))
|
|
(def cases
|
|
(if-let [n (os/getenv "JOLT_CORPUS_LIMIT")]
|
|
(let [stride (max 1 (math/floor (/ (length corpus) (scan-number n))))]
|
|
(seq [i :range [0 (length corpus) stride]] (in corpus i)))
|
|
corpus))
|
|
|
|
# Known subset divergences: cases that compile but need a feature beyond the
|
|
# current increment. None as of inc 3b — dynamic IFn dispatch (a keyword/vector
|
|
# held in a LOCAL then called as a fn, (let [k :a] (k m))) now routes through the
|
|
# jolt-invoke fallback, and the seq tier closed the rest. Add a label here if a
|
|
# future increment surfaces a case that compiles but needs deferred semantics;
|
|
# the gate fails only on a NEW (un-allowlisted) divergence.
|
|
(def known-divergences
|
|
{})
|
|
|
|
(def ctx (d/make-ctx))
|
|
(var compiled 0) (var pass 0) (var out-of-subset 0)
|
|
(def diverged @[])
|
|
(def known-hit @[])
|
|
|
|
(each row cases
|
|
(def {:expected e :actual a :label l} row)
|
|
# :throws cases need error-semantics we don't model yet — skip.
|
|
(if (= e :throws)
|
|
(++ out-of-subset)
|
|
(let [src (string "(= " e " " a ")")
|
|
# compile-program can throw (unsupported op/core fn) or the analyzer can
|
|
# punt; either way the case is outside the compilable subset.
|
|
res (try (d/run-on-chez ctx src) ([err] :uncompilable))]
|
|
(if (= res :uncompilable)
|
|
(++ out-of-subset)
|
|
(let [[code out] res]
|
|
(++ compiled)
|
|
(defn record-div [m] (if (known-divergences l) (array/push known-hit l) (array/push diverged [l m])))
|
|
(cond
|
|
(not= code 0) (record-div (string "exit " code))
|
|
(= out "true") (++ pass)
|
|
(record-div (string "got " out))))))))
|
|
|
|
(printf "\nChez subset parity: %d/%d compiled cases pass (%d/%d out of subset, %d known divergences)"
|
|
pass compiled out-of-subset (length cases) (length known-hit))
|
|
(when (> (length diverged) 0)
|
|
(printf "%d NEW divergence(s) within the compiled subset:" (length diverged))
|
|
(each [l m] (slice diverged 0 (min 25 (length diverged)))
|
|
(printf " [%s] %s" l m)))
|
|
(flush)
|
|
(os/exit (if (> (length diverged) 0) 1 0))
|