Skip to content

Instantly share code, notes, and snippets.

@spacebat
Last active March 28, 2018 22:27
Show Gist options
  • Save spacebat/3acc7185081f76bd6ceede659cb59410 to your computer and use it in GitHub Desktop.
Save spacebat/3acc7185081f76bd6ceede659cb59410 to your computer and use it in GitHub Desktop.
An extension to the syntax of condition-case to support else and finally cases
;; This gist licensed GPLv3
(cl-defmacro condition-case+ (var form (&rest handlers/else/finally))
"Like condition-case, only if the last handlers have matching
forms of :else or :finally. In that case, the body of an :else
handler is evaluated if no exception was thrown. The body of
a :finally clause is evaluated always as the last thing before
the form is exited whether normally or not. If both :else
and :finally appear among the handlers, :else must be second last
and :finally must be last."
(cl-flet ((maybe-split-last (symbol handlers)
(let ((last-handler (last handlers)))
(cond
((and (listp (car last-handler))
(eq (caar last-handler) symbol))
(list (butlast handlers)
(cdar last-handler)))
(t
(list handlers nil))))))
(destructuring-bind (handlers finally) (maybe-split-last :finally handlers/else/finally)
(destructuring-bind (handlers else) (maybe-split-last :else handlers)
(let ((errant-matchers (cl-loop for (match . handler-body) in handlers
if (member match '(:else :finally))
collect match)))
(when errant-matchers
(error "Matcher(s) %s found out of place. Please read the documentation"
(mapconcat 'prin1-to-string errant-matchers ", "))))
(let* ((success? (cl-gensym "success?"))
(body `(let (,success?)
(condition-case ,var
(prog1
,form
(setq ,success? t))
,@handlers)
,@(if else
`((when ,success?
,@(if (listp else)
else
(list else))))))))
(if finally
`(unwind-protect
,body
,@finally)
body))))))
@spacebat
Copy link
Author

ELISP> (defun test-condition-case+ (&optional error?)
         (let (result)
           (condition-case+ err
               (progn
                 (push "body" result)
                 (when error?
                   (error "%s" error?)))
             ((error
               (push (format "error handler: %s" err) result))
              (:else
               (push "else" result))
              (:finally
               (push "finally" result))))
           (reverse result)))
         
test-condition-case+
ELISP> (test-condition-case+ "crashbang")
("body" "error handler: (error crashbang)" "finally")

ELISP> (test-condition-case+ nil)
("body" "else" "finally")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment