fix: unify vector repr on tuples; realize lazy-seqs in =/vec/into; fix into for maps/lists; restore ns after PV load; deftype field-access fallback in IFn dispatch

This commit is contained in:
Yogthos 2026-06-04 13:59:36 -04:00
parent accb7a2c2f
commit 2ea5b2bb5f
5 changed files with 69 additions and 37 deletions

View file

@ -11,6 +11,7 @@
(defn- load-persistent-structures
"Load immutable persistent data structures and swap clojure.core bindings."
[ctx]
(def saved-ns (ctx-current-ns ctx))
(def source (slurp "src/jolt/clojure/lang/persistent_vector.clj"))
(var cur source)
(while (> (length (string/trim cur)) 0)
@ -18,11 +19,17 @@
(set cur rest)
(when (not (nil? form))
(eval-form ctx @{} form)))
(let [core-ns (ctx-find-ns ctx "clojure.core")
pv-ns (ctx-find-ns ctx "jolt.lang.persistent-vector")]
(ns-intern core-ns "vec" (var-get (ns-find pv-ns "vector")))
(ns-intern core-ns "vector" (var-get (ns-find pv-ns "vector")))
(ns-intern core-ns "vector?" (var-get (ns-find pv-ns "vector?")))))
# Vectors are represented as Janet tuples throughout core; bind vec/vector/
# vector? to the tuple-based implementations so literals (`[...]`) and the
# constructors share one representation. The PersistentVector namespace stays
# loaded for code that wants it explicitly via jolt.lang.persistent-vector.
(let [core-ns (ctx-find-ns ctx "clojure.core")]
(ns-intern core-ns "vec" core-vec)
(ns-intern core-ns "vector" core-vector)
(ns-intern core-ns "vector?" core-vector?))
# Restore the namespace: loading the PV file above left current-ns set to
# jolt.lang.persistent-vector, which would shadow clojure.core bindings.
(ctx-set-current-ns ctx saved-ns))
(defn init
"Create a new Jolt evaluation context.

View file

@ -86,13 +86,19 @@
# Comparison
# ============================================================
(defn- norm-for-= [x]
"Realize a lazy-seq to a tuple so it compares as a Clojure sequence."
(if (lazy-seq? x)
(let [s (ls-seq x)] (if (nil? s) [] (tuple/slice (tuple ;s))))
x))
(defn core-= [& args]
(if (< (length args) 2) true
(do
(var ok true)
(var i 0)
(while (and ok (< i (dec (length args))))
(let [a (args i) b (args (+ i 1))]
(let [a (norm-for-= (args i)) b (norm-for-= (args (+ i 1)))]
(set ok
(if (and (tuple? a) (array? b))
(deep= a (tuple/slice (tuple ;b)))
@ -232,24 +238,54 @@
(if (struct? coll) (tuple ;(keys coll))
coll))))))))
(defn realize-for-iteration [c]
"If c is a lazy-seq, traverse and return all its elements as an array.
Otherwise return c as-is. Warning: will loop on infinite lazy-seqs.
Correctly handles nil elements (terminates on the empty cell, not on nil)."
(if (lazy-seq? c)
(do
(var items @[])
(var cur c)
(var go true)
(while go
(let [cell (realize-ls cur)]
(if (or (nil? cell) (= :jolt/pending cell) (= 0 (length cell)))
(set go false)
(do
(array/push items (in cell 0))
(let [rt (in cell 1)]
(if (nil? rt) (set go false) (set cur (make-lazy-seq rt))))))))
items)
c))
(defn core-vec [coll]
(if (tuple? coll) coll
(if (array? coll) (tuple ;coll)
(if (struct? coll) (tuple ;(map |(in (kvs coll) (+ (* $ 2) 1)) (range (/ (length (kvs coll)) 2))))
(tuple)))))
(let [coll (realize-for-iteration coll)]
(if (tuple? coll) coll
(if (array? coll) (tuple ;coll)
(if (struct? coll) (tuple ;(map |(in (kvs coll) (+ (* $ 2) 1)) (range (/ (length (kvs coll)) 2))))
(if (string? coll) (tuple ;(map |(string/from-bytes $) (string/bytes coll)))
(tuple)))))))
(defn core-into [to from]
(if (tuple? to)
(tuple/slice (tuple ;(array/concat (array/slice to) (if (tuple? from) from (array/slice from)))))
(if (array? to)
(array/concat to from)
(if (struct? to)
(let [items (realize-for-iteration from)]
(cond
# map target: each item is a [k v] pair (or map entry) to assoc
(or (phm? to) (struct? to) (and (table? to) (get to :jolt/deftype)))
(do
(var result to)
(each [k v] (pairs from)
(set result (merge result {k v})))
(each item items
(set result (core-assoc result (in item 0) (in item 1))))
result)
to))))
# list target (jolt lists are arrays): conj prepends -> reversed order
(array? to)
(do
(var result (array/slice to))
(each x items (array/insert result 0 x))
result)
# vector target (jolt vectors are tuples): conj appends
(tuple? to)
(tuple/slice (tuple ;(array/concat (array/slice to) (array/slice items))))
to)))
(defn core-merge [& maps]
(if (phm? (first maps))
@ -289,18 +325,6 @@
# Sequence operations
# ============================================================
(defn- realize-for-iteration [c]
"If c is a lazy-seq, traverse and return all elements as an array.
Otherwise return c as-is. Warning: will loop on infinite lazy-seqs."
(if (lazy-seq? c)
(do
(var items @[])
(var cur c)
(while (not (nil? (ls-first cur)))
(array/push items (ls-first cur))
(set cur (ls-rest cur)))
items)
c))
(defn core-map [f & colls]
(if (= 1 (length colls))

View file

@ -49,7 +49,7 @@
and deftype/record values implementing IFn. `args` is an array."
[ctx f args]
(cond
(function? f) (apply f args)
(or (function? f) (cfunction? f)) (apply f args)
(keyword? f) (coll-lookup (get args 0) f (get args 1))
(and (struct? f) (= :symbol (f :jolt/type)))
(coll-lookup (get args 0) f (get args 1))
@ -66,11 +66,11 @@
(and (table? f) (get f :jolt/deftype))
(let [ifn-fn (find-protocol-method ctx (get f :jolt/deftype) "IFn" "-invoke")]
(if ifn-fn (apply ifn-fn f args)
(if (get f :jolt/protocol-methods)
(let [invoke-fn (get (f :jolt/protocol-methods) :-invoke)]
(if invoke-fn (apply invoke-fn f args)
(error (string "Cannot call " (type f) " as a function"))))
(error (string "Cannot call " (type f) " as a function")))))
(if (and (get f :jolt/protocol-methods) (get (f :jolt/protocol-methods) :-invoke))
(apply (get (f :jolt/protocol-methods) :-invoke) f args)
# No IFn impl: fall back to map-like field access, e.g. (point :x)
(let [v (get f (get args 0) :jolt/not-found)]
(if (= v :jolt/not-found) (get args 1) v)))))
(and (table? f) (get f :jolt/protocol-methods))
(let [invoke-fn (get (f :jolt/protocol-methods) :-invoke)]
(if invoke-fn (apply invoke-fn f args)