Merge pull request #274 from jolt-lang/clojure-lift

Clojure lift
This commit is contained in:
Dmitri Sotnikov 2026-06-30 15:24:41 +00:00 committed by GitHub
commit 9e53ba4248
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 372 additions and 338 deletions

View file

@ -11,9 +11,9 @@
(define (jolt-array-map . kvs) (jolt-array-map-build kvs))
(define (jolt-hash-map-fn . kvs) (jolt-hash-map-build kvs))
;; set: realize any seqable to a list, then dedup through the set ctor. nil -> #{}.
(define (jolt-set coll)
(if (jolt-nil? coll) (jolt-hash-set) (apply jolt-hash-set (seq->list coll))))
;; set lives in the kernel overlay tier (clojure/core/00-kernel.clj): it's a pure
;; composition (apply hash-set (seq coll)) the compiler uses only off the emit path,
;; so the Clojure version lowers to the same code without a bootstrap cycle.
;; rand: a flonum in [0, n) (n defaults to 1.0) — jolt is all-flonum, so the
;; result is a double like every other number.
@ -24,6 +24,5 @@
(def-var! "clojure.core" "hash-map" jolt-hash-map-fn)
(def-var! "clojure.core" "hash-set" jolt-hash-set)
(def-var! "clojure.core" "array-map" jolt-array-map)
(def-var! "clojure.core" "set" jolt-set)
(def-var! "clojure.core" "rand" jolt-rand)
(def-var! "clojure.core" "map-entry?" jolt-map-entry?)

View file

@ -17,8 +17,12 @@
jolt-nil)
;; --- reader-conditional: a tagged map (reader-conditional? is an overlay
;; tagged-value predicate that reads :jolt/type). re-matcher / re-find / re-groups
;; are the stateful matcher API in regex.ss.
;; tagged-value predicate that reads :jolt/type). STAYS NATIVE: building a
;; :jolt/type-tagged map is part of the native value model — an overlay defn
;; returning {:jolt/type ...} silently fails to bind during the seed mint (the
;; guard around each prelude form swallows the load-time error), the same reason
;; every other tagged-value constructor (atom/volatile!/tagged-literal) is native.
;; re-matcher / re-find / re-groups are the stateful matcher API in regex.ss.
(define nr-kw-type (keyword "jolt" "type"))
(define nr-kw-rc (keyword "jolt" "reader-conditional"))
(define nr-kw-form (keyword #f "form"))

View file

@ -25,18 +25,11 @@
(def-var! "clojure.core" "volatile!" jolt-volatile!)
(def-var! "clojure.core" "deref" jolt-deref)
;; --- transduce / sequence ----------------------------------------------------
;; (transduce xform f coll) / (transduce xform f init coll): build the transformed
;; reducing fn (xform f), reduce it over coll (reduce-seq honors `reduced`), then
;; run the completion (1-arg) arity. The 3-arg init defaults to (f) — the rf's
;; 0-arity, e.g. (+) = 0, (conj) = [].
(define jolt-transduce
(case-lambda
((xform f coll) (jolt-transduce xform f (jolt-invoke f) coll))
((xform f init coll)
(let* ((xf (jolt-invoke xform f))
(res (reduce-seq xf init (jolt-seq coll))))
(jolt-invoke xf res)))))
;; --- sequence ----------------------------------------------------------------
;; transduce lives in the overlay (clojure/core/22-coll.clj): it's a pure
;; composition (xf (reduce xf init coll)) over reduce, so the Clojure version
;; lowers to the same code the native shim did. sequence stays native (below):
;; its transformer iterator drives the reduced box + lazy realization directly.
;; (sequence coll) -> a seq; (sequence xform coll) -> a LAZY seq of coll transformed
;; by xform. A transformer iterator (mirrors clojure.core's TransformerIterator):
@ -87,7 +80,6 @@
((coll) (jolt-seq coll))
((xform coll) (sequence-xf xform coll))))
(def-var! "clojure.core" "transduce" jolt-transduce)
(def-var! "clojure.core" "sequence" jolt-sequence)
;; --- cat ---------------------------------------------------------------------

View file

@ -78,18 +78,19 @@
(def-var! "clojure.core" "line-seq"
(lambda (rdr)
(if (reader-jhost? rdr) (chez-line-seq rdr) (jolt-invoke overlay-line-seq rdr)))))
;; JVM-parity numeric tower: the overlay (20-coll.clj) carries an
;; all-flonum number-predicate web with no Ratio concept (ratio? -> false,
;; double? -> not-integer, float? -> double?, rational? -> int?), which
;; misclassifies exact rationals on the Chez tower (e.g. (double? 1/2) -> true).
;; Re-assert the native tower-correct versions (predicates.ss) so they win over
;; the overlay defs. int?/double? alias integer?/float?. == is value-equality.
;; JVM-parity numeric tower. integer?/float? are on the compiler emit/inference
;; path (so they stay native) but the overlay (20-coll.clj) still carries an
;; all-flonum int?/double? (int? -> integer?, double? -> not-integer) that
;; misclassifies exact rationals (e.g. (double? 1/2) -> true). Re-assert the
;; native tower-correct versions so they win over those overlay defs. int?/double?
;; alias integer?/float?. == is value-equality. (ratio?/rational? are now correct
;; in the overlay, built on jolt.host tower tests, so they need no re-assertion.)
(def-var! "clojure.core" "integer?" jolt-integer?)
(def-var! "clojure.core" "int?" jolt-integer?)
(def-var! "clojure.core" "float?" jolt-float?)
(def-var! "clojure.core" "double?" jolt-float?)
(def-var! "clojure.core" "ratio?" jolt-ratio?)
(def-var! "clojure.core" "rational?" jolt-rational?)
;; ratio?/rational? now live (correctly) in the overlay, so they no longer need a
;; native re-assertion here. decimal? stays (bigdec re-binds it).
(def-var! "clojure.core" "decimal?" jolt-decimal?)
(def-var! "clojure.core" "==" jolt-num-equiv)
;; chunked-seq? is true for a vector's seq (a real chunked-seq); the overlay's

