Merge pull request #82 from jolt-lang/printer-cold-types

core: cold tagged-type printing migrates to print-method defmethods
This commit is contained in:
Dmitri Sotnikov 2026-06-11 22:44:06 +00:00 committed by GitHub
commit 58f9a5e596
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 47 additions and 6 deletions

View file

@ -156,3 +156,24 @@
(if (keyword? t) t (type x))))) (if (keyword? t) t (type x)))))
(defmethod print-dup :default [o w] (print-method o w)) (defmethod print-dup :default [o w] (print-method o w))
;; Cold tagged-type renderings, migrated from the host renderer (the hot
;; types — numbers, strings, symbols, collections — stay native). Each is the
;; exact output the host branch produced.
(defmethod print-method :jolt/uuid [u w]
(.write w (str "#uuid \"" (get u :str) "\""))
nil)
(defmethod print-method :jolt/regex [re w]
(.write w (str "#\"" (get re :source) "\""))
nil)
;; a transient's get IS the dispatched collection lookup — read the wrapper's
;; own :kind field with the host accessor (same trap as sorted colls).
(defmethod print-method :jolt/transient [t w]
(.write w (str "#<transient " (name (jolt.host/ref-get t :kind)) ">"))
nil)
(defmethod print-method :jolt/chan [c w]
(.write w "#<channel>")
nil)

View file

@ -1560,13 +1560,9 @@
10 "newline" 32 "space" 9 "tab" 13 "return" 10 "newline" 32 "space" 9 "tab" 13 "return"
12 "formfeed" 8 "backspace" 0 "nul" 12 "formfeed" 8 "backspace" 0 "nul"
(char->string v)))) (char->string v))))
(regex? v) (do (buffer/push-string buf "#\"") (buffer/push-string buf (v :source)) (buffer/push-string buf "\""))
(number? v) (buffer/push-string buf (fmt-number v)) (number? v) (buffer/push-string buf (fmt-number v))
(and (struct? v) (= :symbol (v :jolt/type))) (and (struct? v) (= :symbol (v :jolt/type)))
(buffer/push-string buf (if (v :ns) (string (v :ns) "/" (v :name)) (v :name))) (buffer/push-string buf (if (v :ns) (string (v :ns) "/" (v :name)) (v :name)))
(and (struct? v) (= :jolt/uuid (v :jolt/type)))
(do (buffer/push-string buf "#uuid \"") (buffer/push-string buf (v :str))
(buffer/push-string buf "\""))
(and (struct? v) (= :jolt/inst (v :jolt/type))) (and (struct? v) (= :jolt/inst (v :jolt/type)))
(do (buffer/push-string buf "#inst \"") (buffer/push-string buf (inst->rfc3339 v)) (do (buffer/push-string buf "#inst \"") (buffer/push-string buf (inst->rfc3339 v))
(buffer/push-string buf "\"")) (buffer/push-string buf "\""))
@ -1581,8 +1577,6 @@
(lazy-seq? v) (pr-render-seq buf (realize-for-iteration v) "(" ")") (lazy-seq? v) (pr-render-seq buf (realize-for-iteration v) "(" ")")
(set? v) (pr-render-seq buf (phs-seq v) "#{" "}") (set? v) (pr-render-seq buf (phs-seq v) "#{" "}")
(phm? v) (pr-render-pairs buf (phm-entries v)) (phm? v) (pr-render-pairs buf (phm-entries v))
(core-transient? v) (buffer/push-string buf (string "#<transient " (v :kind) ">"))
(and (table? v) (= :jolt/chan (get v :jolt/type))) (buffer/push-string buf "#<channel>")
(pvec? v) (pr-render-seq buf (pv->array v) "[" "]") (pvec? v) (pr-render-seq buf (pv->array v) "[" "]")
(plist? v) (pr-render-seq buf (pl->array v) "(" ")") (plist? v) (pr-render-seq buf (pl->array v) "(" ")")
(and (table? v) (get v :jolt/deftype)) (and (table? v) (get v :jolt/deftype))
@ -1597,6 +1591,15 @@
(tuple? v) (pr-render-seq buf v "[" "]") (tuple? v) (pr-render-seq buf v "[" "]")
# mutable mode: arrays are vectors -> print with [] (else lists -> ()) # mutable mode: arrays are vectors -> print with [] (else lists -> ())
(array? v) (if mutable? (pr-render-seq buf v "[" "]") (pr-render-seq buf v "(" ")")) (array? v) (if mutable? (pr-render-seq buf v "[" "]") (pr-render-seq buf v "(" ")"))
# Any remaining TAGGED value dispatches through print-method when the
# hook is wired: the io tier owns the cold renderings (uuid, regex,
# transient, channel — branches that used to live here), and user
# defmethods on any :jolt/* tag fire from inside nested values. Before
# the overlay loads (init-time error messages) these fall through to
# the raw pairs view below.
(and print-method-cb (get v :jolt/type)
(print-method-cb v (fn [piece] (buffer/push-string buf piece))))
nil
(struct? v) (pr-render-pairs buf (pairs v)) (struct? v) (pr-render-pairs buf (pairs v))
(table? v) (pr-render-pairs buf (pairs v)) (table? v) (pr-render-pairs buf (pairs v))
true (buffer/push-string buf (string v))))) true (buffer/push-string buf (string v)))))

View file

@ -95,3 +95,20 @@
"(let [w (StringWriter.)] (.write w \"a\") (.append w \\b) (.toString w))"] "(let [w (StringWriter.)] (.write w \"a\") (.append w \\b) (.toString w))"]
["methods table inspectable" "true" ["methods table inspectable" "true"
"(do (defrecord Pt [x y]) (defmethod print-method (quote user.Pt) [r w] r) (contains? (methods print-method) (quote user.Pt)))"]) "(do (defrecord Pt [x y]) (defmethod print-method (quote user.Pt) [r w] r) (contains? (methods print-method) (quote user.Pt)))"])
# Cold tagged-type printing now lives in io-tier defmethods (the host
# renderer dispatches any remaining :jolt/* tagged value through the
# print-method hook). Outputs unchanged from the old host branches; any
# tagged type is now user-overridable, atoms included.
(defspec "io / cold tagged types via print-method"
["uuid" "\"#uuid \\\"b6883c0a-0342-4007-9966-bc2dfa6b109e\\\"\""
"(pr-str (parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\"))"]
["uuid nested" "\"[#uuid \\\"b6883c0a-0342-4007-9966-bc2dfa6b109e\\\"]\""
"(pr-str [(parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\")])"]
["regex" "\"#\\\"a+b\\\"\"" "(pr-str #\"a+b\")"]
["transient vector" "\"#<transient vector>\"" "(pr-str (transient [1]))"]
["transient map" "\"#<transient map>\"" "(pr-str (transient {:a 1}))"]
["atom override fires nested" "\"{:a #atom[7]}\""
"(do (defmethod print-method :jolt/atom [a w] (.write w (str \"#atom[\" (deref a) \"]\"))) (pr-str {:a (atom 7)}))"]
["uuid through str unchanged" "\"b6883c0a-0342-4007-9966-bc2dfa6b109e\""
"(str (parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\"))"])