jolt was all-flonum (one :number type, inherited from Janet whose only number type is a double). The Chez runtime has a full numeric tower, so the zero-Janet path now carries it = JVM Clojure semantics: (/ 1 2) => 1/2 (exact Ratio, was 0.5) (integer? 3) => true (integer? 3.0) => false (float? 3.0) => true (ratio? (/ 1 2)) => true (= 3 3.0) => false (== 3 3.0) => true (+ 1 2) => 3 (exact) (/ 1.0 2) => 0.5 (double) jolt= was already exactness-aware (values.ss) and == is value-equality, so =/== match the JVM split. The reader preserves exactness (integer literals exact, a/b ratios exact rationals, decimals/exponents flonums); backend_scheme emit-const renders exact ints/ratios and flonums faithfully; the value-position arithmetic, count, int, compare, bit ops, parseLong, string .length/.indexOf, range, timestamps, and array bytes return exact integers (= JVM int/long) instead of coercing to flonum. double/parseDouble/clojure.math floor|ceil|signum stay double. Only the zero-Janet path carries the tower (the Janet reader loses exactness into a double before emit). The prelude/all-flonum path is unaffected for compiled code; the runtime reader is shared, so a couple of all-flonum reader assertions become value (==) assertions. ~16 numeric corpus cases now give the JVM tower value vs the Janet-era :expected and are allowlisted as tower divergences (Chez == reference JVM) pending the corpus flip to JVM (jolt-ecz0). No BigDecimal type (1M). Re-minted. zero-janet 2682 (floor 2698->2682, the reclassified tower cases), 0 new divergences; fixpoint 10/10, bootstrap 6/6, spine 35/35, cli 49/49; Janet gate 155 files 0 failed.
98 lines
6.1 KiB
Text
98 lines
6.1 KiB
Text
# jolt-r8ku (inc Y) — Chez-side Clojure data reader: read-string / read /
|
|
# read+string / with-in-str / clojure.edn. The reader (host/chez/reader.ss)
|
|
# produces the same jolt forms the Janet reader yields; the *in* family and
|
|
# clojure.edn are Clojure over the read-string / __parse-next seams.
|
|
#
|
|
# Outputs are kept order-stable (equality checks, scalars) so set/map iteration
|
|
# order — which legitimately differs from build/jolt — doesn't masquerade as a
|
|
# divergence. Oracle = build/jolt.
|
|
#
|
|
# janet test/chez/_reader.janet
|
|
(def jolt-bin (or (os/getenv "JOLT_BIN") "bin/jolt-chez"))
|
|
|
|
(def cases
|
|
[# --- scalars + collections (value equality, order-independent) ---
|
|
["(= 42 (read-string \"42\"))" "true"]
|
|
["(= 42 (read-string \"0x2A\"))" "true"]
|
|
# numeric tower (jolt-n6al): "1/2" reads as an exact Ratio (= JVM), so a
|
|
# category-aware = against the double 0.5 is false; assert numeric value (==),
|
|
# which holds in both the all-flonum and tower models.
|
|
["(== 0.5 (read-string \"1/2\"))" "true"]
|
|
["(= -3.5 (read-string \"-3.5\"))" "true"]
|
|
["(== 1000.0 (read-string \"1e3\"))" "true"]
|
|
["(= 1 (read-string \"1N\"))" "true"]
|
|
["(integer? (read-string \"7\"))" "true"]
|
|
["(= :foo (read-string \":foo\"))" "true"]
|
|
["(= :a/b (read-string \":a/b\"))" "true"]
|
|
["(= :foo (read-string \"::foo\"))" "true"]
|
|
["(= (quote sym) (read-string \"sym\"))" "true"]
|
|
["(= (quote ns/sym) (read-string \"ns/sym\"))" "true"]
|
|
["(nil? (read-string \"nil\"))" "true"]
|
|
["(true? (read-string \"true\"))" "true"]
|
|
["(false? (read-string \"false\"))" "true"]
|
|
["(= \\a (read-string \"\\\\a\"))" "true"]
|
|
["(= \\newline (read-string \"\\\\newline\"))" "true"]
|
|
["(= \\space (read-string \"\\\\space\"))" "true"]
|
|
["(= \"a\\nb\" (read-string \"\\\"a\\\\nb\\\"\"))" "true"]
|
|
["(= [1 2 3] (read-string \"[1 2 3]\"))" "true"]
|
|
["(= (quote (+ 1 2)) (read-string \"(+ 1 2)\"))" "true"]
|
|
["(= {:a 1 :b 2} (read-string \"{:a 1 :b 2}\"))" "true"]
|
|
# core read-string returns the raw set FORM (edn->value builds the real set)
|
|
["(:value (read-string \"#{1 2 3}\"))" "[1 2 3]"]
|
|
["(= :jolt/set (:jolt/type (read-string \"#{1 2 3}\")))" "true"]
|
|
["(= [1 [2 3] {:k :v}] (read-string \"[1 [2 3] {:k :v}]\"))" "true"]
|
|
# --- reader macros ---
|
|
["(= (quote (quote x)) (read-string \"'x\"))" "true"]
|
|
["(= (quote (clojure.core/deref a)) (read-string \"@a\"))" "true"]
|
|
["(= (quote (syntax-quote (a (unquote b)))) (read-string \"`(a ~b)\"))" "true"]
|
|
["(= (quote (unquote-splicing xs)) (read-string \"~@xs\"))" "true"]
|
|
# --- whitespace / comments / discard ---
|
|
["(= 42 (read-string \"; comment\\n42\"))" "true"]
|
|
["(= [1 2] (read-string \"[1 #_ 9 2]\"))" "true"]
|
|
["(nil? (read-string \"\"))" "true"]
|
|
["(nil? (read-string \" , ,\"))" "true"]
|
|
# --- metadata ^ on a symbol ---
|
|
["(:tag (meta (read-string \"^String x\")))" "String"]
|
|
["(:foo (meta (read-string \"^:foo x\")))" "true"]
|
|
# --- *in* reader family: read / read+string / with-in-str ---
|
|
["(= 42 (with-in-str \"42\" (read)))" "true"]
|
|
["(= (quote (+ 1 2)) (with-in-str \"(+ 1 2)\" (read)))" "true"]
|
|
["(with-in-str \"1 2\" [(read) (read)])" "[1 2]"]
|
|
["(= :done (with-in-str \"\" (read *in* false :done)))" "true"]
|
|
["(let [[v s] (with-in-str \"42 rest\" (read+string))] (and (= v 42) (string? s)))" "true"]
|
|
["(= [1 2] (with-in-str \"1 2\" [(first (read+string)) (first (read+string))]))" "true"]
|
|
# --- clojure.edn (set/tagged forms built into real values) ---
|
|
["(do (require (quote [clojure.edn :as e0])) (= #{1 2} (e0/read-string \"#{1 2}\")))" "true"]
|
|
["(do (require (quote [clojure.edn :as e0])) (uuid? (e0/read-string \"#uuid \\\"550e8400-e29b-41d4-a716-446655440000\\\"\")))" "true"]
|
|
["(do (require (quote [clojure.edn :as e0])) (inst? (e0/read-string \"#inst \\\"2020-01-01T00:00:00Z\\\"\")))" "true"]
|
|
["(do (require (quote [clojure.edn :as e0])) (= :end (e0/read-string {:eof :end} \"\")))" "true"]
|
|
["(do (require (quote [clojure.edn :as e0])) (= [:custom 5] (e0/read-string {:readers {(quote custom) (fn [v] [:custom v])}} \"#custom 5\")))" "true"]
|
|
["(do (require (quote clojure.edn)) (= {:a 1 :b 2} (clojure.edn/read-string \"{:a 1\\n :b 2}\")))" "true"]])
|
|
|
|
(defn run-capture [bin expr]
|
|
(def proc (os/spawn [bin "-e" expr] :p {:out :pipe :err :pipe}))
|
|
(def out (ev/read (proc :out) 0x100000))
|
|
(def err (ev/read (proc :err) 0x100000))
|
|
(def code (os/proc-wait proc))
|
|
(def lines (filter (fn [l] (not (empty? l)))
|
|
(string/split "\n" (string/trim (if out (string out) "")))))
|
|
[code (if (empty? lines) "" (last lines)) (string/trim (if err (string err) ""))])
|
|
|
|
(var pass 0)
|
|
(def fails @[])
|
|
(each [expr expected] cases
|
|
(def [ocode oracle _] (run-capture "build/jolt" expr))
|
|
(def [code got err] (run-capture jolt-bin expr))
|
|
(cond
|
|
(not= ocode 0) (array/push fails [expr (string "ORACLE FAILED exit " ocode)])
|
|
(not= oracle expected) (array/push fails [expr (string "ORACLE MISMATCH want `" expected "` got `" oracle "`")])
|
|
(not= code 0) (array/push fails [expr (string "exit " code "; err: " err)])
|
|
(= got expected) (++ pass)
|
|
(array/push fails [expr (string "want `" expected "`, got `" got "`")])))
|
|
|
|
(printf "\n_reader parity [%s]: %d/%d passed" jolt-bin pass (length cases))
|
|
(when (> (length fails) 0)
|
|
(printf "%d FAIL(s):" (length fails))
|
|
(each [e m] fails (printf " FAIL %s\n %s" e m)))
|
|
(flush)
|
|
(os/exit (if (empty? fails) 0 1))
|