View file

@ -12,9 +12,7 @@
(define (jolt-vector? x) (pvec? x))
(define (jolt-set? x) (pset? x))
(define (jolt-seq? x) (or (cseq? x) (empty-list-t? x)))
;; (list? x): a list-marked cseq node or the empty list (). A lazy/vector-backed
;; seq, (rest list), (seq coll), (map …) are seqs but not lists.
(define (jolt-list-pred? x) (or (and (cseq? x) (cseq-list? x)) (empty-list-t? x)))
;; list? lives in the overlay (clojure/core/20-coll.clj) — see jolt.host/cseq? etc.
(define (jolt-coll-pred? x)
(or (pvec? x) (pmap? x) (pset? x) (cseq? x) (empty-list-t? x) (jolt-lazyseq? x)))
(define (jolt-number? x) (number? x))
@ -27,13 +25,18 @@
;; BigDecimal). decimal? is always false (no BigDecimal type).
(define (jolt-integer? x) (and (number? x) (exact? x) (integer? x)))
(define (jolt-float? x) (and (number? x) (flonum? x)))
(define (jolt-ratio? x) (and (number? x) (exact? x) (rational? x) (not (integer? x))))
(define (jolt-rational? x) (and (number? x) (exact? x)))
;; ratio?/rational? live in the overlay (clojure/core/20-coll.clj), built on the
;; jolt.host tower tests. decimal? stays native: the optional bigdec module
;; (java/bigdec.ss) re-binds it to jbigdec?, so it can't be a static overlay const.
(define (jolt-decimal? x) #f)
(define (jolt-fn? x) (procedure? x))
(define (jolt-boolean-pred? x) (boolean? x))
;; (boolean x) coerces truthiness (nil/false -> false, else true).
;; (boolean x) coerces truthiness (nil/false -> false, else true). MUST stay native:
;; the backend's emit path calls clojure.core/boolean for every :if node
;; (backend_scheme.clj bool tracking), so it has to exist before ANY compilation,
;; including the kernel overlay tier (whose own fns contain `if`). Migrating it even
;; to the kernel tier deadlocks: compiling the tier that defines boolean needs boolean.
(define (jolt-boolean x) (if (jolt-truthy? x) #t #f))
;; (name x): keyword/symbol -> name string; string -> itself.
@ -57,8 +60,6 @@
(def-var! "clojure.core" "char?" jolt-char-pred?)
(def-var! "clojure.core" "integer?" jolt-integer?)
(def-var! "clojure.core" "float?" jolt-float?)
(def-var! "clojure.core" "ratio?" jolt-ratio?)
(def-var! "clojure.core" "rational?" jolt-rational?)
(def-var! "clojure.core" "decimal?" jolt-decimal?)
;; == numeric value-equality (ignores exactness, unlike =): (== 3 3.0) -> true.
;; 1-arity is trivially true; 2+ args must all be numbers (Numbers.equiv throws
@ -80,10 +81,30 @@
(def-var! "clojure.core" "vector?" jolt-vector?)
(def-var! "clojure.core" "set?" jolt-set?)
(def-var! "clojure.core" "seq?" jolt-seq?)
(def-var! "clojure.core" "list?" jolt-list-pred?)
(def-var! "clojure.core" "coll?" jolt-coll-pred?)
(def-var! "clojure.core" "fn?" jolt-fn?)
(def-var! "clojure.core" "boolean?" jolt-boolean-pred?)
(def-var! "clojure.core" "boolean" jolt-boolean)
(def-var! "clojure.core" "name" jolt-name)
(def-var! "clojure.core" "namespace" jolt-namespace)
;; --- jolt.host raw type-test primitives -------------------------------------
;; Some clojure.core predicates bottom out at host tests overlay Clojure can't
;; reach. Expose the ones the migratable predicates need so the overlay versions
;; lower to exactly these calls — no perf loss. rational-type? is the Chez TYPE
;; test (exact rational), distinct from clojure.core/rational? (which gates on
;; number? first). exact? is wrapped TOTAL (Chez's raw exact? errors on a
;; non-number); rational-type? already returns #f for a non-match.
;;
;; Only the tests consumed by the migrated predicates (ratio?/rational? -> exact?,
;; rational-type?; list? -> cseq?/cseq-list?/empty-list?) are exposed. The rest of
;; the predicate web stays native and is NOT exposed: map?/set?/seq?/coll? are
;; extended at runtime with sorted/record/lazy arms, decimal? is extended by the
;; optional bigdec module, integer?/float? are on the compiler emit/inference path,
;; and vector? is reached by the kernel-tier peek during bootstrap.
(define (jh-exact? x) (and (number? x) (exact? x)))
(def-var! "jolt.host" "exact?" jh-exact?)
(def-var! "jolt.host" "rational-type?" rational?)
(def-var! "jolt.host" "cseq?" cseq?)
(def-var! "jolt.host" "empty-list?" empty-list-t?)
(def-var! "jolt.host" "cseq-list?" cseq-list?)

File diff suppressed because one or more lines are too long

View file

@ -116,6 +116,8 @@
(def-var! "clojure.core" "mapv" (letrec ((mapv (lambda (f . colls) (let fnrec5542 ((f f) (colls (list->cseq colls))) (jolt-invoke (var-deref "clojure.core" "vec") (jolt-apply jolt-map f colls)))))) mapv)))
(guard (e (#t #f))
(def-var! "clojure.core" "update" (letrec ((update (lambda (m k f . args) (let fnrec5543 ((m m) (k k) (f f) (args (list->cseq args))) (jolt-assoc m k (jolt-apply f (jolt-get m k) args)))))) update)))
(guard (e (#t #f))
(def-var! "clojure.core" "set" (letrec ((set (lambda (coll) (let fnrec5544 ((coll coll)) (if (jolt-nil? coll) (jolt-hash-set) (jolt-apply jolt-hash-set (jolt-seq coll))))))) set)))
(guard (e (#t #f))
(def-var! "clojure.core" "vreset!" (letrec ((vreset! (lambda (vol newval) (let fnrec4683 ((vol vol) (newval newval)) (begin (jolt-invoke (var-deref "jolt.host" "ref-put!") vol (keyword #f "val") newval) newval))))) vreset!)))
(guard (e (#t #f))
@ -203,13 +205,13 @@
(guard (e (#t #f))
(def-var! "clojure.core" "simple-ident?" (letrec ((simple-ident? (lambda (x) (let fnrec4789 ((x x)) (let* ((or__26__auto (jolt-invoke (var-deref "clojure.core" "simple-symbol?") x))) (if (jolt-truthy? or__26__auto) or__26__auto (jolt-invoke (var-deref "clojure.core" "simple-keyword?") x))))))) simple-ident?)))
(guard (e (#t #f))
(def-var! "clojure.core" "ratio?" (letrec ((ratio? (lambda (x) (let fnrec4790 ((x x)) #f)))) ratio?)))
(def-var! "clojure.core" "ratio?" (letrec ((ratio? (lambda (x) (let fnrec4790 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "number?") x))) (if (jolt-truthy? and__25__auto) (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "exact?") x))) (if (jolt-truthy? and__25__auto) (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "rational-type?") x))) (if (jolt-truthy? and__25__auto) (jolt-not (jolt-invoke (var-deref "clojure.core" "integer?") x)) and__25__auto)) and__25__auto)) and__25__auto)))))) ratio?)))
(guard (e (#t #f))
(def-var! "clojure.core" "decimal?" (letrec ((decimal? (lambda (x) (let fnrec4791 ((x x)) #f)))) decimal?)))
(def-var! "clojure.core" "rational?" (letrec ((rational? (lambda (x) (let fnrec4791 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "number?") x))) (if (jolt-truthy? and__25__auto) (jolt-invoke (var-deref "jolt.host" "exact?") x) and__25__auto)))))) rational?)))
(guard (e (#t #f))
(def-var! "clojure.core" "class?" (letrec ((class? (lambda (x) (let fnrec4792 ((x x)) #f)))) class?)))
(guard (e (#t #f))
(def-var! "clojure.core" "rational?" (letrec ((rational? (lambda (x) (let fnrec4793 ((x x)) (jolt-invoke (var-deref "clojure.core" "int?") x))))) rational?)))
(def-var! "clojure.core" "list?" (letrec ((list? (lambda (x) (let fnrec4793 ((x x)) (let* ((or__26__auto (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "cseq?") x))) (if (jolt-truthy? and__25__auto) (jolt-invoke (var-deref "jolt.host" "cseq-list?") x) and__25__auto)))) (if (jolt-truthy? or__26__auto) or__26__auto (jolt-invoke (var-deref "jolt.host" "empty-list?") x))))))) list?)))
(guard (e (#t #f))
(def-var! "clojure.core" "nat-int?" (letrec ((nat-int? (lambda (x) (let fnrec4794 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "int?") x))) (if (jolt-truthy? and__25__auto) (>= x 0) and__25__auto)))))) nat-int?)))
(guard (e (#t #f))
@ -485,9 +487,9 @@
(guard (e (#t #f))
(def-var! "clojure.core" "trampoline" (letrec ((trampoline (case-lambda ((f) (let fnrec4585 ((f f)) (let* ((ret (jolt-invoke f))) (if (jolt-truthy? (jolt-invoke (var-deref "clojure.core" "fn?") ret)) (trampoline ret) ret)))) ((f . args) (let fnrec4586 ((f f) (args (list->cseq args))) (trampoline (lambda () (let fnrec4587 () (jolt-apply f args))))))))) trampoline)))
(guard (e (#t #f))
(def-var! "clojure.core" "max" (letrec ((_max (case-lambda ((x) (let fnrec4588 ((x x)) x)) ((x y) (let fnrec4589 ((x x) (y y)) (if (> x y) x y))) ((x y . more) (let fnrec4590 ((x x) (y y) (more (list->cseq more))) (jolt-reduce _max (_max x y) more)))))) _max)))
(def-var! "clojure.core" "max" (letrec ((max (case-lambda ((x) (let fnrec4588 ((x x)) x)) ((x y) (let fnrec4589 ((x x) (y y)) (if (> x y) x y))) ((x y . more) (let fnrec4590 ((x x) (y y) (more (list->cseq more))) (jolt-reduce max (max x y) more)))))) max)))
(guard (e (#t #f))
(def-var! "clojure.core" "min" (letrec ((_min (case-lambda ((x) (let fnrec4591 ((x x)) x)) ((x y) (let fnrec4592 ((x x) (y y)) (if (< x y) x y))) ((x y . more) (let fnrec4593 ((x x) (y y) (more (list->cseq more))) (jolt-reduce _min (_min x y) more)))))) _min)))
(def-var! "clojure.core" "min" (letrec ((min (case-lambda ((x) (let fnrec4591 ((x x)) x)) ((x y) (let fnrec4592 ((x x) (y y)) (if (< x y) x y))) ((x y . more) (let fnrec4593 ((x x) (y y) (more (list->cseq more))) (jolt-reduce min (min x y) more)))))) min)))
(guard (e (#t #f))
(def-var! "clojure.core" "reverse" (letrec ((reverse (lambda (coll) (let fnrec4594 ((coll coll)) (jolt-reduce jolt-conj (jolt-list ) coll))))) reverse)))
(guard (e (#t #f))

View file

@ -43,3 +43,10 @@
(defn mapv [f & colls] (vec (apply map f colls)))
(defn update [m k f & args] (assoc m k (apply f (get m k) args)))
;; set: realize a seqable and dedup through the set constructor; nil -> #{}. The
;; compiler uses it off the emit path (backend bare-native-names, type inference),
;; so unlike boolean it can live here — compiling this tier never calls set, and by
;; the time those callers run the tier is bound. Pure composition of hash-set/seq/
;; apply, so it lowers to the same code the native shim did.
(defn set [coll] (if (nil? coll) #{} (apply hash-set (seq coll))))

View file

@ -177,13 +177,21 @@
(defn simple-ident? [x] (or (simple-symbol? x) (simple-keyword? x)))
;; Jolt has no ratio or bigdecimal types, so these are constants / reduce to int?.
(defn ratio? [x] false)
(defn decimal? [x] false)
;; No first-class Class objects either: class names are symbols the evaluator
;; handles in instance?/new positions, never values — so nothing is a class.
;; Numeric-tower predicates over the Chez tower (jolt has exact ints, ratios, and
;; flonums). ratio? = exact non-integer; rational? = exact (int or ratio). Built on
;; the jolt.host tower tests so they lower to the same code the native shims did.
;; decimal?/integer?/float?/int?/double? stay native (bigdec-extended or on the
;; compiler emit/inference path) — see predicates.ss.
(defn ratio? [x]
(and (number? x) (jolt.host/exact? x) (jolt.host/rational-type? x) (not (integer? x))))
(defn rational? [x] (and (number? x) (jolt.host/exact? x)))
;; No first-class Class objects: class names are symbols the evaluator handles in
;; instance?/new positions, never values — so nothing is a class.
(defn class? [x] false)
(defn rational? [x] (int? x))
;; list?: a list-marked cseq node or the empty list (). A lazy/vector-backed seq,
;; (rest list), (seq coll), (map …) are seqs but not lists. Not extended like
;; map?/set?/seq?, so it migrates cleanly.
(defn list? [x] (or (and (jolt.host/cseq? x) (jolt.host/cseq-list? x)) (jolt.host/empty-list? x)))
(defn nat-int? [x] (and (int? x) (>= x 0)))
(defn neg-int? [x] (and (int? x) (neg? x)))
(defn pos-int? [x] (and (int? x) (pos? x)))