1 ;;;; miscellaneous side-effectful tests of the MOP
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 ;;; Pascal Costanza's implementation of beta methods, lightly
15 ;;; modified. Contains a specialization of MAKE-METHOD-LAMBDA.
22 (defclass beta-generic-function (standard-generic-function)
24 (:metaclass funcallable-standard-class))
26 (defclass beta-method (standard-method)
27 ((betap :reader betap :initarg :betap :initform nil)))
29 (defmethod initialize-instance :around
30 ((method beta-method) &rest initargs &key qualifiers)
31 (declare (dynamic-extent initargs))
32 (if (equal qualifiers '(:beta))
33 (apply #'call-next-method method
39 (defun collect-runs (methods)
40 (let ((complete-runs nil)
42 (flet ((complete-run ()
44 (push (nreverse current-run) complete-runs)
45 (setf current-run nil))))
46 (loop for method in methods with seen-beta = nil do
50 (setq seen-beta t current-run nil)))
51 (push method current-run))
55 (define-method-combination beta ()
58 (primary () :required t)
60 (flet ((call-methods (methods)
61 (mapcar (lambda (method) `(call-method ,method)) methods)))
62 (let ((form (if (or before after (rest primary))
63 (let ((runs (collect-runs primary)))
64 `(multiple-value-prog1
66 ,@(call-methods before)
67 (call-method ,(first (first runs))
70 ,@(call-methods (reverse after))))
71 `(call-method ,(first primary)))))
73 `(call-method ,(first around) (,@(rest around) (make-method ,form)))
76 (defmethod make-method-lambda
77 ((gf beta-generic-function) method-prototype lambda-expression environment)
78 (declare (ignore method-prototype environment))
79 (let ((method-args (gensym))
80 (next-methods (gensym))
81 (inner-runs (gensym)))
82 `(lambda (,method-args &optional ,next-methods ,inner-runs)
83 (declare (ignorable ,next-methods ,inner-runs))
84 (flet ((call-next-method (&rest args)
85 (declare (dynamic-extent args))
86 (if (null ,next-methods)
87 (error "There is no next method for ~S." ,gf)
88 (funcall (method-function (car ,next-methods))
89 (if args args ,method-args)
92 (next-method-p () (not (null ,next-methods)))
93 (call-inner-method (&rest args)
94 (declare (dynamic-extent args))
95 (if (null ,inner-runs)
96 (error "There is no inner method for ~S." ,gf)
97 (funcall (method-function (caar ,inner-runs))
98 (if args args ,method-args)
101 (inner-method-p () (not (null ,inner-runs))))
102 (declare (ignorable #'call-next-method #'next-method-p
103 #'call-inner-method #'inner-method-p))
104 (apply ,lambda-expression ,method-args)))))
106 (defmacro define-beta-function (name (&rest args) &rest options)
107 `(defgeneric ,name ,args
108 ,@(unless (member :generic-function-class options :key #'car)
109 '((:generic-function-class beta-generic-function)))
110 ,@(unless (member :method-class options :key #'car)
111 '((:method-class beta-method)))
112 ,@(unless (member :method-combination options :key #'car)
113 '((:method-combination beta)))
117 (defclass middle (top) ())
118 (defclass bottom (middle) ())
120 (define-beta-function test (object))
122 ;;; MAKE-METHOD-LAMBDA acts at (DEFMETHOD-)expand-time, which is
123 ;;; before DEFCLASS- and DEFGENERIC-load-time.
126 '(defmethod test ((object top)) 'top)
127 '(defmethod test :beta ((object middle))
128 (list 'middle (call-inner-method) (call-next-method)))
129 '(defmethod test :beta ((object bottom)) 'bottom)))
131 (assert (equal '(middle bottom top) (test (make-instance 'bottom))))
132 (assert (equal 'top (test (make-instance 'top))))
133 (assert (null (ignore-errors (test (make-instance 'middle)))))