From c28b5406cac9484065d7b688c180949561702c4f Mon Sep 17 00:00:00 2001 From: Yogthos Date: Wed, 17 Jun 2026 22:32:02 -0400 Subject: [PATCH] Chez inc 3m: numeric-edge literal emit + variadic assoc! ##Inf/##-Inf/##NaN were emitted to bare inf/-inf/nan, which are unbound symbols in Chez. emit-const now lowers them to +inf.0/-inf.0/+nan.0. The -e/element printer renders them inf/-inf/nan (Chez's number->string gives +inf.0), and str renders the long Clojure forms Infinity/-Infinity/NaN. assoc! is now variadic ((assoc! t k v & kvs)) like Clojure. Prelude parity 1382 -> 1407/2497, 0 new divergences. str of inf INSIDE a collection still wants the long form (needs the Phase-2 recursive str renderer), so [inf inside coll] is allowlisted. Transducer arities and the cdr-on-()/\p{} regex gaps are split out to jolt-kxsr/jolt-y1zq. --- host/chez/converters.ss | 5 +++++ host/chez/emit.janet | 12 ++++++++---- host/chez/rt.ss | 12 +++++++++--- host/chez/transients.ss | 5 +++-- test/chez/README.md | 15 +++++++++++---- test/chez/emit-test.janet | 21 +++++++++++++++++++++ test/chez/run-corpus-prelude.janet | 11 ++++++++--- 7 files changed, 65 insertions(+), 16 deletions(-) diff --git a/host/chez/converters.ss b/host/chez/converters.ss index d1d348c..e8284c4 100644 --- a/host/chez/converters.ss +++ b/host/chez/converters.ss @@ -13,6 +13,11 @@ ((string? v) v) ((char? v) (string v)) ((regex-t? v) (regex-t-source v)) + ;; str/print render the infinities and NaN long-form (Clojure .toString), + ;; unlike the -e printer's inf/-inf/nan. + ((and (flonum? v) (fl= v +inf.0)) "Infinity") + ((and (flonum? v) (fl= v -inf.0)) "-Infinity") + ((and (flonum? v) (not (fl= v v))) "NaN") (else (jolt-pr-str v)))) (define (jolt-str . xs) (let loop ((xs xs) (acc '())) diff --git a/host/chez/emit.janet b/host/chez/emit.janet index 363d2c7..d737a9f 100644 --- a/host/chez/emit.janet +++ b/host/chez/emit.janet @@ -122,10 +122,14 @@ # jolt models every number as a double (no ratios/bignums; see reader.janet). # Emit flonums so arithmetic matches the Janet host and Chez doesn't fall into # exploding exact rationals (mandelbrot). Integer-valued -> append ".0". - (number? v) (let [s (string v)] - (if (or (string/find "." s) (string/find "e" s) (string/find "n" s)) - s - (string s ".0"))) + # ##Inf/##-Inf/##NaN: Janet stringifies these as inf/-inf/nan, which are + # unbound symbols in Chez — emit Chez's flonum literals instead. + (number? v) (cond + (= v math/inf) "+inf.0" + (= v (- math/inf)) "-inf.0" + (not= v v) "+nan.0" + (let [s (string v)] + (if (or (string/find "." s) (string/find "e" s)) s (string s ".0")))) (string? v) (string/format "%j" v) # quoted+escaped string literal # keyword literal -> (keyword ns name); ns is everything before the first "/" (keyword? v) (let [s (string v) idx (string/find "/" s)] diff --git a/host/chez/rt.ss b/host/chez/rt.ss index 9f2c6c8..2d45ac1 100644 --- a/host/chez/rt.ss +++ b/host/chez/rt.ss @@ -98,9 +98,15 @@ ;; jolt models every number as a Clojure double: integer-valued values print ;; without a ".0" (the Janet host prints (* 1.0 5) as "5", (/ 1 2) as "0.5"). (define (jolt-num->string x) - (if (and (rational? x) (integer? x)) - (number->string (exact x)) - (number->string x))) + (cond + ;; the -e / element printer renders the infinities and NaN as inf/-inf/nan + ;; (Chez's number->string gives +inf.0 etc.); the str/print family uses the + ;; long "Infinity"/"NaN" forms (see jolt-str-render-one in converters.ss). + ((and (flonum? x) (fl= x +inf.0)) "inf") + ((and (flonum? x) (fl= x -inf.0)) "-inf") + ((and (flonum? x) (not (fl= x x))) "nan") + ((and (rational? x) (integer? x)) (number->string (exact x))) + (else (number->string x)))) ;; Program-final-value printer. jolt's `-e` prints in str-style: strings raw (no ;; quotes), chars as `\c`/`\newline`, collections recursively. NOTE: maps/sets diff --git a/host/chez/transients.ss b/host/chez/transients.ss index 62cda65..e5557fc 100644 --- a/host/chez/transients.ss +++ b/host/chez/transients.ss @@ -27,9 +27,10 @@ (jolt-trans-check t "conj!") (jolt-transient-coll-set! t (apply jolt-conj (jolt-transient-coll t) xs)) t) -(define (jolt-assoc! t k v) +;; (assoc! t k v & kvs): variadic like Clojure (jolt-assoc already folds pairs). +(define (jolt-assoc! t . kvs) (jolt-trans-check t "assoc!") - (jolt-transient-coll-set! t (jolt-assoc (jolt-transient-coll t) k v)) + (jolt-transient-coll-set! t (apply jolt-assoc (jolt-transient-coll t) kvs)) t) (define (jolt-dissoc! t . ks) (jolt-trans-check t "dissoc!") diff --git a/test/chez/README.md b/test/chez/README.md index 0134ce2..9324cb4 100644 --- a/test/chez/README.md +++ b/test/chez/README.md @@ -105,7 +105,8 @@ corpus case with all of core present, bucketing the result — JOLT_CORPUS_LIMIT=200 … (every-Nth stride, fast) Parity baseline: inc 3j **1220/2497**; 3k (converters, jolt-t6cr) **1326**; -3l (transients, jolt-kl2l) **1382/2497**, 0 NEW divergences (13 allowlisted: +3l (transients, jolt-kl2l) **1382**; 3m (numeric-edge emit + variadic assoc!, +jolt-q3w8) **1407/2497**, 0 NEW divergences (14 allowlisted: dynamic vars `*ns*`/`*clojure-version*`/`*unchecked-math*`, var/`*ns*` rendering, class names, eval-order, with-open — all deferred Phase-2 / dynamic-var gaps). - inc 3k `host/chez/converters.ss`: `str`/`subs`/`vec`/`keyword`/`symbol`/`compare`/ @@ -118,11 +119,17 @@ class names, eval-order, with-open — all deferred Phase-2 / dynamic-var gaps). are redefined to see THROUGH a transient (frequencies/group-by do `(get tm k)` on a transient map); `vector?` on a transient vector is false, which group-by relies on. +- inc 3m: `##Inf`/`##-Inf`/`##NaN` emitted to bare `inf`/`nan` (unbound in Chez); + emit-const now lowers them to `+inf.0`/`-inf.0`/`+nan.0`, the `-e` printer renders + `inf`/`-inf`/`nan` and `str` renders `Infinity`/`-Infinity`/`NaN` (Clojure). Plus + variadic `assoc!`. (`str` of inf *inside a collection* still wants the long form — + the Phase-2 recursive str renderer — so `[inf inside coll]` is allowlisted.) + The remaining buckets are the punch-list the next increments chase: ~360 emit-fail (genuine host interop — qualified Java/Janet refs, runtime `defmacro`/`eval`, out of -the analyzer's subset) and ~740 runtime crashes, including `##Inf`/`##NaN` literals → -unbound `inf`/`nan` and seq-prim transducer arities (inc 3m jolt-q3w8), multimethod -dispatch (Phase 2 jolt-9ls5), and more host-coupled natives without a shim. +the analyzer's subset) and ~715 runtime crashes — more host-coupled natives without a +shim, transducer arities (jolt-kxsr), `cdr`-on-`()` and `\p{}` regex classes +(jolt-y1zq), and multimethod dispatch (Phase 2 jolt-9ls5). Two host shims landed with the prelude. `host/chez/atoms.ss`: atom/deref/swap!/ reset! (+ compare-and-set!/swap-vals!/reset-vals!) — host-coupled mutable cells the diff --git a/test/chez/emit-test.janet b/test/chez/emit-test.janet index f966cf2..c22b095 100644 --- a/test/chez/emit-test.janet +++ b/test/chez/emit-test.janet @@ -467,6 +467,27 @@ (ok (string "jolt-chez -e: " src) (and (= code 0) (= out want)) (string "chez=" out " janet=" want " | " err))))) +# 3r) numeric-edge literals (jolt-q3w8): ##Inf/##-Inf/##NaN emitted to bare +# inf/nan (unbound on Chez) — fix emit-const to +inf.0/-inf.0/+nan.0, the +# -e printer to inf/-inf/nan, and str to Infinity/-Infinity/NaN (Clojure). +# Value/print cases are pure literals -> subset path (d/run-on-chez). +(each src ["(< 5 ##Inf)" "(> 5 ##-Inf)" "(= ##Inf ##Inf)" + "##Inf" "##-Inf" "##NaN" "[##Inf]" "[##NaN ##-Inf]"] + (let [[code out err] (d/run-on-chez ctx src) want (cli-oracle src)] + (ok (string "numedge: " src) (and (= code 0) (= out want)) + (string "chez=" out " janet=" want " | " err)))) +# str of inf/nan needs the prelude (str is a converter shim). +(each src ["(str ##Inf)" "(str ##-Inf)" "(str ##NaN)"] + (let [[code out err] (run-prelude src) want (cli-oracle src)] + (ok (string "numedge: " src) (and (= code 0) (= out want)) + (string "chez=" out " janet=" want " | " err)))) +# variadic assoc! (jolt-q3w8): (assoc! t k v & kvs). +(each src ["(count (persistent! (assoc! (transient {}) :a 1 :b 2 :c 3)))" + "(get (persistent! (assoc! (transient {}) :a 1 :b 2)) :b)"] + (let [[code out err] (run-prelude src) want (cli-oracle src)] + (ok (string "numedge: " src) (and (= code 0) (= out want)) + (string "chez=" out " janet=" want " | " err)))) + # 4) perf signal: emitted fib(30) in-Scheme timing (excludes Chez startup), to # track against the spike ceiling (hand-Scheme fib ~5ms). Informational — the # jolt-truthy? wrapper (~3x) and flonum modeling are known Phase-4 levers. diff --git a/test/chez/run-corpus-prelude.janet b/test/chez/run-corpus-prelude.janet index 4c87f71..9269436 100644 --- a/test/chez/run-corpus-prelude.janet +++ b/test/chez/run-corpus-prelude.janet @@ -54,7 +54,12 @@ "*ns* user" true "str of a var" true "*clojure-version* major" true - "*unchecked-math*" true}) + "*unchecked-math*" true + # (str [##Inf]) wants "[Infinity]" but the Chez -e printer (jolt-pr-str, which + # str falls back to for collections) renders inf as "inf" — str needs a + # recursive long-form renderer, the Phase-2 canonical printer. Top-level + # (str ##Inf) -> "Infinity" already works (jolt-str-render-one). + "inf inside coll" true}) (def ctx (d/make-ctx)) @@ -130,8 +135,8 @@ # reach-floor and the suite baseline. The gate fails if parity drops below it, or # on any NEW (un-allowlisted) divergence — a real Chez correctness regression. # Full-corpus baseline: inc 3j 1220/2497; 3k (converters) 1326; 3l (transients) -# 1382. Strided runs scale the floor down. -(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1382"))) +# 1382; 3m (numeric-edge emit + variadic assoc!) 1407. Strided runs scale down. +(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1407"))) (def floor (if (os/getenv "JOLT_CORPUS_LIMIT") 0 base-floor)) (when (or (> (length diverged) 0) (< pass floor)) (printf "REGRESSION: pass %d < floor %d or %d new divergence(s)" pass floor (length diverged)))