Advertisement
Guest User

Implementation of Common Lisp Condition System in CL

a guest
Jul 8th, 2011
353
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 9.17 KB | None | 0 0
  1. (in-package #:cl-user)
  2.  
  3. (defpackage #:clcs
  4.   (:use #:cl)
  5.   (:shadow    
  6.     #:condition
  7.     #:simple-condition
  8.     #:simple-condition-format-control
  9.     #:simple-condition-format-arguments
  10.     #:error
  11.     #:simple-error
  12.     #:warning
  13.     #:simple-warning
  14.     #:restart
  15.     #:restart-name
  16.     #:signal
  17.     #:warn
  18.     #:invoke-debugger
  19.     #:muffle-warning
  20.     #:abort
  21.     #:find-restart
  22.     #:invoke-restart
  23.     #:compute-restarts
  24.     #:define-condition
  25.     #:make-condition    
  26.     #:handler-bind
  27.     #:handler-case
  28.     #:restart-bind
  29.     #:restart-case)
  30.   (:export
  31.     #:condition
  32.     #:simple-condition
  33.     #:simple-condition-format-control
  34.     #:simple-condition-format-arguments
  35.     #:error
  36.     #:simple-error
  37.     #:warning
  38.     #:simple-warning
  39.     #:restart
  40.     #:restart-name
  41.     #:signal
  42.     #:warn
  43.     #:invoke-debugger
  44.     #:muffle-warning
  45.     #:abort
  46.     #:find-restart    
  47.     #:invoke-restart
  48.     #:compute-restarts
  49.     #:define-condition
  50.     #:make-condition    
  51.     #:handler-bind
  52.     #:handler-case
  53.     #:restart-bind
  54.     #:restart-case))
  55.  
  56. (in-package #:clcs)
  57.  
  58. ;;;;==========================================
  59. ;;;;                Conditions
  60. ;;;;==========================================
  61.  
  62. (defclass condition ()
  63.   ((report-function :initform #'default-report-function)))
  64.  
  65. (defmethod print-object ((object condition) stream)
  66.   (if *print-escape*
  67.     (print-unreadable-object (object stream :type t :identity t))
  68.     (funcall (slot-value object 'report-function) object stream))
  69.   object)
  70.  
  71. (defun default-report-function (c s)
  72.   (format s "Condition of type ~s signalled." c))
  73.  
  74. (defmacro define-condition (name (&rest superclasses)
  75.                                  (&rest slots)
  76.                                  &rest options)
  77.   `(defclass ,name (,@superclasses condition)
  78.      ,(append (let ((report (find :report options :key #'car)))
  79.                 (if report
  80.                   `((report-function
  81.                       :initform ,(make-report-lambda report)))
  82.                   '()))
  83.               slots)
  84.      ,@(remove :report options :key #'car)))
  85.  
  86. (defun make-report-lambda (report-form)
  87.   (typecase (second report-form)
  88.     (string `(lambda (c s)
  89.                (declare (ignore c))
  90.                (write-string ,(second report-form) s)))
  91.     (symbol `(function ,(second report-form)))
  92.     ((cons (eql lambda) (cons list list)) (second report-form))
  93.     (T (error "Invalid :report argument: ~s" report-form))))
  94.  
  95. (defclass simple-condition (condition)
  96.   ((report-function :initform #'report-simple-condition)
  97.    (format-control :initform nil :initarg :format-control
  98.                    :accessor simple-condition-format-control)
  99.    (format-arguments :initform '() :initarg :format-arguments
  100.                      :accessor simple-condition-format-arguments)))
  101.  
  102. (defun report-simple-condition (c s)
  103.   (apply #'format s
  104.          (or (slot-value c 'format-control)
  105.              (error "No format control for ~s" c))
  106.          (slot-value c 'format-arguments)))
  107.  
  108. (defclass error (condition)
  109.   ())
  110.  
  111. (defclass simple-error (error simple-condition)
  112.   ())
  113.  
  114. (defclass warning (condition)
  115.   ())
  116.  
  117. (defclass simple-warning (warning simple-condition)
  118.   ())
  119.  
  120. (defvar *active-handlers* '())
  121.  
  122. (defmacro handler-bind ((&rest bindings) &body body)
  123.   `(let* ,(mapcar (lambda (binding)
  124.                     (destructuring-bind
  125.                         (type handler) binding
  126.                       `(*active-handlers* (cons (cons ',type ,handler)
  127.                                                 *active-handlers*))))
  128.             (reverse bindings))
  129.      ,@body))
  130.  
  131. (defmacro handler-case (body &rest cases)
  132.   (let ((block (gensym)))    
  133.     `(block ,block
  134.        (handler-bind
  135.          ,(mapcar (lambda (case)
  136.                     (destructuring-bind
  137.                         (type (&optional (var (gensym) var-p)) &body body) case
  138.                       `(,type (lambda (,var)
  139.                                 ,(unless var-p `(declare (ignore ,var)))
  140.                                 (return-from ,block (locally ,@body))))))
  141.             cases)
  142.          ,body))))
  143.  
  144. ;;;;==========================================
  145. ;;;;                Restarts
  146. ;;;;==========================================
  147.  
  148. (defstruct restart
  149.   (name nil :type symbol :read-only t)
  150.   (function (constantly nil) :type function :read-only t))
  151.  
  152. (defmethod print-object ((object restart) stream)
  153.   (print-unreadable-object (object stream :type t :identity t)
  154.     (format stream "~s" (restart-name object)))
  155.   object)
  156.  
  157. (defvar *active-restarts* '())
  158.  
  159. (defun compute-restarts ()
  160.   (copy-list *active-restarts*))
  161.  
  162. (defun find-restart (name)
  163.   (unless (symbolp name)
  164.     (error "~a is not a symbol." name))
  165.   (find name *active-restarts* :key #'restart-name))
  166.  
  167. (defun invoke-restart (restart &rest args)
  168.   (typecase restart
  169.     (symbol (let ((r (find restart *active-restarts* :key #'restart-name)))
  170.               (if r
  171.                 (apply (restart-function r) args)
  172.                 (error "No restart named ~s is active" restart))))
  173.     (restart (if (find restart *active-restarts*)
  174.                (apply (restart-function restart) args)
  175.                (error "Restart ~s is inactive" restart)))
  176.     (t (error "~s is not a restart designator." restart))))
  177.  
  178. (defmacro restart-bind ((&rest restarts) &body body)
  179.   `(let* ,(mapcar (lambda (binding)
  180.                     (destructuring-bind
  181.                         (name function) binding
  182.                       `(*active-restarts* (cons (make-restart
  183.                                                   :name ',name
  184.                                                   :function ,function)
  185.                                                 *active-restarts*))))
  186.             (reverse restarts))
  187.      ,@body))
  188.  
  189. (defmacro restart-case (body &rest cases)
  190.   (let ((block (gensym)))
  191.     `(block ,block
  192.        (restart-bind
  193.          ,(mapcar (lambda (case)
  194.                     (destructuring-bind
  195.                         (name (&rest args) &body body) case
  196.                       `(,name (lambda ,args (return-from ,block (locally ,@body))))))
  197.             cases)
  198.          ,body))))
  199.  
  200. (defun make-condition (type &rest args)
  201.   (let ((class (typecase type
  202.                  (symbol (if (subtypep type 'condition)
  203.                            (find-class type)
  204.                            (error "~s is not a condition class" type)))
  205.                  (class (if (subtypep (class-name type) 'condition)
  206.                           type
  207.                           (error "~s is not a condition class" (class-name type))))
  208.                  (T (error "Bad type argument: ~s" type)))))
  209.     (apply #'make-instance class args)))
  210.  
  211. (defun signal (datum &rest args)
  212.   (let ((condition (if (stringp datum)
  213.                      (make-instance 'simple-condition
  214.                        :format-control datum
  215.                        :format-arguments args)
  216.                      (apply #'make-condition datum args))))
  217.     (dolist (binding *active-handlers*)
  218.       (when (typep condition (car binding))
  219.         (funcall (cdr binding) condition)))))
  220.  
  221. (defun warn (datum &rest args)
  222.   (let ((condition (if (stringp datum)
  223.                      (make-instance 'simple-warning
  224.                        :format-control datum
  225.                        :format-arguments args)
  226.                      (apply #'make-condition datum args))))
  227.     (unless (typep condition 'warning)
  228.       (error "~a is not a warning" condition))
  229.     (restart-case
  230.       (dolist (binding *active-handlers*
  231.                        (format *error-output* "WARNING: ~a" condition))
  232.         (when (typep condition (car binding))
  233.           (funcall (cdr binding) condition)))
  234.       (muffle-warning () (return-from warn nil)))))
  235.  
  236. (defun muffle-warning (&optional condition)
  237.   (declare (ignore condition))
  238.   (invoke-restart 'muffle-warning))
  239.  
  240. (defun error (datum &rest args)
  241.   (let ((condition (if (stringp datum)
  242.                      (make-instance 'simple-error
  243.                        :format-control datum
  244.                        :format-arguments args)
  245.                      (apply #'make-condition datum args))))
  246.     (dolist (binding *active-handlers*
  247.                      (invoke-debugger condition))
  248.       (when (typep condition (car binding))
  249.         (funcall (cdr binding) condition)))))
  250.  
  251. ;;;;====================================
  252. ;;;;          Toplevel & debugger
  253. ;;;;====================================
  254.  
  255. (defvar *debug-level* 0)
  256.  
  257. (defmacro within-toplevel (&body body)
  258.   `(restart-case
  259.      (progn ,@body)
  260.      (abort () (values))))
  261.  
  262. (defun invoke-debugger (condition)
  263.   (let ((*debug-level* (1+ *debug-level*)))
  264.     (within-toplevel
  265.       (unless (typep condition 'condition)
  266.         (error "~s is not a condition object" condition))
  267.       (format *error-output* "~&Debugger invoked on ~s: ~_ ~a~%~%"
  268.               (class-name (class-of condition)) condition)
  269.       (loop (format *error-output* "~&DEBUG ~a " (make-string *debug-level*
  270.                                                    :initial-element #\>))
  271.             (force-output *error-output*)
  272.             (print (eval (read)) *error-output*)))))
  273.  
  274. (defun abort (&optional condition)
  275.   (declare (ignore condition))
  276.   (invoke-restart 'abort))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement