From 7f68a5872c36384e8e94bd6656ce306cf649b0ea Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 20:17:03 -0400 Subject: [PATCH] =?UTF-8?q?core:=20Phase=204=20=E2=80=94=20move=20ex-data/?= =?UTF-8?q?ex-message/ex-cause=20to=20the=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ex-info value exposes :jolt/type/:message/:data/:cause via get (it's the caught value too), so the accessors are pure over get — no host surface. The constructor (ex-info) stays in Janet since it wires into throw. A thrown non-ex-info arrives wrapped as {:jolt/type :jolt/exception :value v}; the overlay unwraps that, matching the old Janet helper. ex-cause now unwraps and returns nil (not false) on non-ex values, matching Clojure. Added non-ex-info and string regression cases. --- jolt-core/clojure/core/20-coll.clj | 17 +++++++++++++++++ src/jolt/core.janet | 14 ++------------ test/spec/exceptions-spec.janet | 5 ++++- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index 3d9760a..df358bc 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -236,3 +236,20 @@ (map? coll) (reduce (fn [acc k] (f acc k (get coll k))) init (keys coll)) (nil? coll) init :else (throw (str "reduce-kv not supported on: " coll)))) + +;; ex-info accessors. The Janet constructor (ex-info) stays — it builds the tagged +;; value and wires into throw — but the value exposes :jolt/type/:message/:data/ +;; :cause via get, so the accessors are pure over get. A thrown non-ex-info arrives +;; wrapped as {:jolt/type :jolt/exception :value v}; unwrap that first. +(defn- ex-info-val? [x] (= (get x :jolt/type) :jolt/ex-info)) +(defn- ex-unwrap [e] + (if (= (get e :jolt/type) :jolt/exception) (get e :value) e)) +(defn ex-data [e] + (let [e (ex-unwrap e)] (if (ex-info-val? e) (get e :data) nil))) +(defn ex-message [e] + (let [e (ex-unwrap e)] + (cond (ex-info-val? e) (get e :message) + (string? e) e + :else nil))) +(defn ex-cause [e] + (let [e (ex-unwrap e)] (if (ex-info-val? e) (get e :cause) nil))) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 5f4d311..3812ec8 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2374,14 +2374,8 @@ (defn core-ex-info [msg data & more] @{:jolt/type :jolt/ex-info :message msg :data data :cause (if (> (length more) 0) (in more 0) nil)}) -(defn core-ex-info? [x] (and (table? x) (= :jolt/ex-info (x :jolt/type)))) -(defn- unwrap-ex [e] - (if (and (or (table? e) (struct? e)) (= :jolt/exception (get e :jolt/type))) (get e :value) e)) -(defn core-ex-data [e] - (let [e (unwrap-ex e)] (if (core-ex-info? e) (e :data) nil))) -(defn core-ex-message [e] - (let [e (unwrap-ex e)] - (cond (core-ex-info? e) (e :message) (string? e) e nil))) +# ex-data / ex-message / ex-cause now live in the Clojure collection tier +# (core/20-coll.clj) — pure over get on the tagged value the constructor builds. # String split/replace that accept either a literal string or a regex value. (defn core-str-split [pat s] @@ -2688,7 +2682,6 @@ (defn core-hash-unordered-coll [coll] (var h 0) (each x (realize-for-iteration coll) (set h (band (+ h (h24 x)) 0xffffff))) h) -(defn core-ex-cause [e] (and (table? e) (get e :cause))) (defn core-prefers [mm-var] (or (get mm-var :jolt/prefers) {})) (defn core-random-uuid [] @@ -2840,7 +2833,6 @@ "hash-combine" core-hash-combine "hash-ordered-coll" core-hash-ordered-coll "hash-unordered-coll" core-hash-unordered-coll - "ex-cause" core-ex-cause "prefers" core-prefers "random-uuid" core-random-uuid "interpose" core-interpose @@ -2868,8 +2860,6 @@ "ifn?" core-ifn? "indexed?" core-indexed? "ex-info" core-ex-info - "ex-data" core-ex-data - "ex-message" core-ex-message "prn-str" core-prn-str "println-str" core-println-str "volatile?" core-volatile? diff --git a/test/spec/exceptions-spec.janet b/test/spec/exceptions-spec.janet index 7df5e47..ce89a14 100644 --- a/test/spec/exceptions-spec.janet +++ b/test/spec/exceptions-spec.janet @@ -35,4 +35,7 @@ ["catch binds thrown value" "42" "(try (throw 42) (catch :default e e))"] ["rethrow preserves ex" "\"inner\"" - "(try (try (throw (ex-info \"inner\" {})) (catch :default e (throw e))) (catch :default e (ex-message e)))"]) + "(try (try (throw (ex-info \"inner\" {})) (catch :default e (throw e))) (catch :default e (ex-message e)))"] + ["ex-data on non-ex" "nil" "(ex-data 42)"] + ["ex-cause on non-ex" "nil" "(ex-cause {:k 1})"] + ["ex-message of string" "\"hi\"" "(ex-message \"hi\")"])