diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index 9443e87..8d362e0 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -5,3 +5,4 @@ {"id":"int-a7573343","kind":"field_change","created_at":"2026-06-04T18:09:14.608401Z","actor":"Yogthos","issue_id":"jolt-alz","extra":{"field":"status","new_value":"closed","old_value":"open"}} {"id":"int-40cd8348","kind":"field_change","created_at":"2026-06-04T18:09:15.510649Z","actor":"Yogthos","issue_id":"jolt-nte","extra":{"field":"status","new_value":"closed","old_value":"open"}} {"id":"int-57f4d2b3","kind":"field_change","created_at":"2026-06-04T18:31:23.83434Z","actor":"Yogthos","issue_id":"jolt-9sb","extra":{"field":"status","new_value":"closed","old_value":"open"}} +{"id":"int-f46da620","kind":"field_change","created_at":"2026-06-04T18:33:45.099148Z","actor":"Yogthos","issue_id":"jolt-ytt","extra":{"field":"status","new_value":"closed","old_value":"open"}} diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a71cc04..50d79cf 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -7,7 +7,7 @@ {"_type":"issue","id":"jolt-gxr","title":"HIGH: aliased namespace calls (require :as) don't resolve (s/join)","status":"open","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:58Z","created_by":"Yogthos","updated_at":"2026-06-04T18:09:58Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-4z9","title":"HIGH: dispatch — multimethod :default, protocol-on-record, extend-protocol","status":"open","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:57Z","created_by":"Yogthos","updated_at":"2026-06-04T18:09:57Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-40n","title":"HIGH: update/update-in/assoc-in fail on map literals (struct)","status":"open","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:09:56Z","dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"jolt-ytt","title":"HIGH: str semantics — (str nil)='nil' should be '', (str :b)='b' should be ':b'","status":"open","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:09:56Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"jolt-ytt","title":"HIGH: str semantics — (str nil)='nil' should be '', (str :b)='b' should be ':b'","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:33:45Z","closed_at":"2026-06-04T18:33:45Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-9sb","title":"HIGH: destructuring gaps (nested seq, :strs, nested map, fn-param)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:55Z","created_by":"Yogthos","updated_at":"2026-06-04T18:31:24Z","closed_at":"2026-06-04T18:31:24Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-7dy","title":"MED: compiler path lacks IFn dispatch (collections-as-fn) — only interpreter fixed","status":"open","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T18:10:00Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-90c","title":"MED: filter/take-while not lazy — infinite-seq (take N (filter p (range))) loops","status":"open","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T18:10:00Z","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 1795b61..056f092 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -799,13 +799,72 @@ # String functions # ============================================================ +# Readable rendering of a value (Clojure pr semantics): strings quoted, +# keywords with leading ':', symbols by name, collections with their reader +# syntax. Used by both pr-str (readable) and str (collection elements). +(var pr-render nil) + +(defn- pr-render-seq [buf items open close] + (buffer/push-string buf open) + (var first true) + (each x items + (if first (set first false) (buffer/push-string buf " ")) + (pr-render buf x)) + (buffer/push-string buf close)) + +(defn- pr-render-pairs [buf pairs] + (buffer/push-string buf "{") + (var first true) + (each pair pairs + (if first (set first false) (buffer/push-string buf ", ")) + (pr-render buf (in pair 0)) + (buffer/push-string buf " ") + (pr-render buf (in pair 1))) + (buffer/push-string buf "}")) + +(set pr-render + (fn [buf v] + (cond + (nil? v) (buffer/push-string buf "nil") + (= true v) (buffer/push-string buf "true") + (= false v) (buffer/push-string buf "false") + (string? v) (do (buffer/push-string buf "\"") (buffer/push-string buf v) (buffer/push-string buf "\"")) + (buffer? v) (do (buffer/push-string buf "\"") (buffer/push-string buf (string v)) (buffer/push-string buf "\"")) + (keyword? v) (do (buffer/push-string buf ":") (buffer/push-string buf (string v))) + (number? v) (buffer/push-string buf (string v)) + (and (struct? v) (= :symbol (v :jolt/type))) + (buffer/push-string buf (if (v :ns) (string (v :ns) "/" (v :name)) (v :name))) + (lazy-seq? v) (pr-render-seq buf (realize-for-iteration v) "(" ")") + (set? v) (pr-render-seq buf (phs-seq v) "#{" "}") + (phm? v) (pr-render-pairs buf (phm-entries v)) + (and (table? v) (get v :jolt/deftype)) (buffer/push-string buf (string v)) + (tuple? v) (pr-render-seq buf v "[" "]") + (array? v) (pr-render-seq buf v "(" ")") + (struct? v) (pr-render-pairs buf (pairs v)) + (table? v) (pr-render-pairs buf (pairs v)) + true (buffer/push-string buf (string v))))) + +(defn- str-render-one + "Render one value with Clojure's `str`/.toString semantics (bare strings, + nil -> empty, keywords/symbols by name, collections via pr-render)." + [v] + (cond + (nil? v) "" + (string? v) v + (buffer? v) (string v) + (keyword? v) (string ":" (string v)) + (and (struct? v) (= :symbol (v :jolt/type))) + (if (v :ns) (string (v :ns) "/" (v :name)) (v :name)) + (number? v) (string v) + (= true v) "true" + (= false v) "false" + (let [buf @""] (pr-render buf v) (string buf)))) + (defn core-str [& xs] (if (= 0 (length xs)) "" (do (var result @[]) - (each x xs - (if (nil? x) (array/push result "nil") - (array/push result (if (string? x) x (string x))))) + (each x xs (array/push result (str-render-one x))) (string/join result "")))) (defn core-name @@ -856,14 +915,7 @@ (var i 0) (let [n (length xs)] (while (< i n) - (def v (xs i)) - (cond - (nil? v) (buffer/push-string buf "nil") - (string? v) (do (buffer/push-string buf "\"") (buffer/push-string buf v) (buffer/push-string buf "\"")) - (keyword? v) (do (buffer/push-string buf ":") (buffer/push-string buf (string v))) - (= true v) (buffer/push-string buf "true") - (= false v) (buffer/push-string buf "false") - (buffer/push-string buf (string v))) + (pr-render buf (xs i)) (when (< (+ i 1) n) (buffer/push-string buf " ")) (++ i))) (string buf)) diff --git a/test/cljs-port-1.janet b/test/cljs-port-1.janet index 36452cd..e25f119 100644 --- a/test/cljs-port-1.janet +++ b/test/cljs-port-1.janet @@ -89,7 +89,7 @@ (assert (= "hello" (ct-eval ctx "(str \"hello\")")) "str") (assert (= "ab" (ct-eval ctx "(str \"a\" \"b\")")) "str two") (assert (= "42" (ct-eval ctx "(str 42)")) "str number") - (assert (= "nil" (ct-eval ctx "(str nil)")) "str nil") + (assert (= "" (ct-eval ctx "(str nil)")) "str nil -> empty string (Clojure semantics)") (assert (= "a" (ct-eval ctx "(name :a)")) "name")) (print " passed") (print "9: apply...")