Numbers-style category dispatch for binary numeric ops
Arithmetic and comparisons lowered to raw Chez ops, so an operand outside Chez's tower (BigDecimal) crashed with a raw condition, and Chez contagion leaked: (* 1.0 0) gave exact 0 where the JVM gives 0.0, (* ##Inf 0) gave 0 instead of ##NaN, (/ 1 0) raised an untyped error. One seam now (host/chez/seq.ss): call position emits jolt-n* macros with the both-Chez-numbers fast path open-coded; value position folds through the same binary ops. Anything outside the tower falls to per-op slow hooks that java/bigdec.ss extends, so bigdec arithmetic works in every position (the old static-only :bigdec typing limitation is gone). JVM rules patched into the fast path: a double operand wins, an exact zero divisor throws ArithmeticException while a double zero divisor yields Inf/NaN, quot/rem/mod cover ratios and doubles, min/max return the original operand with NaN winning, a nil operand is NPE and a non-number CCE, zero-arg -// throw ArityException at runtime instead of failing expansion. Also: with-precision now binds *math-context* and bigdec results round with real RoundingMode semantics (UNNECESSARY throws; division rounds to precision instead of throwing); rationalize goes through the shortest decimal print like BigDecimal.valueOf (the identity stub is gone); ratios coerce to bigdec like Numbers.toBigDecimal; min/max int-literal operands no longer coerce to flonum in the numeric pass. Perf neutral: fib and seq benches unchanged (the fast path is two type checks the optimizer folds); hinted fl/fx paths untouched. 19 JVM-certified corpus rows; cts baseline 5614->5730 pass, 192->88 errors, 84->79 baselined namespaces.
This commit is contained in:
parent
38abc1be84
commit
e66a91750e
13 changed files with 1106 additions and 729 deletions
|
|
@ -133,8 +133,8 @@
|
|||
(concat (map first ss)
|
||||
(apply interleave (map rest ss))))))))
|
||||
|
||||
;; No ratio type on Jolt, so rationalize is identity.
|
||||
(defn rationalize [x] x)
|
||||
;; rationalize is host-native (java/bigdec.ss): a double routes through its
|
||||
;; shortest decimal print like BigDecimal.valueOf, so (rationalize 1.1) is 11/10.
|
||||
|
||||
;; 0-arg: a stateful transducer (tracks [seen? prev] in a volatile, so no sentinel
|
||||
;; value is needed). 1-arg: eager dedupe of consecutive equal elements.
|
||||
|
|
|
|||
|
|
@ -109,11 +109,15 @@
|
|||
(with-open ~(vec (drop 2 bindings)) ~@body)
|
||||
(finally (__close ~(first bindings)))))))
|
||||
|
||||
;; jolt numbers are doubles — there is no BigDecimal math context, so the
|
||||
;; precision (and optional :rounding mode) is accepted and ignored.
|
||||
;; Binds *math-context*; BigDecimal arithmetic in the dynamic scope rounds its
|
||||
;; results to the precision with the rounding mode (default HALF_UP, like
|
||||
;; java.math.MathContext).
|
||||
(defmacro with-precision [precision & exprs]
|
||||
(let [body (if (= :rounding (first exprs)) (drop 2 exprs) exprs)]
|
||||
`(do ~@body)))
|
||||
(let [[rounding body] (if (= :rounding (first exprs))
|
||||
[(second exprs) (drop 2 exprs)]
|
||||
['HALF_UP exprs])]
|
||||
`(binding [clojure.core/*math-context* {:precision ~precision :rounding '~rounding}]
|
||||
~@body)))
|
||||
|
||||
(defmacro with-bindings [binding-map & body]
|
||||
`(with-bindings* ~binding-map (fn [] ~@body)))
|
||||
|
|
|
|||
|
|
@ -17,13 +17,17 @@
|
|||
|
||||
;; Hot clojure.core primitives lowered to native Scheme.
|
||||
;; `=` is the exactness-aware jolt= from values.ss; inc/dec/
|
||||
;; not are rt shims; mod/rem/quot map to Scheme's (Scheme has all three).
|
||||
;; not are rt shims. Arithmetic and comparisons lower to the jolt-n* checked
|
||||
;; macros (host/chez/seq.ss): the both-Chez-numbers fast path is open-coded and
|
||||
;; anything else (BigDecimal, a non-number) takes the Numbers.ops-style category
|
||||
;; dispatch, with JVM contagion (a double operand wins; an exact zero divisor is
|
||||
;; ArithmeticException; a double zero divisor is ##Inf/##NaN).
|
||||
(def ^:private native-ops
|
||||
{"+" "+" "-" "-" "*" "*" "/" "/"
|
||||
"<" "<" ">" ">" "<=" "<=" ">=" ">="
|
||||
{"+" "jolt-n+" "-" "jolt-n-" "*" "jolt-n*" "/" "jolt-n-div"
|
||||
"<" "jolt-n<" ">" "jolt-n>" "<=" "jolt-n<=" ">=" "jolt-n>="
|
||||
"=" "jolt=" "inc" "jolt-inc" "dec" "jolt-dec" "not" "jolt-not"
|
||||
"min" "min" "max" "max"
|
||||
"mod" "modulo" "rem" "remainder" "quot" "quotient"
|
||||
"min" "jolt-n-min" "max" "jolt-n-max"
|
||||
"mod" "jolt-mod" "rem" "jolt-rem" "quot" "jolt-quot"
|
||||
"vector" "jolt-vector" "hash-map" "jolt-hash-map-fn" "hash-set" "jolt-hash-set"
|
||||
"conj" "jolt-conj" "get" "jolt-get" "nth" "jolt-nth" "count" "jolt-count"
|
||||
"assoc" "jolt-assoc" "dissoc" "jolt-dissoc" "contains?" "jolt-contains?"
|
||||
|
|
@ -53,12 +57,12 @@
|
|||
"protocol-dispatch3" "protocol-dispatch3"})
|
||||
|
||||
;; Value-position resolution for a clojure.core ref passed AS A VALUE (to map /
|
||||
;; filter / reduce / apply). Arithmetic is the exception — Scheme's +/-/*// return
|
||||
;; EXACT results for exact/zero-arg inputs, breaking the all-double model in
|
||||
;; higher-order use, so value-position arithmetic routes to the flonum wrappers.
|
||||
;; filter / reduce / apply). The jolt-n* call-position forms are macros, so value
|
||||
;; position substitutes the variadic procedures over the same binary dispatch.
|
||||
(def ^:private core-value-procs
|
||||
(merge native-ops {"+" "jolt-add" "-" "jolt-sub" "*" "jolt-mul" "/" "jolt-div"
|
||||
"min" "jolt-min" "max" "jolt-max"}))
|
||||
"min" "jolt-min" "max" "jolt-max"
|
||||
"<" "jolt-lt" ">" "jolt-gt" "<=" "jolt-le" ">=" "jolt-ge"}))
|
||||
|
||||
;; Per-op arity gate: only lower when the Scheme prim and the jolt fn agree at
|
||||
;; this arity. Ops absent from the table are variadic (legal at any arity).
|
||||
|
|
@ -83,7 +87,7 @@
|
|||
|
||||
;; jolt's comparison ops are vacuously true at arity 1 and DON'T inspect the arg,
|
||||
;; but Scheme's < demands a number even there — special-case.
|
||||
(def ^:private cmp1-ops #{"<" ">" "<=" ">="})
|
||||
(def ^:private cmp1-ops #{"jolt-n<" "jolt-n>" "jolt-n<=" "jolt-n>="})
|
||||
|
||||
;; Host interop methods with a Chez RT shim (rt.ss jolt-host-call). A `.method`
|
||||
;; call on any other method routes to record-method-dispatch (a reify/record
|
||||
|
|
@ -93,7 +97,7 @@
|
|||
;; Native-op Scheme procedures that return a genuine Scheme boolean (#t/#f), so an
|
||||
;; :if test built from them needs no jolt-truthy? wrapper.
|
||||
(def ^:private bool-returning-ops
|
||||
#{"<" "<=" ">" ">=" "jolt=" "jolt-not"
|
||||
#{"jolt-n<" "jolt-n<=" "jolt-n>" "jolt-n>=" "jolt=" "jolt-not"
|
||||
"jolt-even?" "jolt-odd?" "jolt-pos?" "jolt-neg?"
|
||||
"jolt-zero?" "jolt-empty?" "jolt-contains?" "jolt-nil?" "jolt-some?"})
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,12 @@
|
|||
ls (lng-spec nm n)
|
||||
bs (bd-spec nm n)]
|
||||
(cond
|
||||
(and ds (ok? :double :double))
|
||||
(and ds (ok? :double :double)
|
||||
;; min/max return the ORIGINAL operand (Numbers.min: an integer
|
||||
;; literal stays exact), so an int-literal operand blocks the
|
||||
;; flonum lowering there — flmin would coerce it.
|
||||
(or (not (contains? #{"min" "max"} nm))
|
||||
(every? (fn [c] (= c :double)) cls)))
|
||||
;; coerce integer-literal operands to flonum so fl-ops never see an exact int.
|
||||
(let [args' (mapv (fn [nd] (if (int-lit? nd) (assoc nd :val (double (get nd :val))) nd))
|
||||
argnodes)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue