core: pr/prn/pr-str/print/println to the overlay; pr-str escapes strings now

Round 6 of the seed shrink (the printer round, scoped by the perf wall). The
five wrappers move to 20-coll over two new host seams: __write (push a string
to *out*) and __pr-str1 (render one value readably). The renderer itself
stays in the seed — it's representation-coupled (pvec/phm/phs/sorted
internals) and shared with the hot str, and rendering through overlay calls
would pay the per-element call cost everywhere big values get printed.
print-method as a real multimethod is follow-up work.

The new spec rows caught a renderer bug: string bodies were never escaped, so
(pr-str "a\"b") didn't round-trip through the reader. pr-render now
escapes quote/backslash/control chars per Clojure.
This commit is contained in:
Yogthos 2026-06-11 14:12:12 -04:00
parent 5cac9efd4e
commit 1de5ede246
3 changed files with 60 additions and 41 deletions

View file

@ -17,6 +17,32 @@
;; even?/odd? stay in the seed: (filter even? ...) is idiomatic-hot and the
;; overlay versions cost an extra call layer per element (seq-pipe bench 4x).
;; The printing family, over two host seams: __write (push a string to *out*)
;; and __pr-str1 (render ONE value readably). The renderer itself stays host —
;; it's representation-coupled (pvec/phm/phs/sorted internals) and shared with
;; the hot str. print uses str semantics (unreadable), pr/pr-str readable;
;; println/prn append the newline. Defined this early because printf and the
;; print-str family below call them. (print-method as a real multimethod is a
;; separate project.)
(defn pr-str [& xs]
(loop [out "" s (seq xs) first? true]
(if s
(recur (str out (if first? "" " ") (__pr-str1 (first s))) (next s) false)
out)))
(defn pr [& xs] (__write (apply pr-str xs)) nil)
(defn prn [& xs] (apply pr xs) (__write "\n") nil)
(defn print [& xs]
(__write (loop [out "" s (seq xs) first? true]
(if s
(recur (str out (if first? "" " ") (str (first s))) (next s) false)
out)))
nil)
(defn println [& xs] (apply print xs) (__write "\n") nil)
;; Base is (hash-map), not the {} literal: a literal map is a struct that doesn't
;; canonicalize collection keys across representations (a {:a 1} literal vs
;; (hash-map :a 1) key), whereas a PHM does — so counting/grouping by collection