0.6.10.21:
[sbcl.git] / src / pcl / macros.lisp
1 ;;;; macros, global variable definitions, and other miscellaneous support stuff
2 ;;;; used by the rest of the PCL subsystem
3
4 ;;;; This software is part of the SBCL system. See the README file for
5 ;;;; more information.
6
7 ;;;; This software is derived from software originally released by Xerox
8 ;;;; Corporation. Copyright and release statements follow. Later modifications
9 ;;;; to the software are in the public domain and are provided with
10 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
11 ;;;; information.
12
13 ;;;; copyright information from original PCL sources:
14 ;;;;
15 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
16 ;;;; All rights reserved.
17 ;;;;
18 ;;;; Use and copying of this software and preparation of derivative works based
19 ;;;; upon this software are permitted. Any distribution of this software or
20 ;;;; derivative works must comply with all applicable United States export
21 ;;;; control laws.
22 ;;;;
23 ;;;; This software is made available AS IS, and Xerox Corporation makes no
24 ;;;; warranty about the software, its performance or its conformity to any
25 ;;;; specification.
26 \f
27 (in-package "SB-PCL")
28
29 (declaim (declaration
30           ;; These three nonstandard declarations seem to be used
31           ;; privately within PCL itself to pass information around,
32           ;; so we can't just delete them.
33           %class
34           %method-name
35           %method-lambda-list
36           ;; This declaration may also be used within PCL to pass
37           ;; information around, I'm not sure. -- WHN 2000-12-30
38           %variable-rebinding))
39
40 ;;; FIXME: CONSTANTLY-FOO should be boosted up to SB-INT too.
41 (macrolet ((def-constantly-fun (name constant-expr)
42              `(setf (symbol-function ',name)
43                     (constantly ,constant-expr))))
44   (def-constantly-fun constantly-t t)
45   (def-constantly-fun constantly-nil nil)
46   (def-constantly-fun constantly-0 0))
47
48 ;;; FIXME: This looks like SBCL's PARSE-BODY, and should be shared.
49 (eval-when (:compile-toplevel :load-toplevel :execute)
50 (defun extract-declarations (body &optional environment)
51   ;;(declare (values documentation declarations body))
52   (let (documentation
53         declarations
54         form)
55     (when (and (stringp (car body))
56                (cdr body))
57       (setq documentation (pop body)))
58     (block outer
59       (loop
60         (when (null body) (return-from outer nil))
61         (setq form (car body))
62         (when (block inner
63                 (loop (cond ((not (listp form))
64                              (return-from outer nil))
65                             ((eq (car form) 'declare)
66                              (return-from inner t))
67                             (t
68                              (multiple-value-bind (newform macrop)
69                                   (macroexpand-1 form environment)
70                                (if (or (not (eq newform form)) macrop)
71                                    (setq form newform)
72                                  (return-from outer nil)))))))
73           (pop body)
74           (dolist (declaration (cdr form))
75             (push declaration declarations)))))
76     (values documentation
77             (and declarations `((declare ,.(nreverse declarations))))
78             body)))
79 ) ; EVAL-WHEN
80
81 (defun get-declaration (name declarations &optional default)
82   (dolist (d declarations default)
83     (dolist (form (cdr d))
84       (when (and (consp form) (eq (car form) name))
85         (return-from get-declaration (cdr form))))))
86
87 (defmacro collecting-once (&key initial-value)
88    `(let* ((head ,initial-value)
89            (tail ,(and initial-value `(last head))))
90           (values #'(lambda (value)
91                            (if (null head)
92                                (setq head (setq tail (list value)))
93                                (unless (memq value head)
94                                  (setq tail
95                                        (cdr (rplacd tail (list value)))))))
96                   #'(lambda nil head))))
97
98 (defmacro doplist ((key val) plist &body body &environment env)
99   (multiple-value-bind (doc decls bod)
100       (extract-declarations body env)
101     (declare (ignore doc))
102     `(let ((.plist-tail. ,plist) ,key ,val)
103        ,@decls
104        (loop (when (null .plist-tail.) (return nil))
105              (setq ,key (pop .plist-tail.))
106              (when (null .plist-tail.)
107                (error "malformed plist, odd number of elements"))
108              (setq ,val (pop .plist-tail.))
109              (progn ,@bod)))))
110
111 (defmacro dolist-carefully ((var list improper-list-handler) &body body)
112   `(let ((,var nil)
113          (.dolist-carefully. ,list))
114      (loop (when (null .dolist-carefully.) (return nil))
115            (if (consp .dolist-carefully.)
116                (progn
117                  (setq ,var (pop .dolist-carefully.))
118                  ,@body)
119                (,improper-list-handler)))))
120 \f
121 ;;;; FIND-CLASS
122 ;;;;
123 ;;;; This is documented in the CLOS specification. FIXME: Except that
124 ;;;; SBCL deviates from the spec by having CL:FIND-CLASS distinct from
125 ;;;; PCL:FIND-CLASS, alas.
126
127 (defvar *find-class* (make-hash-table :test 'eq))
128
129 (defmacro find-class-cell-class (cell)
130   `(car ,cell))
131
132 (defmacro find-class-cell-predicate (cell)
133   `(cadr ,cell))
134
135 (defmacro find-class-cell-make-instance-function-keys (cell)
136   `(cddr ,cell))
137
138 (defmacro make-find-class-cell (class-name)
139   (declare (ignore class-name))
140   '(list* nil #'constantly-nil nil))
141
142 (defun find-class-cell (symbol &optional dont-create-p)
143   (or (gethash symbol *find-class*)
144       (unless dont-create-p
145         (unless (legal-class-name-p symbol)
146           (error "~S is not a legal class name." symbol))
147         (setf (gethash symbol *find-class*) (make-find-class-cell symbol)))))
148
149 (defvar *create-classes-from-internal-structure-definitions-p* t)
150
151 (defun find-class-from-cell (symbol cell &optional (errorp t))
152   (or (find-class-cell-class cell)
153       (and *create-classes-from-internal-structure-definitions-p*
154            (structure-type-p symbol)
155            (find-structure-class symbol))
156       (cond ((null errorp) nil)
157             ((legal-class-name-p symbol)
158              (error "There is no class named ~S." symbol))
159             (t
160              (error "~S is not a legal class name." symbol)))))
161
162 (defun find-class-predicate-from-cell (symbol cell &optional (errorp t))
163   (unless (find-class-cell-class cell)
164     (find-class-from-cell symbol cell errorp))
165   (find-class-cell-predicate cell))
166
167 (defun legal-class-name-p (x)
168   (and (symbolp x)
169        (not (keywordp x))))
170
171 (defun find-class (symbol &optional (errorp t) environment)
172   (declare (ignore environment))
173   (find-class-from-cell symbol
174                         (find-class-cell symbol errorp)
175                         errorp))
176
177 (defun find-class-predicate (symbol &optional (errorp t) environment)
178   (declare (ignore environment))
179   (find-class-predicate-from-cell symbol
180                                   (find-class-cell symbol errorp)
181                                   errorp))
182 \f
183 ;;; This DEFVAR was originally in defs.lisp, now moved here.
184 ;;;
185 ;;; Possible values are NIL, EARLY, BRAID, or COMPLETE.
186 ;;;
187 ;;; KLUDGE: This should probably become
188 ;;;   (DECLAIM (TYPE (MEMBER NIL :EARLY :BRAID :COMPLETE) *BOOT-STATE*))
189 (defvar *boot-state* nil)
190
191 ;;; Note that in SBCL as in CMU CL,
192 ;;;   COMMON-LISP:FIND-CLASS /= SB-PCL:FIND-CLASS.
193 ;;; (Yes, this is a KLUDGE!)
194 (define-compiler-macro find-class (&whole form
195                                    symbol &optional (errorp t) environment)
196   (declare (ignore environment))
197   (if (and (constantp symbol)
198            (legal-class-name-p (eval symbol))
199            (constantp errorp)
200            (member *boot-state* '(braid complete)))
201       (let ((symbol (eval symbol))
202             (errorp (not (null (eval errorp))))
203             (class-cell (make-symbol "CLASS-CELL")))    
204         `(let ((,class-cell (load-time-value (find-class-cell ',symbol))))
205            (or (find-class-cell-class ,class-cell)
206                ,(if errorp
207                     `(find-class-from-cell ',symbol ,class-cell t)
208                     `(and (sb-kernel:class-cell-class
209                            ',(sb-kernel:find-class-cell symbol))
210                           (find-class-from-cell ',symbol ,class-cell nil))))))
211       form))
212
213 (defun (setf find-class) (new-value symbol)
214   (if (legal-class-name-p symbol)
215       (let ((cell (find-class-cell symbol)))
216         (setf (find-class-cell-class cell) new-value)
217         (when (or (eq *boot-state* 'complete)
218                   (eq *boot-state* 'braid))
219           (when (and new-value (class-wrapper new-value))
220             (setf (find-class-cell-predicate cell)
221                   (fdefinition (class-predicate-name new-value))))
222           (when (and new-value (not (forward-referenced-class-p new-value)))
223
224             (dolist (keys+aok (find-class-cell-make-instance-function-keys
225                                cell))
226               (update-initialize-info-internal
227                (initialize-info new-value (car keys+aok) nil (cdr keys+aok))
228                'make-instance-function))))
229         new-value)
230       (error "~S is not a legal class name." symbol)))
231
232 (defun (setf find-class-predicate)
233        (new-value symbol)
234   (if (legal-class-name-p symbol)
235     (setf (find-class-cell-predicate (find-class-cell symbol)) new-value)
236     (error "~S is not a legal class name." symbol)))
237
238 (defun find-wrapper (symbol)
239   (class-wrapper (find-class symbol)))
240
241 (defmacro gathering1 (gatherer &body body)
242   `(gathering ((.gathering1. ,gatherer))
243      (macrolet ((gather1 (x) `(gather ,x .gathering1.)))
244        ,@body)))
245
246 (defmacro vectorizing (&key (size 0))
247   `(let* ((limit ,size)
248           (result (make-array limit))
249           (index 0))
250      (values #'(lambda (value)
251                  (if (= index limit)
252                      (error "vectorizing more elements than promised")
253                      (progn
254                        (setf (svref result index) value)
255                        (incf index)
256                        value)))
257              #'(lambda () result))))
258
259 ;;; These are augmented definitions of LIST-ELEMENTS and LIST-TAILS from
260 ;;; iterate.lisp. These versions provide the extra :BY keyword which can
261 ;;; be used to specify the step function through the list.
262 (defmacro *list-elements (list &key (by #'cdr))
263   `(let ((tail ,list))
264      #'(lambda (finish)
265          (if (endp tail)
266              (funcall finish)
267              (prog1 (car tail)
268                     (setq tail (funcall ,by tail)))))))
269
270 (defmacro *list-tails (list &key (by #'cdr))
271    `(let ((tail ,list))
272       #'(lambda (finish)
273           (prog1 (if (endp tail)
274                      (funcall finish)
275                      tail)
276                  (setq tail (funcall ,by tail))))))
277
278 (defmacro function-funcall (form &rest args)
279   `(funcall (the function ,form) ,@args))
280
281 (defmacro function-apply (form &rest args)
282   `(apply (the function ,form) ,@args))
283 \f
284
285 (defun get-setf-function-name (name)
286   `(setf ,name))
287
288 (defsetf slot-value set-slot-value)