Merge pull request #95 from jolt-lang/migratus-fixes

Migratus fixes
This commit is contained in:
Dmitri Sotnikov 2026-06-13 23:13:43 +00:00 committed by GitHub
commit 52bea0b620
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1214 additions and 114 deletions

View file

@ -110,7 +110,12 @@
;; then. Its body resolves fn/map/reduce/cond at EXPANSION time, by which point all
;; of 00-syntax has loaded, so using them here is fine.
(defmacro ns [nm & clauses]
(let [calls (reduce
;; ^{:map} metadata on the ns name reads as a (with-meta sym {...}) form, not an
;; annotated symbol (jolt-8w2). Real libraries put :author/:doc there
;; (clojure.tools.logging), so unwrap to the bare symbol; jolt does not track
;; namespace metadata, so the map is dropped.
(let [nm (if (and (seq? nm) (= 'with-meta (first nm))) (second nm) nm)
calls (reduce
(fn [acc clause]
(if (seq? clause)
(let [head (first clause) args (rest clause)]
@ -341,10 +346,14 @@
(defmacro defn [fn-name & body]
(let [body (if (and (seq body) (string? (first body))) (rest body) body)
body (if (and (seq body) (map? (first body)) (not (symbol? (first body))))
(rest body) body)]
(rest body) body)
;; ^{:map} metadata on the name reads as a (with-meta sym …) form, not an
;; annotated symbol (jolt-8w2). def attaches the metadata, but fn needs a
;; bare symbol, so unwrap it for the fn name.
fn-only-name (if (symbol? fn-name) fn-name (first (rest fn-name)))]
;; pass the name through to fn: the compiled fn's janet name carries it,
;; so stack traces read app.deep/level3 instead of a gensym (jolt-2o7.1)
`(def ~fn-name (fn ~fn-name ~@body))))
`(def ~fn-name (fn ~fn-only-name ~@body))))
;; Jolt doesn't enforce privacy, so defn- is just defn (matching how Clojure's own
;; defn- delegates to defn with :private metadata).

View file

@ -519,7 +519,11 @@
;; via the host dir primitives. Paths (strings), not File objects. (Lives below
;; tree-seq: forward references are analysis errors now — jolt-2o7.3.)
(defn file-seq [root]
(tree-seq __dir? __list-dir root))
(if (__file? root)
;; java.io.File tree: walk via the File method surface so leaves are File
;; values callers can invoke .isFile/.getName/slurp on (jolt-hjw).
(tree-seq (fn [f] (.isDirectory f)) (fn [f] (seq (.listFiles f))) root)
(tree-seq __dir? __list-dir root)))
;; Canonical flatten via tree-seq: the leaves (non-sequential nodes) in order.
;; Flattens lists too (sequential?), matching Clojure/CLJS.

View file

@ -21,8 +21,15 @@
;; clojure.core fns) so they compile as plain invokes. name/mm are passed quoted;
;; the dispatch fn, options, and dispatch value evaluate normally, and the method
;; body becomes a compiled (fn …).
(defmacro defmulti [name dispatch & opts]
`(defmulti-setup (quote ~name) ~dispatch ~@opts))
;; Clojure allows (defmulti name docstring? attr-map? dispatch-fn & options);
;; drop a leading docstring and/or attr-map so the dispatch fn isn't mistaken for
;; one (migratus's multimethods carry docstrings).
(defmacro defmulti [name & args]
(let [args (if (string? (first args)) (rest args) args)
args (if (and (map? (first args)) (not (symbol? (first args)))) (rest args) args)
dispatch (first args)
opts (rest args)]
`(defmulti-setup (quote ~name) ~dispatch ~@opts)))
(defmacro defmethod [mm dispatch-val & fn-tail]
`(defmethod-setup (quote ~mm) ~dispatch-val (fn ~@fn-tail)))
@ -39,11 +46,14 @@
(defmacro remove-all-methods [mm]
`(remove-all-methods-setup (quote ~mm)))
;; methods/get-method take the multimethod VALUE (Clojure semantics); the setup
;; maps it back to its var via the registry, so a bare multifn ref works from a
;; compiled fn in any namespace (jolt multimethod table-visibility fix).
(defmacro get-method [mm dval]
`(get-method-setup (quote ~mm) ~dval))
`(get-method-setup ~mm ~dval))
(defmacro methods [mm]
`(methods-setup (quote ~mm)))
`(methods-setup ~mm))
;; prefers reads the store off the VAR (the multifn value can't carry it) —
;; same symbol-passing shape as the other multimethod table ops.

View file

@ -177,3 +177,30 @@
(defmethod print-method :jolt/chan [c w]
(.write w "#<channel>")
nil)
;; Minimal synchronous agent shim. jolt has no thread pool or STM, so this is
;; enough for libraries that hold an agent but don't depend on asynchronous
;; dispatch (e.g. clojure.tools.logging's *logging-agent*, which only sends from
;; within a transaction — never the case here, so it always logs directly). An
;; agent is an atom; send/send-off apply the action immediately. NOT concurrent.
(defn agent
"Creates an agent (an atom on jolt — synchronous, no async dispatch)."
[state & _opts]
(atom state))
(defn send-off
"Apply (action state & args) to the agent's state immediately; return the agent."
[a f & args]
(apply swap! a f args)
a)
(defn send
"Like send-off on jolt (no separate thread pool)."
[a f & args]
(apply swap! a f args)
a)
(defn agent-error
"jolt agents never enter an error state."
[_a]
nil)

View file

@ -182,9 +182,16 @@
(when (< (count items) 3)
(uncompilable "def with no init"))
(let [nm (form-sym-name name-sym)
cur (compile-ns ctx)]
cur (compile-ns ctx)
;; (def name docstring value): docstring is form 2, value form 3.
;; Matches the interpreter; without this the docstring was taken
;; as the value and the real init dropped (jolt-6ym).
has-doc (and (> (count items) 3) (string? (nth items 2)))
val-form (nth items (if has-doc 3 2))
base-meta (or (form-sym-meta name-sym) {})
node-meta (if has-doc (assoc base-meta :doc (nth items 2)) base-meta)]
(host-intern! ctx cur nm)
(def-node cur nm (analyze ctx (nth items 2) env) (form-sym-meta name-sym))))
(def-node cur nm (analyze ctx val-form env) node-meta)))
"let*" (let [bvec (vec (form-vec-items (nth items 1)))
r (analyze-bindings ctx bvec env)]
(let-node (first r) (analyze-seq ctx (drop 2 items) (second r))))