(defun expand-handler-bind (type lambda-list &key before-unwind after-unwind)
"Helper for the HANDLER-CASE* macro. This one creates the HANDLER-BIND lambdas out of the :BEFORE-UNWIND form."
(declare (ignorable after-unwind))
(when before-unwind
(when (null lambda-list)
(error ":BEFORE-UNWIND must bind the condtion to a variable."))
`(,type (lambda ,lambda-list (declare (ignorable ,@lambda-list)) ,before-unwind))))
(defun expand-handler-case (type lambda-list &key before-unwind after-unwind)
"Helper for the HANDLER-CASE* macro. This one creates the HANDLER-CASE handler-clauses out of the :AFTER-UNWIND form"
(declare (ignorable before-unwind))
(if after-unwind
`(,type ,lambda-list (declare (ignorable ,@lambda-list)) ,after-unwind))))
(defmacro handler-case* (form &rest cases)
"Like HANDLER-CASE and HANDLER-BIND rolled into one. Example usage:
(handler-case*
(restart-case
(error \"ZOMG! ERROR!\")
(fuck-it () 'ignored))
(t (condition)
:before-unwind
(progn (format t \"An error occurred Ignore it (y/N)? \")
(if (eq (read) 'y)
(invoke-restart 'fuck-it)))
:after-unwind
(format t \"You just couldn't fucking ignore it, could you?~%\")))
:before-unwind is, of course, executed before the stack unrolls, so you can
invoke restarts from there. If no restart is invoked, the error falls through
to the :after-unwind case, where you can handle it like a regular handler-case.
The above paragraph is not 100% accurate: If the :BEFORE-UNWIND case does not
invoke a restart, another HANDLER-CASE or HANDLER-CASE* form may get control
of the error *before* the :AFTER-UNWIND case:
(defun handles-own-error ()
(handler-case
(error \"ZOMG! ERROR!\")
(t () :i-got-this)))
(handler-case*
(restart-case
(handles-own-error)
(fuck-it () 'ignored))
(t (condition)
:before-unwind
(progn (format t \"An error occurred Ignore it (y/N)? \")
(if (eq (read) 'y)
(invoke-restart 'fuck-it)))
:after-unwind
(format t \"You just couldn't fucking ignore it, could you?~%\")))
In this case, first the :BEFORE-UNWIND case is evaluated, and then the error is given
to the HANDLER-CASE form *inside* HANDLES-OWN-ERROR. The :AFTER-UNWIND case is never
evaluated.
If no :after-unwind form is provided and no restart is invoked, the condition is not trapped."
`(handler-case
(handler-bind ,(remove nil (mapply #'expand-handler-bind cases))
,form)
,@(remove nil (mapply #'expand-handler-case cases))))
Provides: HANDLER-CASE*
Requires: MAPPLY
Author: Jeremy Phelps phelpsj@nuvox.net
License: Public Domain
;; Write the code here.
Provides: HANDLER-CASE* Requires: MAPPLY Author: Jeremy Phelps phelpsj@nuvox.net License: Public Domain