;;;;
;;;; Common Lisp Condition System for XLISP-STAT 2.0
;;;; XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney
;;;; Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz
;;;; You may give out copies of this software; for conditions see the file
;;;; COPYING included with this distribution.
;;;;
;; The condition system is used if the variable xlisp::*condition-hook* is
;; not nil. The internal functions xlerror and xlcerror, as well as the
;; Lisp-callable C functions xerror, xcerror, xsignal, xwarn, xbreak and
;; xdebug, call the hook with the function symbol, the frame index and the
;; current environment as arguments. The environment can be used by evalhook
;; for evaluating things in the environment that was active when the
;; condition was signaled.
;;
;; If an image containing the condition system is saved, then the function
;; xlisp::use-conditions needs t be called on startup to enable the
;; condition system.
;; To do:
;; **** rethink handling of internal xlabort's
;; **** make full version of assert, etypecase, ctypecase, ecase, ccase
;; **** make remaining standard condition types
;; **** collect debugger features
;; **** different debugging tools -- get-value, set-value as alt to evalhook?
;; **** move around in stack frames?
(in-package "XLISP")
(require "common")
;;;;
;;;; Exported Symbols
;;;;
;; Signaling Conditions
(export '(error cerror signal *break-on-signals*))
;; Assertions
(export '(check-type assert))
;; Exhaustive Case Analysis
(export '(etypecase ctypecase ecase ccase))
;; Handling Conditions
(export '(handler-case ignore-errors handler-bind))
;; Defining and Creating Conditions
(export '(define-condition make-condition))
;; Establishing Restarts
(export '(with-simple-restart restart-case restart-bind))
;; Finding and manipulating Restarts
(export '(compute-restarts restart-name find-restart invoke-restart
invoke-restart-interactively))
;; Warnings
(export 'warn)
;; Restart Functions
(export '(abort continue muffle-warning store-value use-value))
;; Debugging Utilities
(export '(break invoke-debugger *debugger-hook*))
;; Predefined Condition Types
(export '(condition simple-condition serious-condition
error simple-error arithmetic-error division-by-zero
cell-error unbound-variable undefined-function
control-error file-error package-error program-error
stream-error end-of-file type-error simple-type-error
storage-condition warning simple-warning
simple-condition-format-string simple-condition-format-arguments
type-error-datum type-error-expected-type package-error-package
stream-error-stream file-error-pathname cell-error-name
arithmetic-error-operation arithmetic-error-operands))
;; Restart Data Type
(export 'restart)
;;;;
;;;; Public Variables
;;;;
(defvar *break-on-signals* nil)
(defvar *debugger-hook* nil)
;;;;
;;;; Internal Variables
;;;;
;; Unique Markers
(defvar *eof-mark* (gensym "EOF"))
(defvar *not-found* (gensym "NOT-FOUND"))
;; Handler Variables
(defvar *default-handler* nil)
(defvar *active-handlers* nil)
;; Restart Variables
(defvar *default-restart* nil)
(defvar *active-restarts* nil)
(defvar *continue-restarts* nil)
(defvar *condition-restarts* nil)
;; Debugger Variables
(defvar *debug-level* 0)
(defvar *debug-env* nil)
(defvar *debug-frame* nil)
(defvar *debug-print-length* nil)
(defvar *debug-print-level* nil)
;;;;
;;;; Initialization Function
;;;; (Must be called on each system startup, e. g. as a startup action)
;;;;
(defun use-conditions (&optional (reset nil))
(setf *condition-hook* 'condition-hook)
(setf *active-restarts* (list (list nil *default-restart*)))
(setf *continue-restarts* *active-restarts*)
(setf *active-handlers*
(if *default-handler* (list (list nil t *default-handler*))))
(if reset (top-level)))
(defun unuse-conditions ()
(setf *condition-hook* nil)
(top-level))
;;;;
;;;; Internal Restart Representation
;;;;
(defstruct (restart (:print-function print-restart))
name
function
(test-function #'(lambda (c) (declare (ignore c)) t))
interactive-function
report-function)
(defun print-restart (restart stream depth)
(declare (ignore depth))
(if *print-escape*
(format stream "#<Restart ~a: ~d>"
(restart-name restart)
(address-of restart))
(let ((report (restart-report-function restart)))
(if report
(funcall report stream)
(let ((name (restart-name restart)))
(if name
(format stream "~a." name)
(error "can't print restart ~s without escapes"
restart)))))))
(setf *default-restart*
(make-restart :name 'abort
:function
#'(lambda (&rest args)
(declare (ignore args))
(top-level nil))
:test-function #'(lambda (c) (declare (ignore c)) t)
:report-function
#'(lambda (s)
(format s "Return to Lisp Toplevel."))))
;;;;
;;;; Helper Functions for RESTART-CASE and RESTART-BIND
;;;;
(defun push-restarts (c reslist)
(dolist (r (reverse reslist))
(push (list c r) *active-restarts*)))
(defun expand-restart-binding-form (x)
(let ((name (first x))
(function (second x))
(options (rest (rest x))))
(unless function (error "restart form missing function - ~s" x))
(when (and (null name) (not (getf options :report-function)))
(error "anonymous restart needs a report function - ~s"x))
`(make-restart :name ',name :function ,function ,@options)))
(defun make-restart-case-parts (tagsym argsym cases)
(let ((syms (mapcar #'(lambda (x) (gensym "GO")) cases))
(head nil)
(tail nil))
(mapc #'(lambda (c s)
(push `(,(first c)
#'(lambda (&rest temp)
(setq ,argsym temp)
(go ,s))
,@(transform-restart-case-options c))
head)
(push s tail)
(push `(return-from ,tagsym
(apply #'(lambda ,(second c)
,@(restart-case-case-body c))
,argsym))
tail))
cases
syms)
(cons (nreverse head) (nreverse tail))))
(defun transform-restart-case-options (c)
(let ((opts nil))
(loop
(setf c (rest (rest c)))
(case (first c)
(:report
(push :report-function opts)
(push (if (stringp (second c))
`(function (lambda (s) (format s "~a" ,(second c))))
`(function ,(second c)))
opts))
(:test
(push :test-function opts)
(push `(function ,(second c)) opts))
(:interactive
(push :interactive-function opts)
(push `(function ,(second c)) opts))
(t (return (nreverse opts)))))))
(defun restart-case-case-body (c)
(loop
(setf c (rest (rest c)))
(unless (member (first c) '(:report :test :interactive))
(return c))))
(defun condition-restarts (expr clist)
(if (and (consp expr) (member (first expr) '(error cerror signal warn)))
(mapcar #'second
(butlast *active-restarts*
(- (length *active-restarts*) (length clist))))))
;;;;
;;;; Internal Condition Representation
;;;;
(setf (get 'condition '*struct-slots*) nil)
(setf (get 'condition '*struct-print-function*) 'print-condition)
(defun transform-condition-report-option (x)
(if x
(if (stringp x) x `(function ,x))))
(defun plist-to-alist (plist)
(do ((plist plist (rest (rest plist)))
(alist nil (push (list (first plist) (second plist)) alist)))
((not (consp (rest plist))) (nreverse alist))))
(defun transform-condition-slot-options (spec)
(let* ((name (if (consp spec) (first spec) spec))
(opts (if (consp spec) (plist-to-alist (rest spec)) nil))
(iform (assoc :initform opts)))
(if iform
(let ((form (second iform)))
`(cons
',name
(cons
,(if (constantp form)
`'(:initform ,form)
`(list :initform
(list 'eval
(list (function (lambda () ,form))))))
',(remove-if #'(lambda (x) (eq (first x) :initform)) opts))))
`'(,name ,@opts))))
(defun print-condition (c s d)
(declare (ignore d))
(let ((type (type-of c)))
(if *print-escape*
(format s "#<Condition ~s: ~d>" (type-of c) (address-of c))
(let ((rep (get type '*condition-report*)))
(cond
((null rep) (format s "~s" type))
((stringp rep) (format s "~s" rep))
(t (funcall rep c s)))))))
(defun make-condition-class (name parent report doc slots)
(setf (get name '*struct-print-function*) 'print-condition)
(if parent (setf (get name '*struct-include*) parent))
(setf (get name '*condition-report*)
(if report report (get parent '*condition-report*)))
(if (stringp doc) (setf (documentation name 'type) doc))
(let ((old (if parent (mapcar #'copy-list (get parent '*struct-slots*))))
(new nil))
(dolist (s slots)
(unless (assoc (first s) old) (push (list (first s) nil nil) new)))
(let ((entries (append old (nreverse new))))
(dolist (spec slots)
(let* ((entry (assoc (first spec) entries))
(opts (rest spec))
(i (+ (position entry entries) 1)))
(dolist (opt opts)
(case (first opt)
(:reader
(setf (symbol-function (second opt))
(eval `(function (lambda (x) (%struct-ref x ,i))))))
(:writer
(setf (symbol-function (second opt))
(eval `(function (lambda (x v) (%struct-set x ,i v))))))
(:accessor
(setf (symbol-function (second opt))
(eval `(function (lambda (x) (%struct-ref x ,i)))))
(setf (get (second opt) '*setf*)
(eval `(function (lambda (x v) (%struct-set x ,i v))))))
(:initarg (push (second opt) (second entry)))
(:initform (setf (third entry) (second opt)))
))))
(setf (get name '*struct-slots*) entries)))
name)
(defun initialize-condition (c info args)
(do* ((i 1 (+ i 1))
(info info (rest info))
(vi (first info) (first info)))
((null info))
(let* ((iargs (second vi))
(ival *not-found*)
(iform (third vi)))
(dolist (i iargs)
(setf ival (getf args i *not-found*))
(unless (eq ival *not-found*) (return)))
(if (eq ival *not-found*)
(setf ival (if (constantp iform) iform (eval iform))))
(%struct-set c i ival))))
;;;;
;;;; Helper Functions for HANDLER-BIND and HANDLER-CASE
;;;;
(defun reverse-expand-handler-bind-forms (th)
(let ((forms nil))
(dolist (f th forms)
(push `(list ',(first f) ,(second f)) forms))))
(defun push-condition-handlers (clist active)
(dolist (ch clist)
(push (cons active ch) *active-handlers*)))
(defun handler-case-handler-bind-forms (hforms varsym tagsyms)
(mapcar #'(lambda (ht ts)
`(,(first ht) #'(lambda (temp) (setq ,varsym temp) (go ,ts))))
hforms
tagsyms))
(defun expand-handler-case-bodies (hforms bsym varsym tagsyms)
(apply #'nconc
(mapcar #'(lambda (hf ts)
(let ((v (if (second hf) (first (second hf)) varsym)))
`(,ts
(return-from ,bsym
(let ((,v ,varsym))
,@(rest (rest hf)))))))
hforms
tagsyms)))
;;;;
;;;; Hook Function and Lisp-Level Signaling Functions
;;;;
(defun handle-condition (c)
(if (typep c *break-on-signals*)
(with-simple-restart (continue "Proceed with signalling.")
(format *debug-io* "~&Break on signal: ")
(do-debugger c)))
(dolist (he *active-handlers*)
(let* ((*active-handlers* (first he))
(tspec (second he))
(h (third he)))
(if (typep c tspec) (funcall h c)))))
(defun condition-argument (datum args &optional
(simple-type 'simple-error)
(type 'condition))
(cond
((typep datum type) datum) ;;**** check for no additional args?
((symbolp datum) (apply #'make-condition datum args))
((stringp datum)
(make-condition simple-type :format-string datum :format-arguments args))
(t (error "bad condition arguments - ~s" (cons datum args)))))
(defun base-condition-hook (type *debug-frame* *debug-env* &rest args)
(let ((*condition-hook* 'condition-hook))
(case type
(error (apply #'do-error args))
(cerror (apply #'do-cerror args))
(signal (apply #'do-signal args))
(warn (apply #'do-warn args))
(break (apply #'do-break args))
(debug (apply #'do-debugger args)))))
(defun condition-hook (&rest args)
(let ((*condition-hook* 'condition-hook))
(handler-bind
((unbound-variable #'(lambda (c)
(autoload-variable (cell-error-name c))))
(undefined-function #'(lambda (c)
(autoload-function (cell-error-name c)))))
(apply #'base-condition-hook args))))
(defun do-error (datum &rest args)
(let ((condition (condition-argument datum args)))
(with-condition-restarts condition *condition-restarts*
(setf *condition-restarts* nil)
(handle-condition condition)
(format *debug-io* "~&Error: ")
(do-debugger condition))))
(defun do-cerror (cmsg datum &rest args)
(let ((condition (condition-argument datum args)))
(with-condition-restarts condition *condition-restarts*
(setf *condition-restarts* nil)
(restart-case
(progn
(handle-condition condition)
(format *debug-io* "~&Error: ")
(do-debugger condition))
(continue ()
:report (lambda (s) (apply #'format s cmsg args))
:test (lambda (c) (eq c condition)))))
nil))
(defun do-signal (datum &rest args)
(let ((condition (condition-argument datum args 'simple-condition)))
(with-condition-restarts condition *condition-restarts*
(setf *condition-restarts* nil)
(handle-condition condition))
nil))
(defun do-warn (datum &rest args)
(let ((condition (condition-argument datum args 'simple-warning 'warning)))
(with-condition-restarts condition *condition-restarts*
(setf *condition-restarts* nil)
(restart-case
(progn
(signal condition)
(format *error-output* "~&Warning: ~a~%" condition))
(muffle-warning ()
:report "Muffle warning"
:test (lambda (c) (eq c condition)))))
nil))
(defun do-break (&optional (fmt-string "**BREAK**") &rest fmt-args)
(with-simple-restart (continue "Return from BREAK.")
(format *debug-io* "Break: ")
(do-debugger
(make-condition 'simple-condition
:format-string fmt-string
:format-arguments fmt-args)))
nil)
;;;;
;;;; Debugger Functions
;;;;
(defun do-debugger (condition)
;; should probably check for a condition
(let ((*print-readably* nil))
(when *debugger-hook*
(let* ((hook *debugger-hook*)
(*debugger-hook* nil))
(funcall hook condition hook)))
(let* ((*debug-level* (+ *debug-level* 1))
(current-level *debug-level*)
(*print-level* (if *debug-print-level*
*debug-print-level*
*print-level*))
(*print-length* (if *debug-print-length*
*debug-print-length*
*print-length*)))
;; print the error message
(when condition
(multiple-value-bind (val err)
(ignore-errors
(format *debug-io* "~a~%" condition))
(declare (ignore val))
(when err (format *debug-io* "~s~%" condition))))
;; flush the input buffer and reset the system internals
(reset-system)
;; do the back trace
(if *tracenable* (baktrace (if *tracelimit* *tracelimit* -1)))
;; read-eval-print loop
(let ((*continue-restarts* (compute-restarts condition)))
(loop
(with-simple-restart (abort "Return to break level ~d." current-level)
(when *batch-mode* (format *debug-io* "uncaught error~%") (exit))
;; print restart information (**** optional??)
(format *debug-io* "Break level ~d.~%" current-level)
(format
*debug-io*
"To continue, type (continue n), where n is an option number:~%")
(dotimes (i (length *continue-restarts*))
(multiple-value-bind
(val err)
(ignore-errors
(format *debug-io* "~2d: ~a~%" i (nth i *continue-restarts*)))
(declare (ignore val))
(when err
(ignore-errors
(format *debug-io* "~s~%" (nth i *continue-restarts*))))))
(loop
;; print a prompt
(if (eq *package* (find-package "USER"))
(format *debug-io* "~&~d> " *debug-level*)
(format *debug-io* "~&~A ~d> "
(package-name *package*)
*debug-level*))
;; read and save an input expression
(let ((expr (read *debug-io* nil *eof-mark*)))
(if (eq expr *eof-mark*) (continue 0));;**** is this right??
(setf +++ ++ ++ + + - - expr))
;; evaluate the expression, save and print the results
(let ((vals (multiple-value-list (evalhook - nil nil *debug-env*))))
(setf *** ** ** * * (first vals))
(fresh-line *debug-io*)
(dolist (v vals) (format *debug-io* "~s~%" v))))))))))
(defun debug-fun ()
(if (or (null *debug-frame*) (null (stack-value *debug-frame*)))
nil
(stack-value (+ *debug-frame* 1))))
(defun clean-up (&optional c) (continue c))
(defun baktrace (&optional levels (print-args *baktrace-print-arguments*))
(if *debug-frame*
(do ((fp *debug-frame* (- fp (stack-value fp)))
(n (if levels levels -1) (- n 1)))
((or (= n 0) (null (stack-value fp))))
(let ((p (+ fp 1)))
(format *error-output* "Function: ~s~%" (stack-value p))
(incf p)
(if print-args
(let ((argc (stack-value p)))
(incf p)
(when (> argc 0)
(format *error-output* "Arguments:~%")
(dotimes (i argc)
(format *error-output* " ~s~%"
(stack-value (+ p i))))))))))
(values))
(defun show-bindings (&optional vars)
(dolist (a (first *debug-env*))
(if (consp a)
(dolist (b a)
(if (consp b)
(let ((s (car b))
(v (cdr b)))
(if (and (symbolp s)
(or (null vars)
(eq s vars)
(and (consp vars) (member s vars))))
(format *error-output* "~s~15t~s~%" s v)))))))
(values))
(defmacro get-value (form) `(evalhook ,form nil nil *debug-env*))
(defmacro set-value (form val) `(evalhook (setf ,form ,val) *debug-env*))
;;;;
;;;; Public Interface
;;;;
(defun prompt-for (type fmt-string &rest fmt-args)
(loop
(apply #'format *debug-io* fmt-string fmt-args)
(let ((val (eval (read *debug-io*))))
(if (typep val type) (return val))
(format *debug-io* "~s is not of type ~s.~%" val type))))
;;**** simple version -- use this in define-cmp-macro
(defun type-check (x spec)
(unless (typep x spec) (error "~s is not of type ~s." x spec))
nil)
#|
(defmacro check-type (place spec &optional string)
`(type-check ,place ',spec))
|#
;; version of check-type that returns the final value of the place form
(defmacro base-check-type (place spec &optional string)
(let ((valsym (gensym "VAL")))
`(loop
(let ((,valsym ,place))
(if (typep ,valsym ',spec)
(return ,valsym)
(restart-case
(error 'check-type-error
:datum ,valsym
:expected-type ',spec
:form ',place
:type-string ,string)
(store-value (,valsym)
:report "Store new value."
:interactive (lambda ()
(list
(prompt-for ',spec "Value for ~s: " ',place)))
(setf ,place ,valsym))))))))
(defmacro check-type (place spec &optional string)
`(progn (base-check-type ,place ,spec ,string)
nil))
;;**** simple versions
(defmacro assert (testform &optional places datum &rest args)
(if datum
`(unless ,testform (error ,datum ,@args))
`(unless ,testform
(error "The assertion ~S failed" ',testform))))
(defmacro etypecase (var &rest forms)
(let ((vsym (gensym "VAR")))
`(let ((,vsym ,var))
(typecase ,vsym
,@forms
(t (error 'type-error
:datum ,vsym
:expected-type '(or ,@(mapcar #'first forms))))))))
(defmacro ctypecase (var &rest body)
`(typecase (base-check-type ,var (or ,@(mapcar #'first body)))
,@body))
(defun compute-case-match-type (cases)
(let ((keys nil))
(dolist (b cases (cons 'member (nreverse keys)))
(if (consp (first b))
(dolist (k (first b)) (push k keys)))
(push (first b) keys))))
(defmacro ecase (var &rest forms)
(let ((vsym (gensym "VAR")))
`(let ((,vsym ,var))
(case ,vsym
,@forms
(t (error 'type-error
:datum ,vsym
:expected-type (compute-case-match-type ',forms)))))))
(defmacro ccase (var &rest body)
`(case (base-check-type ,var ,(compute-case-match-type body))
,@body))
(defmacro handler-case (expr &rest hforms)
(if (eq (first (first (last hforms))) :no-error)
(let ((errsym (gensym "ERROR"))
(normsym (gensym "NORMAL"))
(ne-form (first (last hforms)))
(e-forms (butlast hforms)))
`(block ,errsym
(multiple-value-call #'(lambda ,@(rest ne-form))
(block ,normsym
(return-from ,errsym
(handler-case (return-from ,normsym ,expr)
,@e-forms))))))
(let ((bsym (gensym "BLOCK"))
(varsym (gensym "VAR"))
(tagsyms (mapcar #'(lambda (x) (gensym "TAG")) hforms)))
`(block ,bsym
(let (,varsym)
(tagbody
(handler-bind
,(handler-case-handler-bind-forms hforms varsym tagsyms)
(return-from ,bsym ,expr))
,@(expand-handler-case-bodies hforms bsym varsym tagsyms)))))))
(defmacro ignore-errors (&rest forms)
`(handler-case (progn ,@forms)
(error (c) (values nil c))))
(defmacro handler-bind (th &rest body)
(let ((valsym (gensym "VALUE")))
`(let ((*active-handlers* *active-handlers*))
(push-condition-handlers (list ,@(reverse-expand-handler-bind-forms th))
*active-handlers*)
;; this errset traps internal xlabort's (from stack overflows)
;; and converts them to calls to (abort)
(let ((,valsym (errset (multiple-value-list (progn ,@body)) nil)))
(if ,valsym
(values-list (first ,valsym))
(error "stack overflow"))))))
#|
(defmacro handler-bind (th &rest body)
`(let ((*active-handlers* *active-handlers*))
(push-condition-handlers (list ,@(reverse-expand-handler-bind-forms th))
*active-handlers*)
,@body))
|#
(defmacro handler-bind (th &rest body)
(let ((valsym (gensym "VALUE")))
`(let ((*active-handlers* *active-handlers*))
(push-condition-handlers (list ,@(reverse-expand-handler-bind-forms th))
*active-handlers*)
;; this errset traps internal xlabort's (from stack overflows)
;; and converts them to calls to (abort)
(let ((,valsym (errset (multiple-value-list (progn ,@body)))))
(if ,valsym
(values-list (first ,valsym))
(error "stack overflow"))))))
(defmacro define-condition (name plist &optional slots &rest options)
(if (< 1 (length plist)) (error "multiple inheritance not supported"))
(if (null plist) (error "new conditions must inherit from an existing one"))
`(make-condition-class ',name
',(first plist)
,(transform-condition-report-option
(second (assoc :report options)))
',(second (assoc :documentation options))
(list ,@(mapcar #'transform-condition-slot-options
slots))))
(defun make-condition (type &rest args)
(when (eq (get type '*struct-slots* *not-found*) *not-found*)
(error "bad condition type - ~s" type))
(let* ((info (get type '*struct-slots*))
(c (apply #'%make-struct type (make-list (length info)))))
(initialize-condition c info args)
c))
(defmacro with-simple-restart (rfa &rest forms)
(let ((restart-name (first rfa))
(format-string (second rfa))
(format-args (rest (rest rfa))))
`(restart-case (progn ,@forms)
(,restart-name ()
:report (lambda (stream) (format stream ,format-string ,@format-args))
(values nil t)))))
(defmacro restart-case (expr &rest cases)
(let* ((tagsym (gensym "TAG"))
(argsym (gensym "ARGS"))
(valsym (gensym "VALS"))
(parts (make-restart-case-parts tagsym argsym cases)))
`(block ,tagsym
(let ((,argsym nil))
(tagbody
(restart-bind
,(first parts)
(let ((*condition-restarts* (condition-restarts ',expr ',cases)))
(return-from ,tagsym ,expr)))
,@(rest parts))))))
#|
(defmacro restart-bind (bds &rest body)
(let ((valsym (gensym "VALUE")))
`(let ((*active-restarts* *active-restarts*))
(push-restarts nil (list ,@(mapcar #'expand-restart-binding-form bds)))
;; this errset traps internal xlabort's (from stack overflows)
;; and converts them to calls to (abort)
(let ((,valsym (errset (multiple-value-list (progn ,@body)))))
(if ,valsym (values-list (first ,valsym)) (abort))))))
|#
(defmacro restart-bind (bds &rest body)
`(let ((*active-restarts* *active-restarts*))
(push-restarts nil (list ,@(mapcar #'expand-restart-binding-form bds)))
,@body))
(defmacro with-condition-restarts (condition rlist &rest forms)
`(let ((*active-restarts* *active-restarts*))
(push-restarts ,condition ,rlist)
,@forms))
(defun compute-restarts (&optional condition)
(let ((result nil))
(dolist (cr *active-restarts*)
(if (restart-entry-applicable-p cr condition)
(push (second cr) result)))
(nreverse (delete-duplicates result))))
;restart-name
(defun restart-entry-applicable-p (cr condition)
(let ((c (first cr))
(r (second cr)))
(if c
(eq c condition)
(or (null condition)
(funcall (restart-test-function r) condition)))))
(defun find-restart (identifier &optional condition)
(cond
((null identifier) nil)
((symbolp identifier)
(second
(find-if #'(lambda (x)
(and (restart-entry-applicable-p x condition)
(eq identifier (restart-name (second x)))))
*active-restarts*)))
((restart-p identifier)
(second (find identifier *active-restarts* :key #'second)))))
(defun invoke-restart (identifier &rest args)
(let ((restart (find-restart identifier)))
(if restart
(apply (restart-function restart) args)
(error "invalid restart - ~s" identifier))))
(defun invoke-restart-interactively (identifier)
(let* ((restart (find-restart identifier))
(ifun (restart-interactive-function restart))
(rfun (restart-function restart)))
(if restart
(if ifun (apply rfun (funcall ifun)) (funcall rfun))
(error "invalid restart - ~s" identifier))))
(defun abort (&optional condition)
(invoke-restart (find-restart 'abort condition)))
(defun continue (&optional condition)
(if (integerp condition)
(let ((restart (nth condition *continue-restarts*)))
(if restart (invoke-restart-interactively restart)))
(let ((restart (find-restart 'continue condition)))
(if restart (invoke-restart restart)))))
(defun muffle-warning (&optional condition)
(invoke-restart (find-restart 'muffle-warning condition)))
(defun store-value (value &optional condition)
(let ((restart (find-restart 'store-value condition)))
(if restart (invoke-restart restart value))))
(defun use-value (value &optional condition)
(let ((restart (find-restart 'use-value condition)))
(if restart (invoke-restart restart value))))
;;;;
;;;; Condition Types
;;;;
(defun print-simple-condition (c s)
(if *print-escape*
(print-condition c s nil)
(apply #'format
s
(simple-condition-format-string c)
(simple-condition-format-arguments c))))
(define-condition simple-condition (condition)
((format-string :accessor simple-condition-format-string
:initform "Simple condition."
:initarg :format-string)
(format-arguments :accessor simple-condition-format-arguments
:initarg :format-arguments))
(:report print-simple-condition))
(define-condition serious-condition (condition))
(define-condition error (serious-condition))
(define-condition simple-error (error)
((format-string :initform "Simple error." :initarg :format-string)
(format-arguments :initarg :format-arguments))
(:report print-simple-condition))
(define-condition warning (condition))
(define-condition simple-warning (warning)
((format-string :initform "Simple warning." :initarg :format-string)
(format-arguments :initarg :format-arguments))
(:report print-simple-condition))
(define-condition storage-condition (condition))
(define-condition cell-error (error)
((name :accessor cell-error-name :initarg :name))
(:report "Cell error"))
(define-condition unbound-variable (cell-error)
()
(:report
(lambda (c s)
(format s "The variable ~s is unbound." (cell-error-name c)))))
(define-condition undefined-function (cell-error)
()
(:report
(lambda (c s)
(format s "The function ~s is not defined." (cell-error-name c)))))
(define-condition type-error (error)
(format-string
format-arguments
(datum :initarg :datum :accessor type-error-datum)
(expected-type :initarg :expected-type
:accessor type-error-expected-type))
(:report
(lambda (c s)
(format s "~s is not of type ~s."
(type-error-datum c)
(type-error-expected-type c)))))
(define-condition check-type-error (type-error)
((form :initarg :form :accessor check-type-error-form)
(type-string :initarg :type-string :accessor check-type-error-type-string))
(:report
(lambda (c s)
(if (check-type-error-type-string c)
(format s "The value of ~s, ~s, is not ~a."
(check-type-error-form c)
(type-error-datum c)
(check-type-error-type-string c))
(format s "The value of ~s, ~s, is not of type ~s."
(check-type-error-form c)
(type-error-datum c)
(type-error-expected-type c))))))
(define-condition simple-type-error (type-error))
#|
arithmetic-error
division-by-zero
control-error
file-error
package-error
program-error
stream-error
end-of-file
package-error-package
stream-error-stream
file-error-pathname
arithmetic-error-operation
arithmetic-error-operands
|#
syntax highlighted by Code2HTML, v. 0.9.1