diff options
Diffstat (limited to 'src/clojure/contrib/condition.clj')
-rw-r--r-- | src/clojure/contrib/condition.clj | 87 |
1 files changed, 51 insertions, 36 deletions
diff --git a/src/clojure/contrib/condition.clj b/src/clojure/contrib/condition.clj index 5565ee8d..c396a7a9 100644 --- a/src/clojure/contrib/condition.clj +++ b/src/clojure/contrib/condition.clj @@ -22,10 +22,10 @@ ;; scgilardi (gmail) ;; Created 09 June 2009 -(ns - #^{:author "Stephen C. Gilardi", +(ns #^{:author "Stephen C. Gilardi", :doc "Flexible raising and handling of conditions. A condition is a map containing: + - keys and values specified as arguments to raise, and - a stack trace at key :stack-trace. @@ -35,49 +35,64 @@ Based on an idea from Chouser: http://groups.google.com/group/clojure/browse_frm/thread/da1285c538f22bb5"} clojure.contrib.condition (:require clojure.contrib.condition.Condition) - (:import clojure.contrib.condition.Condition)) + (:import clojure.contrib.condition.Condition) + (:use [clojure.contrib.def :only (defvar)])) + +(defvar *condition* + "While a handler is running, bound to the condition being handled") + +(defvar *selector* + "While a handler is running, bound to the selector returned by + dispatch-fn for *condition*") + +(defvar *condition-object* + "While a handler is running, bound to the Condition object being + handled") (defmacro raise - "Raises a condition with the supplied mappings. - keyval => key val" + "Raises a condition with the supplied mappings. With no arguments, + re-raises the current condition. (keyval => key val)" [& keyvals] - `(throw (Condition. (hash-map ~@keyvals)))) + `(let [m# (hash-map ~@keyvals)] + (throw (if (seq m#) + (Condition. m#) + *condition-object*)))) (defmacro handler-case - "Executes body in a context in which any raised conditions can be handled. - - dispatch-fn accepts a raised condition: a map, and returns a value used - to select a handler. + "Executes body in a context in which raised conditions can be handled. - The name specified by condition is bound to the condition within - handlers. + dispatch-fn accepts a raised condition (a map) and returns a selector + value used to choose a handler. Handlers are forms within body: (handle key ...) - If a condition is raised, executes the body of the first handler whose - key satisfies (isa? selector key). If no handlers match, the condition is - re-raised." - [dispatch-fn condition & body] - (let [selector (gensym "selector")] - (loop [[form & forms] body - m {:code [] :handlers []}] - (if form - (recur - forms - (if (and (list? form) (= (first form) 'handle)) - (let [[_ key & body] form - handler `[(isa? ~selector ~key) (do ~@body)]] - (update-in m [:handlers] concat handler)) - (update-in m [:code] conj form))) - (if (empty? (:handlers m)) - `(do ~@(:code m)) - `(try - ~@(:code m) - (catch Condition c# - (let [~condition (meta c#) - ~selector (~dispatch-fn ~condition)] - (cond ~@(:handlers m) - :else (throw c#)))))))))) + If a condition is raised, handler-case executes the body of the first + handler whose key satisfies (isa? selector key). If no handlers match, + the condition is re-raised. + + While a handler is running, *condition* is bound to the condition being + handled and *selector* is bound to to the value returned by dispatch-fn + that matched the handler's key." + [dispatch-fn & body] + (loop [[form & forms] body + m {:code [] :handlers []}] + (if form + (recur + forms + (if (and (list? form) (= (first form) 'handle)) + (let [[_ key & body] form + handler `[(isa? *selector* ~key) (do ~@body)]] + (update-in m [:handlers] concat handler)) + (update-in m [:code] conj form))) + `(try + ~@(:code m) + (catch Condition c# + (binding [*condition-object* c# + *condition* ^c# + *selector* (~dispatch-fn ^c#)] + (cond + ~@(:handlers m) + :else (raise)))))))) |