1 | ;;; defclass.lisp |
---|
2 | ;;; |
---|
3 | ;;; Copyright (C) 2003 Peter Graves |
---|
4 | ;;; $Id: defclass.lisp,v 1.22 2003-10-13 14:12:26 piso Exp $ |
---|
5 | ;;; |
---|
6 | ;;; This program is free software; you can redistribute it and/or |
---|
7 | ;;; modify it under the terms of the GNU General Public License |
---|
8 | ;;; as published by the Free Software Foundation; either version 2 |
---|
9 | ;;; of the License, or (at your option) any later version. |
---|
10 | ;;; |
---|
11 | ;;; This program is distributed in the hope that it will be useful, |
---|
12 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | ;;; GNU General Public License for more details. |
---|
15 | ;;; |
---|
16 | ;;; You should have received a copy of the GNU General Public License |
---|
17 | ;;; along with this program; if not, write to the Free Software |
---|
18 | ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
---|
19 | |
---|
20 | ;;; Adapted from Closette. |
---|
21 | |
---|
22 | (in-package "SYSTEM") |
---|
23 | |
---|
24 | (defmacro push-on-end (value location) |
---|
25 | `(setf ,location (nconc ,location (list ,value)))) |
---|
26 | |
---|
27 | ;;; (SETF GETF*) is like (SETF GETF) except that it always changes the list, |
---|
28 | ;;; which must be non-nil. |
---|
29 | |
---|
30 | (defun (setf getf*) (new-value plist key) |
---|
31 | (block body |
---|
32 | (do ((x plist (cddr x))) |
---|
33 | ((null x)) |
---|
34 | (when (eq (car x) key) |
---|
35 | (setf (car (cdr x)) new-value) |
---|
36 | (return-from body new-value))) |
---|
37 | (push-on-end key plist) |
---|
38 | (push-on-end new-value plist) |
---|
39 | new-value)) |
---|
40 | |
---|
41 | (defun mapappend (fun &rest args) |
---|
42 | (if (some #'null args) |
---|
43 | () |
---|
44 | (append (apply fun (mapcar #'car args)) |
---|
45 | (apply #'mapappend fun (mapcar #'cdr args))))) |
---|
46 | |
---|
47 | (defun mapplist (fun x) |
---|
48 | (if (null x) |
---|
49 | () |
---|
50 | (cons (funcall fun (car x) (cadr x)) |
---|
51 | (mapplist fun (cddr x))))) |
---|
52 | |
---|
53 | (defsetf class-name %set-class-name) |
---|
54 | (defsetf class-direct-superclasses %set-class-direct-superclasses) |
---|
55 | (defsetf class-direct-subclasses %set-class-direct-subclasses) |
---|
56 | (defsetf class-direct-methods %set-class-direct-methods) |
---|
57 | (defsetf class-direct-slots %set-class-direct-slots) |
---|
58 | (defsetf class-precedence-list %set-class-precedence-list) |
---|
59 | (defsetf class-slots %set-class-slots) |
---|
60 | (defsetf std-instance-class %set-std-instance-class) |
---|
61 | (defsetf std-instance-slots %set-std-instance-slots) |
---|
62 | |
---|
63 | (defun (setf find-class) (new-value symbol &optional errorp environment) |
---|
64 | (%set-find-class symbol new-value)) |
---|
65 | |
---|
66 | (defun canonicalize-direct-slots (direct-slots) |
---|
67 | `(list ,@(mapcar #'canonicalize-direct-slot direct-slots))) |
---|
68 | |
---|
69 | (defun canonicalize-direct-slot (spec) |
---|
70 | (if (symbolp spec) |
---|
71 | `(list :name ',spec) |
---|
72 | (let ((name (car spec)) |
---|
73 | (initfunction nil) |
---|
74 | (initform nil) |
---|
75 | (initargs ()) |
---|
76 | (readers ()) |
---|
77 | (writers ()) |
---|
78 | (other-options ())) |
---|
79 | (do ((olist (cdr spec) (cddr olist))) |
---|
80 | ((null olist)) |
---|
81 | (case (car olist) |
---|
82 | (:initform |
---|
83 | (setq initfunction |
---|
84 | `(function (lambda () ,(cadr olist)))) |
---|
85 | (setq initform `',(cadr olist))) |
---|
86 | (:initarg |
---|
87 | (push-on-end (cadr olist) initargs)) |
---|
88 | (:reader |
---|
89 | (push-on-end (cadr olist) readers)) |
---|
90 | (:writer |
---|
91 | (push-on-end (cadr olist) writers)) |
---|
92 | (:accessor |
---|
93 | (push-on-end (cadr olist) readers) |
---|
94 | (push-on-end `(setf ,(cadr olist)) writers)) |
---|
95 | (otherwise |
---|
96 | (push-on-end `',(car olist) other-options) |
---|
97 | (push-on-end `',(cadr olist) other-options)))) |
---|
98 | `(list |
---|
99 | :name ',name |
---|
100 | ,@(when initfunction |
---|
101 | `(:initform ,initform |
---|
102 | :initfunction ,initfunction)) |
---|
103 | ,@(when initargs `(:initargs ',initargs)) |
---|
104 | ,@(when readers `(:readers ',readers)) |
---|
105 | ,@(when writers `(:writers ',writers)) |
---|
106 | ,@other-options)))) |
---|
107 | |
---|
108 | (defun canonicalize-direct-superclasses (direct-superclasses) |
---|
109 | `(list ,@(mapcar #'canonicalize-direct-superclass direct-superclasses))) |
---|
110 | |
---|
111 | (defun canonicalize-direct-superclass (class-name) |
---|
112 | `(find-class ',class-name)) |
---|
113 | |
---|
114 | (defun canonicalize-defclass-options (options) |
---|
115 | (mapappend #'canonicalize-defclass-option options)) |
---|
116 | |
---|
117 | (defun canonicalize-defclass-option (option) |
---|
118 | (case (car option) |
---|
119 | (:metaclass |
---|
120 | (list ':metaclass |
---|
121 | `(find-class ',(cadr option)))) |
---|
122 | (:default-initargs |
---|
123 | (list |
---|
124 | ':direct-default-initargs |
---|
125 | `(list ,@(mapappend |
---|
126 | #'(lambda (x) x) |
---|
127 | (mapplist |
---|
128 | #'(lambda (key value) |
---|
129 | `(',key ,value)) |
---|
130 | (cdr option)))))) |
---|
131 | (t (list `',(car option) `',(cadr option))))) |
---|
132 | |
---|
133 | ;;; Slot definition metaobjects |
---|
134 | |
---|
135 | ;;; N.B. Quietly retain all unknown slot options (rather than signaling an |
---|
136 | ;;; error), so that it's easy to add new ones. |
---|
137 | |
---|
138 | (defun make-direct-slot-definition (&rest properties |
---|
139 | &key name |
---|
140 | (initargs ()) |
---|
141 | (initform nil) |
---|
142 | (initfunction nil) |
---|
143 | (readers ()) |
---|
144 | (writers ()) |
---|
145 | (allocation :instance) |
---|
146 | &allow-other-keys) |
---|
147 | (let ((slot (copy-list properties))) ; Don't want to side effect &rest list |
---|
148 | (setf (getf* slot ':name) name) |
---|
149 | (setf (getf* slot ':initargs) initargs) |
---|
150 | (setf (getf* slot ':initform) initform) |
---|
151 | (setf (getf* slot ':initfunction) initfunction) |
---|
152 | (setf (getf* slot ':readers) readers) |
---|
153 | (setf (getf* slot ':writers) writers) |
---|
154 | (setf (getf* slot ':allocation) allocation) |
---|
155 | slot)) |
---|
156 | |
---|
157 | (defun make-effective-slot-definition (&rest properties |
---|
158 | &key name |
---|
159 | (initargs ()) |
---|
160 | (initform nil) |
---|
161 | (initfunction nil) |
---|
162 | (allocation :instance) |
---|
163 | &allow-other-keys) |
---|
164 | (let ((slot (copy-list properties))) ; Don't want to side effect &rest list |
---|
165 | (setf (getf* slot ':name) name) |
---|
166 | (setf (getf* slot ':initargs) initargs) |
---|
167 | (setf (getf* slot ':initform) initform) |
---|
168 | (setf (getf* slot ':initfunction) initfunction) |
---|
169 | (setf (getf* slot ':allocation) allocation) |
---|
170 | slot)) |
---|
171 | |
---|
172 | (defun slot-definition-name (slot) |
---|
173 | (getf slot ':name)) |
---|
174 | (defun (setf slot-definition-name) (new-value slot) |
---|
175 | (setf (getf* slot ':name) new-value)) |
---|
176 | |
---|
177 | (defun slot-definition-initfunction (slot) |
---|
178 | (getf slot ':initfunction)) |
---|
179 | (defun (setf slot-definition-initfunction) (new-value slot) |
---|
180 | (setf (getf* slot ':initfunction) new-value)) |
---|
181 | |
---|
182 | (defun slot-definition-initform (slot) |
---|
183 | (getf slot ':initform)) |
---|
184 | (defun (setf slot-definition-initform) (new-value slot) |
---|
185 | (setf (getf* slot ':initform) new-value)) |
---|
186 | |
---|
187 | (defun slot-definition-initargs (slot) |
---|
188 | (getf slot ':initargs)) |
---|
189 | (defun (setf slot-definition-initargs) (new-value slot) |
---|
190 | (setf (getf* slot ':initargs) new-value)) |
---|
191 | |
---|
192 | (defun slot-definition-readers (slot) |
---|
193 | (getf slot ':readers)) |
---|
194 | (defun (setf slot-definition-readers) (new-value slot) |
---|
195 | (setf (getf* slot ':readers) new-value)) |
---|
196 | |
---|
197 | (defun slot-definition-writers (slot) |
---|
198 | (getf slot ':writers)) |
---|
199 | (defun (setf slot-definition-writers) (new-value slot) |
---|
200 | (setf (getf* slot ':writers) new-value)) |
---|
201 | |
---|
202 | (defun slot-definition-allocation (slot) |
---|
203 | (getf slot ':allocation)) |
---|
204 | (defun (setf slot-definition-allocation) (new-value slot) |
---|
205 | (setf (getf* slot ':allocation) new-value)) |
---|
206 | |
---|
207 | ;;; finalize-inheritance |
---|
208 | |
---|
209 | (defun std-finalize-inheritance (class) |
---|
210 | (setf (class-precedence-list class) |
---|
211 | (funcall (if (eq (class-of class) the-class-standard-class) |
---|
212 | #'std-compute-class-precedence-list |
---|
213 | #'compute-class-precedence-list) |
---|
214 | class)) |
---|
215 | (setf (class-slots class) |
---|
216 | (funcall (if (eq (class-of class) the-class-standard-class) |
---|
217 | #'std-compute-slots |
---|
218 | #'compute-slots) |
---|
219 | class)) |
---|
220 | (values)) |
---|
221 | |
---|
222 | ;;; Class precedence lists |
---|
223 | |
---|
224 | (defun std-compute-class-precedence-list (class) |
---|
225 | (let ((classes-to-order (collect-superclasses* class))) |
---|
226 | (topological-sort classes-to-order |
---|
227 | (remove-duplicates |
---|
228 | (mapappend #'local-precedence-ordering |
---|
229 | classes-to-order)) |
---|
230 | #'std-tie-breaker-rule))) |
---|
231 | |
---|
232 | ;;; topological-sort implements the standard algorithm for topologically |
---|
233 | ;;; sorting an arbitrary set of elements while honoring the precedence |
---|
234 | ;;; constraints given by a set of (X,Y) pairs that indicate that element |
---|
235 | ;;; X must precede element Y. The tie-breaker procedure is called when it |
---|
236 | ;;; is necessary to choose from multiple minimal elements; both a list of |
---|
237 | ;;; candidates and the ordering so far are provided as arguments. |
---|
238 | |
---|
239 | (defun topological-sort (elements constraints tie-breaker) |
---|
240 | (let ((remaining-constraints constraints) |
---|
241 | (remaining-elements elements) |
---|
242 | (result ())) |
---|
243 | (loop |
---|
244 | (let ((minimal-elements |
---|
245 | (remove-if |
---|
246 | #'(lambda (class) |
---|
247 | (member class remaining-constraints |
---|
248 | :key #'cadr)) |
---|
249 | remaining-elements))) |
---|
250 | (when (null minimal-elements) |
---|
251 | (if (null remaining-elements) |
---|
252 | (return-from topological-sort result) |
---|
253 | (error "Inconsistent precedence graph."))) |
---|
254 | (let ((choice (if (null (cdr minimal-elements)) |
---|
255 | (car minimal-elements) |
---|
256 | (funcall tie-breaker |
---|
257 | minimal-elements |
---|
258 | result)))) |
---|
259 | (setq result (append result (list choice))) |
---|
260 | (setq remaining-elements |
---|
261 | (remove choice remaining-elements)) |
---|
262 | (setq remaining-constraints |
---|
263 | (remove choice |
---|
264 | remaining-constraints |
---|
265 | :test #'member))))))) |
---|
266 | |
---|
267 | ;;; In the event of a tie while topologically sorting class precedence lists, |
---|
268 | ;;; the CLOS Specification says to "select the one that has a direct subclass |
---|
269 | ;;; rightmost in the class precedence list computed so far." The same result |
---|
270 | ;;; is obtained by inspecting the partially constructed class precedence list |
---|
271 | ;;; from right to left, looking for the first minimal element to show up among |
---|
272 | ;;; the direct superclasses of the class precedence list constituent. |
---|
273 | ;;; (There's a lemma that shows that this rule yields a unique result.) |
---|
274 | |
---|
275 | (defun std-tie-breaker-rule (minimal-elements cpl-so-far) |
---|
276 | (dolist (cpl-constituent (reverse cpl-so-far)) |
---|
277 | (let* ((supers (class-direct-superclasses cpl-constituent)) |
---|
278 | (common (intersection minimal-elements supers))) |
---|
279 | (when (not (null common)) |
---|
280 | (return-from std-tie-breaker-rule (car common)))))) |
---|
281 | |
---|
282 | ;;; This version of collect-superclasses* isn't bothered by cycles in the class |
---|
283 | ;;; hierarchy, which sometimes happen by accident. |
---|
284 | |
---|
285 | (defun collect-superclasses* (class) |
---|
286 | (labels ((all-superclasses-loop (seen superclasses) |
---|
287 | (let ((to-be-processed |
---|
288 | (set-difference superclasses seen))) |
---|
289 | (if (null to-be-processed) |
---|
290 | superclasses |
---|
291 | (let ((class-to-process |
---|
292 | (car to-be-processed))) |
---|
293 | (all-superclasses-loop |
---|
294 | (cons class-to-process seen) |
---|
295 | (union (class-direct-superclasses |
---|
296 | class-to-process) |
---|
297 | superclasses))))))) |
---|
298 | (all-superclasses-loop () (list class)))) |
---|
299 | |
---|
300 | ;;; The local precedence ordering of a class C with direct superclasses C_1, |
---|
301 | ;;; C_2, ..., C_n is the set ((C C_1) (C_1 C_2) ...(C_n-1 C_n)). |
---|
302 | |
---|
303 | (defun local-precedence-ordering (class) |
---|
304 | (mapcar #'list |
---|
305 | (cons class |
---|
306 | (butlast (class-direct-superclasses class))) |
---|
307 | (class-direct-superclasses class))) |
---|
308 | |
---|
309 | ;;; Slot inheritance |
---|
310 | |
---|
311 | (defun std-compute-slots (class) |
---|
312 | (let* ((all-slots (mapappend #'class-direct-slots |
---|
313 | (class-precedence-list class))) |
---|
314 | (all-names (remove-duplicates |
---|
315 | (mapcar #'slot-definition-name all-slots)))) |
---|
316 | (mapcar #'(lambda (name) |
---|
317 | (funcall |
---|
318 | (if (eq (class-of class) the-class-standard-class) |
---|
319 | #'std-compute-effective-slot-definition |
---|
320 | #'compute-effective-slot-definition) |
---|
321 | class |
---|
322 | (remove name all-slots |
---|
323 | :key #'slot-definition-name |
---|
324 | :test-not #'eq))) |
---|
325 | all-names))) |
---|
326 | |
---|
327 | (defun std-compute-effective-slot-definition (class direct-slots) |
---|
328 | (declare (ignore class)) |
---|
329 | (let ((initer (find-if-not #'null direct-slots |
---|
330 | :key #'slot-definition-initfunction))) |
---|
331 | (make-effective-slot-definition |
---|
332 | :name (slot-definition-name (car direct-slots)) |
---|
333 | :initform (if initer |
---|
334 | (slot-definition-initform initer) |
---|
335 | nil) |
---|
336 | :initfunction (if initer |
---|
337 | (slot-definition-initfunction initer) |
---|
338 | nil) |
---|
339 | :initargs (remove-duplicates |
---|
340 | (mapappend #'slot-definition-initargs |
---|
341 | direct-slots)) |
---|
342 | :allocation (slot-definition-allocation (car direct-slots))))) |
---|
343 | |
---|
344 | ;;; Simple vectors are used for slot storage. |
---|
345 | |
---|
346 | (defun allocate-slot-storage (size initial-value) |
---|
347 | (make-array size :initial-element initial-value)) |
---|
348 | |
---|
349 | ;;; Standard instance slot access |
---|
350 | |
---|
351 | ;;; N.B. The location of the effective-slots slots in the class metaobject for |
---|
352 | ;;; standard-class must be determined without making any further slot |
---|
353 | ;;; references. |
---|
354 | |
---|
355 | (defvar the-slots-of-standard-class) ;standard-class's class-slots |
---|
356 | (defvar the-class-standard-class (find-class 'standard-class)) |
---|
357 | |
---|
358 | (defun slot-location (class slot-name) |
---|
359 | (if (and (eq slot-name 'effective-slots) |
---|
360 | (eq class the-class-standard-class)) |
---|
361 | (position 'effective-slots the-slots-of-standard-class |
---|
362 | :key #'slot-definition-name) |
---|
363 | (let ((slot (find slot-name |
---|
364 | (class-slots class) |
---|
365 | :key #'slot-definition-name))) |
---|
366 | (if (null slot) |
---|
367 | (error "the slot ~S is missing from the class ~S" |
---|
368 | slot-name class) |
---|
369 | (let ((pos (position slot |
---|
370 | (remove-if-not #'instance-slot-p |
---|
371 | (class-slots class))))) |
---|
372 | (if (null pos) |
---|
373 | (error "the slot ~S is not an instance slot in the class ~S" |
---|
374 | slot-name class) |
---|
375 | pos)))))) |
---|
376 | |
---|
377 | (defun slot-contents (slots location) |
---|
378 | (svref slots location)) |
---|
379 | |
---|
380 | (defun (setf slot-contents) (new-value slots location) |
---|
381 | (setf (svref slots location) new-value)) |
---|
382 | |
---|
383 | (defun std-slot-value (instance slot-name) |
---|
384 | (let* ((location (slot-location (class-of instance) slot-name)) |
---|
385 | (slots (std-instance-slots instance)) |
---|
386 | (val (slot-contents slots location))) |
---|
387 | (if (eq secret-unbound-value val) |
---|
388 | (error "the slot ~S is unbound in the object ~S" slot-name instance) |
---|
389 | val))) |
---|
390 | (defun slot-value (object slot-name) |
---|
391 | (if (eq (class-of (class-of object)) the-class-standard-class) |
---|
392 | (std-slot-value object slot-name) |
---|
393 | (slot-value-using-class (class-of object) object slot-name))) |
---|
394 | |
---|
395 | (defun (setf std-slot-value) (new-value instance slot-name) |
---|
396 | (let ((location (slot-location (class-of instance) slot-name)) |
---|
397 | (slots (std-instance-slots instance))) |
---|
398 | (setf (slot-contents slots location) new-value))) |
---|
399 | (defun (setf slot-value) (new-value object slot-name) |
---|
400 | (if (eq (class-of (class-of object)) the-class-standard-class) |
---|
401 | (setf (std-slot-value object slot-name) new-value) |
---|
402 | (setf-slot-value-using-class |
---|
403 | new-value (class-of object) object slot-name))) |
---|
404 | |
---|
405 | (defun std-slot-boundp (instance slot-name) |
---|
406 | (let ((location (slot-location (class-of instance) slot-name)) |
---|
407 | (slots (std-instance-slots instance))) |
---|
408 | (not (eq secret-unbound-value (slot-contents slots location))))) |
---|
409 | (defun slot-boundp (object slot-name) |
---|
410 | (if (eq (class-of (class-of object)) the-class-standard-class) |
---|
411 | (std-slot-boundp object slot-name) |
---|
412 | (slot-boundp-using-class (class-of object) object slot-name))) |
---|
413 | |
---|
414 | (defun std-slot-makunbound (instance slot-name) |
---|
415 | (let ((location (slot-location (class-of instance) slot-name)) |
---|
416 | (slots (std-instance-slots instance))) |
---|
417 | (setf (slot-contents slots location) secret-unbound-value)) |
---|
418 | instance) |
---|
419 | (defun slot-makunbound (object slot-name) |
---|
420 | (if (eq (class-of (class-of object)) the-class-standard-class) |
---|
421 | (std-slot-makunbound object slot-name) |
---|
422 | (slot-makunbound-using-class (class-of object) object slot-name))) |
---|
423 | |
---|
424 | (defun std-slot-exists-p (instance slot-name) |
---|
425 | (not (null (find slot-name (class-slots (class-of instance)) |
---|
426 | :key #'slot-definition-name)))) |
---|
427 | (defun slot-exists-p (object slot-name) |
---|
428 | (if (eq (class-of (class-of object)) the-class-standard-class) |
---|
429 | (std-slot-exists-p object slot-name) |
---|
430 | (slot-exists-p-using-class (class-of object) object slot-name))) |
---|
431 | |
---|
432 | ;;; Standard instance allocation |
---|
433 | |
---|
434 | (defparameter secret-unbound-value (list "slot unbound")) |
---|
435 | |
---|
436 | (defun instance-slot-p (slot) |
---|
437 | (eq (slot-definition-allocation slot) ':instance)) |
---|
438 | |
---|
439 | (defun std-allocate-instance (class) |
---|
440 | ;; (format t "std-allocate-instance class = ~S~%" class) |
---|
441 | ;; (format t "class-slots = ~S~%" (class-slots class)) |
---|
442 | (allocate-std-instance |
---|
443 | class |
---|
444 | (allocate-slot-storage (count-if #'instance-slot-p (class-slots class)) |
---|
445 | secret-unbound-value))) |
---|
446 | |
---|
447 | (defun allocate-instance (class) |
---|
448 | (std-allocate-instance class)) |
---|
449 | |
---|
450 | (defun make-instance-standard-class (metaclass &key name direct-superclasses direct-slots |
---|
451 | &allow-other-keys) |
---|
452 | (declare (ignore metaclass)) |
---|
453 | ;; (format t "name = ~S~%" name) |
---|
454 | ;; (format t "direct-superclasses = ~S~%" direct-superclasses) |
---|
455 | ;; (format t "direct-slots = ~S~%" direct-slots) |
---|
456 | (let ((class (std-allocate-instance (find-class 'standard-class)))) |
---|
457 | (setf (class-name class) name) |
---|
458 | (setf (class-direct-subclasses class) ()) |
---|
459 | (setf (class-direct-methods class) ()) |
---|
460 | (std-after-initialization-for-classes class |
---|
461 | :direct-slots direct-slots |
---|
462 | :direct-superclasses direct-superclasses) |
---|
463 | class)) |
---|
464 | |
---|
465 | ;; FIXME |
---|
466 | (defun std-after-initialization-for-classes (class |
---|
467 | &key direct-superclasses direct-slots |
---|
468 | &allow-other-keys) |
---|
469 | (let ((supers |
---|
470 | (or direct-superclasses |
---|
471 | (list (find-class 'standard-object))))) |
---|
472 | (setf (class-direct-superclasses class) supers) |
---|
473 | (dolist (superclass supers) |
---|
474 | (push class (class-direct-subclasses superclass)))) |
---|
475 | (let ((slots |
---|
476 | (mapcar #'(lambda (slot-properties) |
---|
477 | (apply #'make-direct-slot-definition |
---|
478 | slot-properties)) |
---|
479 | direct-slots))) |
---|
480 | (setf (class-direct-slots class) slots) |
---|
481 | (dolist (direct-slot slots) |
---|
482 | (dolist (reader (slot-definition-readers direct-slot)) |
---|
483 | (add-reader-method |
---|
484 | class reader (slot-definition-name direct-slot))) |
---|
485 | (dolist (writer (slot-definition-writers direct-slot)) |
---|
486 | (add-writer-method |
---|
487 | class writer (slot-definition-name direct-slot))))) |
---|
488 | (funcall (if (eq (class-of class) (find-class 'standard-class)) |
---|
489 | #'std-finalize-inheritance |
---|
490 | #'finalize-inheritance) |
---|
491 | class) |
---|
492 | (values)) |
---|
493 | |
---|
494 | (defun canonical-slot-name (canonical-slot) |
---|
495 | (getf canonical-slot :name)) |
---|
496 | |
---|
497 | (defun ensure-class (name &rest all-keys &allow-other-keys) |
---|
498 | ;; Check for duplicate slots. |
---|
499 | (let ((slots (getf all-keys :direct-slots))) |
---|
500 | (dolist (s1 slots) |
---|
501 | (let ((name1 (canonical-slot-name s1))) |
---|
502 | (dolist (s2 (cdr (memq s1 slots))) |
---|
503 | (when (eq name1 (canonical-slot-name s2)) |
---|
504 | (error 'program-error "duplicate slot ~S" name1)))))) |
---|
505 | (let ((class (find-class name nil))) |
---|
506 | (unless class |
---|
507 | (setf class (apply #'make-instance-standard-class |
---|
508 | (find-class 'standard-class) :name name all-keys)) |
---|
509 | (%set-find-class name class)) |
---|
510 | class)) |
---|
511 | |
---|
512 | (defmacro defclass (name direct-superclasses direct-slots |
---|
513 | &rest options) |
---|
514 | `(ensure-class ',name |
---|
515 | :direct-superclasses |
---|
516 | ,(canonicalize-direct-superclasses direct-superclasses) |
---|
517 | :direct-slots |
---|
518 | ,(canonicalize-direct-slots direct-slots) |
---|
519 | ,@(canonicalize-defclass-options options))) |
---|
520 | |
---|
521 | ;;; |
---|
522 | ;;; Generic function metaobjects and standard-generic-function |
---|
523 | ;;; |
---|
524 | |
---|
525 | (defclass standard-generic-function (generic-function) |
---|
526 | ((name :initarg :name) ; :accessor generic-function-name |
---|
527 | (lambda-list ; :accessor generic-function-lambda-list |
---|
528 | :initarg :lambda-list) |
---|
529 | (methods :initform ()) ; :accessor generic-function-methods |
---|
530 | (method-class ; :accessor generic-function-method-class |
---|
531 | :initarg :method-class) |
---|
532 | ;; (discriminating-function) ; :accessor generic-function-discriminating-function |
---|
533 | (classes-to-emf-table ; :accessor classes-to-emf-table |
---|
534 | :initform (make-hash-table :test #'equal)))) |
---|
535 | |
---|
536 | (defvar the-class-standard-gf (find-class 'standard-generic-function)) |
---|
537 | |
---|
538 | (defun generic-function-name (gf) |
---|
539 | (slot-value gf 'name)) |
---|
540 | (defun (setf generic-function-name) (new-value gf) |
---|
541 | (setf (slot-value gf 'name) new-value)) |
---|
542 | |
---|
543 | (defun generic-function-lambda-list (gf) |
---|
544 | (slot-value gf 'lambda-list)) |
---|
545 | (defun (setf generic-function-lambda-list) (new-value gf) |
---|
546 | (setf (slot-value gf 'lambda-list) new-value)) |
---|
547 | |
---|
548 | (defun generic-function-methods (gf) |
---|
549 | (slot-value gf 'methods)) |
---|
550 | (defun (setf generic-function-methods) (new-value gf) |
---|
551 | (setf (slot-value gf 'methods) new-value)) |
---|
552 | |
---|
553 | ;; (defun generic-function-discriminating-function (gf) |
---|
554 | ;; (slot-value gf 'discriminating-function)) |
---|
555 | ;; (defun (setf generic-function-discriminating-function) (new-value gf) |
---|
556 | ;; (setf (slot-value gf 'discriminating-function) new-value)) |
---|
557 | (defsetf generic-function-discriminating-function |
---|
558 | %set-generic-function-discriminating-function) |
---|
559 | |
---|
560 | (defun generic-function-method-class (gf) |
---|
561 | (slot-value gf 'method-class)) |
---|
562 | (defun (setf generic-function-method-class) (new-value gf) |
---|
563 | (setf (slot-value gf 'method-class) new-value)) |
---|
564 | |
---|
565 | ;;; Internal accessor for effective method function table |
---|
566 | |
---|
567 | (defun classes-to-emf-table (gf) |
---|
568 | (slot-value gf 'classes-to-emf-table)) |
---|
569 | (defun (setf classes-to-emf-table) (new-value gf) |
---|
570 | (setf (slot-value gf 'classes-to-emf-table) new-value)) |
---|
571 | |
---|
572 | ;;; |
---|
573 | ;;; Method metaobjects and standard-method |
---|
574 | ;;; |
---|
575 | |
---|
576 | (defclass standard-method () |
---|
577 | ((lambda-list :initarg :lambda-list) ; :accessor method-lambda-list |
---|
578 | (qualifiers :initarg :qualifiers) ; :accessor method-qualifiers |
---|
579 | (specializers :initarg :specializers) ; :accessor method-specializers |
---|
580 | (body :initarg :body) ; :accessor method-body |
---|
581 | (environment :initarg :environment) ; :accessor method-environment |
---|
582 | (generic-function :initform nil) ; :accessor method-generic-function |
---|
583 | (function))) ; :accessor method-function |
---|
584 | |
---|
585 | (defvar the-class-standard-method (find-class 'standard-method)) |
---|
586 | |
---|
587 | (defun method-lambda-list (method) (slot-value method 'lambda-list)) |
---|
588 | (defun (setf method-lambda-list) (new-value method) |
---|
589 | (setf (slot-value method 'lambda-list) new-value)) |
---|
590 | |
---|
591 | (defun method-qualifiers (method) (slot-value method 'qualifiers)) |
---|
592 | (defun (setf method-qualifiers) (new-value method) |
---|
593 | (setf (slot-value method 'qualifiers) new-value)) |
---|
594 | |
---|
595 | (defun method-specializers (method) (slot-value method 'specializers)) |
---|
596 | (defun (setf method-specializers) (new-value method) |
---|
597 | (setf (slot-value method 'specializers) new-value)) |
---|
598 | |
---|
599 | (defun method-body (method) (slot-value method 'body)) |
---|
600 | (defun (setf method-body) (new-value method) |
---|
601 | (setf (slot-value method 'body) new-value)) |
---|
602 | |
---|
603 | (defun method-environment (method) (slot-value method 'environment)) |
---|
604 | (defun (setf method-environment) (new-value method) |
---|
605 | (setf (slot-value method 'environment) new-value)) |
---|
606 | |
---|
607 | (defun method-generic-function (method) |
---|
608 | (slot-value method 'generic-function)) |
---|
609 | (defun (setf method-generic-function) (new-value method) |
---|
610 | (setf (slot-value method 'generic-function) new-value)) |
---|
611 | |
---|
612 | (defun method-function (method) (slot-value method 'function)) |
---|
613 | (defun (setf method-function) (new-value method) |
---|
614 | (setf (slot-value method 'function) new-value)) |
---|
615 | |
---|
616 | ;;; defgeneric |
---|
617 | |
---|
618 | (defmacro defgeneric (function-name lambda-list |
---|
619 | &rest options-and-method-descriptions) |
---|
620 | (let ((options ()) |
---|
621 | (methods ())) |
---|
622 | (dolist (item options-and-method-descriptions) |
---|
623 | (case (car item) |
---|
624 | (declare) ; FIXME |
---|
625 | (:method |
---|
626 | (push `(defmethod ,function-name ,@(cdr item)) methods)) |
---|
627 | (t |
---|
628 | (push item options)))) |
---|
629 | (setf options (nreverse options) |
---|
630 | methods (nreverse methods)) |
---|
631 | `(prog1 |
---|
632 | (ensure-generic-function |
---|
633 | ',function-name |
---|
634 | :lambda-list ',lambda-list |
---|
635 | ,@(canonicalize-defgeneric-options options)) |
---|
636 | ,@methods))) |
---|
637 | |
---|
638 | (defun canonicalize-defgeneric-options (options) |
---|
639 | (mapappend #'canonicalize-defgeneric-option options)) |
---|
640 | |
---|
641 | (defun canonicalize-defgeneric-option (option) |
---|
642 | (case (car option) |
---|
643 | (:generic-function-class |
---|
644 | (list ':generic-function-class |
---|
645 | `(find-class ',(cadr option)))) |
---|
646 | (:method-class |
---|
647 | (list ':method-class |
---|
648 | `(find-class ',(cadr option)))) |
---|
649 | (t (list `',(car option) `',(cadr option))))) |
---|
650 | |
---|
651 | ;;; find-generic-function looks up a generic function by name. It's an |
---|
652 | ;;; artifact of the fact that our generic function metaobjects can't legally |
---|
653 | ;;; be stored a symbol's function value. |
---|
654 | |
---|
655 | (defparameter generic-function-table (make-hash-table :test #'equal)) |
---|
656 | |
---|
657 | (defun find-generic-function (symbol &optional (errorp t)) |
---|
658 | (let ((gf (gethash symbol generic-function-table nil))) |
---|
659 | (if (and (null gf) errorp) |
---|
660 | (error "no generic function named ~S" symbol) |
---|
661 | gf))) |
---|
662 | |
---|
663 | (defun (setf find-generic-function) (new-value symbol) |
---|
664 | (setf (gethash symbol generic-function-table) new-value)) |
---|
665 | |
---|
666 | ;;; ensure-generic-function |
---|
667 | |
---|
668 | (defun ensure-generic-function (function-name |
---|
669 | &rest all-keys |
---|
670 | &key |
---|
671 | (generic-function-class the-class-standard-gf) |
---|
672 | (method-class the-class-standard-method) |
---|
673 | &allow-other-keys) |
---|
674 | (if (find-generic-function function-name nil) |
---|
675 | (find-generic-function function-name) |
---|
676 | (progn |
---|
677 | (when (fboundp function-name) |
---|
678 | (error 'program-error |
---|
679 | "~A already names an ordinary function, macro, or special operator" |
---|
680 | function-name)) |
---|
681 | (let ((gf (apply (if (eq generic-function-class the-class-standard-gf) |
---|
682 | #'make-instance-standard-generic-function |
---|
683 | #'make-instance) |
---|
684 | generic-function-class |
---|
685 | :name function-name |
---|
686 | :method-class method-class |
---|
687 | all-keys))) |
---|
688 | (setf (find-generic-function function-name) gf) |
---|
689 | gf)))) |
---|
690 | |
---|
691 | ;;; finalize-generic-function |
---|
692 | |
---|
693 | ;;; N.B. Same basic idea as finalize-inheritance. Takes care of recomputing |
---|
694 | ;;; and storing the discriminating function, and clearing the effective method |
---|
695 | ;;; function table. |
---|
696 | |
---|
697 | (defun finalize-generic-function (gf) |
---|
698 | (setf (generic-function-discriminating-function gf) |
---|
699 | (funcall (if (eq (class-of gf) the-class-standard-gf) |
---|
700 | #'std-compute-discriminating-function |
---|
701 | #'compute-discriminating-function) |
---|
702 | gf)) |
---|
703 | (setf (fdefinition (generic-function-name gf)) |
---|
704 | ;; (generic-function-discriminating-function gf)) |
---|
705 | gf) |
---|
706 | (clrhash (classes-to-emf-table gf)) |
---|
707 | (values)) |
---|
708 | |
---|
709 | ;;; make-instance-standard-generic-function creates and initializes an |
---|
710 | ;;; instance of standard-generic-function without falling into method lookup. |
---|
711 | ;;; However, it cannot be called until standard-generic-function exists. |
---|
712 | |
---|
713 | (defun make-instance-standard-generic-function (generic-function-class |
---|
714 | &key name lambda-list method-class) |
---|
715 | (declare (ignore generic-function-class)) |
---|
716 | (let ((gf (std-allocate-instance the-class-standard-gf))) |
---|
717 | ;; (format t "gf = ~S~%" gf) |
---|
718 | (setf (generic-function-name gf) name) |
---|
719 | (setf (generic-function-lambda-list gf) lambda-list) |
---|
720 | (setf (generic-function-methods gf) ()) |
---|
721 | (setf (generic-function-method-class gf) method-class) |
---|
722 | (setf (classes-to-emf-table gf) (make-hash-table :test #'equal)) |
---|
723 | (finalize-generic-function gf) |
---|
724 | gf)) |
---|
725 | |
---|
726 | ;;; Run-time environment hacking (Common Lisp ain't got 'em). |
---|
727 | |
---|
728 | (defun top-level-environment () |
---|
729 | nil) ; Bogus top level lexical environment |
---|
730 | |
---|
731 | (defvar compile-methods nil) ; by default, run everything interpreted |
---|
732 | |
---|
733 | (defun compile-in-lexical-environment (env lambda-expr) |
---|
734 | (declare (ignore env)) |
---|
735 | (if compile-methods |
---|
736 | (compile nil lambda-expr) |
---|
737 | (eval `(function ,lambda-expr)))) |
---|
738 | |
---|
739 | ;;; defmethod |
---|
740 | |
---|
741 | (defmacro defmethod (&rest args) |
---|
742 | (multiple-value-bind (function-name qualifiers lambda-list specializers |
---|
743 | body) |
---|
744 | (parse-defmethod args) |
---|
745 | `(progn |
---|
746 | (ensure-generic-function |
---|
747 | ',function-name |
---|
748 | :lambda-list ',lambda-list) |
---|
749 | (ensure-method (find-generic-function ',function-name) |
---|
750 | :lambda-list ',lambda-list |
---|
751 | :qualifiers ',qualifiers |
---|
752 | :specializers ,(canonicalize-specializers specializers) |
---|
753 | :body ',body |
---|
754 | :environment (top-level-environment))))) |
---|
755 | |
---|
756 | (defun canonicalize-specializers (specializers) |
---|
757 | `(list ,@(mapcar #'canonicalize-specializer specializers))) |
---|
758 | |
---|
759 | (defun canonicalize-specializer (specializer) |
---|
760 | ;; FIXME (EQL specializers) |
---|
761 | `(if (atom ',specializer) (find-class ',specializer) (find-class 't))) |
---|
762 | |
---|
763 | (defun parse-defmethod (args) |
---|
764 | (let ((fn-spec (car args)) |
---|
765 | (qualifiers ()) |
---|
766 | (specialized-lambda-list nil) |
---|
767 | (body ()) |
---|
768 | (parse-state :qualifiers)) |
---|
769 | (dolist (arg (cdr args)) |
---|
770 | (ecase parse-state |
---|
771 | (:qualifiers |
---|
772 | (if (and (atom arg) (not (null arg))) |
---|
773 | (push-on-end arg qualifiers) |
---|
774 | (progn (setq specialized-lambda-list arg) |
---|
775 | (setq parse-state :body)))) |
---|
776 | (:body (push-on-end arg body)))) |
---|
777 | (values fn-spec |
---|
778 | qualifiers |
---|
779 | (extract-lambda-list specialized-lambda-list) |
---|
780 | (extract-specializers specialized-lambda-list) |
---|
781 | (list* 'block |
---|
782 | (if (consp fn-spec) |
---|
783 | (cadr fn-spec) |
---|
784 | fn-spec) |
---|
785 | body)))) |
---|
786 | |
---|
787 | ;;; Several tedious functions for analyzing lambda lists |
---|
788 | |
---|
789 | (defun required-portion (gf args) |
---|
790 | (let ((number-required (length (gf-required-arglist gf)))) |
---|
791 | (when (< (length args) number-required) |
---|
792 | (error 'program-error "not enough arguments for generic function ~S" gf)) |
---|
793 | (subseq args 0 number-required))) |
---|
794 | |
---|
795 | (defun gf-required-arglist (gf) |
---|
796 | (let ((plist |
---|
797 | (analyze-lambda-list |
---|
798 | (generic-function-lambda-list gf)))) |
---|
799 | (getf plist ':required-args))) |
---|
800 | |
---|
801 | (defun extract-lambda-list (specialized-lambda-list) |
---|
802 | (let* ((plist (analyze-lambda-list specialized-lambda-list)) |
---|
803 | (requireds (getf plist ':required-names)) |
---|
804 | (rv (getf plist ':rest-var)) |
---|
805 | (ks (getf plist ':key-args)) |
---|
806 | (aok (getf plist ':allow-other-keys)) |
---|
807 | (opts (getf plist ':optional-args)) |
---|
808 | (auxs (getf plist ':auxiliary-args))) |
---|
809 | `(,@requireds |
---|
810 | ,@(if rv `(&rest ,rv) ()) |
---|
811 | ,@(if (or ks aok) `(&key ,@ks) ()) |
---|
812 | ,@(if aok '(&allow-other-keys) ()) |
---|
813 | ,@(if opts `(&optional ,@opts) ()) |
---|
814 | ,@(if auxs `(&aux ,@auxs) ())))) |
---|
815 | |
---|
816 | (defun extract-specializers (specialized-lambda-list) |
---|
817 | (let ((plist (analyze-lambda-list specialized-lambda-list))) |
---|
818 | (getf plist ':specializers))) |
---|
819 | |
---|
820 | (defun analyze-lambda-list (lambda-list) |
---|
821 | (labels ((make-keyword (symbol) |
---|
822 | (intern (symbol-name symbol) |
---|
823 | (find-package 'keyword))) |
---|
824 | (get-keyword-from-arg (arg) |
---|
825 | (if (listp arg) |
---|
826 | (if (listp (car arg)) |
---|
827 | (caar arg) |
---|
828 | (make-keyword (car arg))) |
---|
829 | (make-keyword arg)))) |
---|
830 | (let ((keys ()) ; Just the keywords |
---|
831 | (key-args ()) ; Keywords argument specs |
---|
832 | (required-names ()) ; Just the variable names |
---|
833 | (required-args ()) ; Variable names & specializers |
---|
834 | (specializers ()) ; Just the specializers |
---|
835 | (rest-var nil) |
---|
836 | (optionals ()) |
---|
837 | (auxs ()) |
---|
838 | (allow-other-keys nil) |
---|
839 | (state :parsing-required)) |
---|
840 | (dolist (arg lambda-list) |
---|
841 | (if (member arg lambda-list-keywords) |
---|
842 | (ecase arg |
---|
843 | (&optional |
---|
844 | (setq state :parsing-optional)) |
---|
845 | (&rest |
---|
846 | (setq state :parsing-rest)) |
---|
847 | (&key |
---|
848 | (setq state :parsing-key)) |
---|
849 | (&allow-other-keys |
---|
850 | (setq allow-other-keys 't)) |
---|
851 | (&aux |
---|
852 | (setq state :parsing-aux))) |
---|
853 | (case state |
---|
854 | (:parsing-required |
---|
855 | (push-on-end arg required-args) |
---|
856 | (if (listp arg) |
---|
857 | (progn (push-on-end (car arg) required-names) |
---|
858 | (push-on-end (cadr arg) specializers)) |
---|
859 | (progn (push-on-end arg required-names) |
---|
860 | (push-on-end 't specializers)))) |
---|
861 | (:parsing-optional (push-on-end arg optionals)) |
---|
862 | (:parsing-rest (setq rest-var arg)) |
---|
863 | (:parsing-key |
---|
864 | (push-on-end (get-keyword-from-arg arg) keys) |
---|
865 | (push-on-end arg key-args)) |
---|
866 | (:parsing-aux (push-on-end arg auxs))))) |
---|
867 | (list :required-names required-names |
---|
868 | :required-args required-args |
---|
869 | :specializers specializers |
---|
870 | :rest-var rest-var |
---|
871 | :keywords keys |
---|
872 | :key-args key-args |
---|
873 | :auxiliary-args auxs |
---|
874 | :optional-args optionals |
---|
875 | :allow-other-keys allow-other-keys)))) |
---|
876 | |
---|
877 | ;;; ensure method |
---|
878 | |
---|
879 | (defun ensure-method (gf &rest all-keys) |
---|
880 | (let ((new-method |
---|
881 | (apply |
---|
882 | (if (eq (generic-function-method-class gf) |
---|
883 | the-class-standard-method) |
---|
884 | #'make-instance-standard-method |
---|
885 | #'make-instance) |
---|
886 | (generic-function-method-class gf) |
---|
887 | all-keys))) |
---|
888 | (add-method gf new-method) |
---|
889 | new-method)) |
---|
890 | |
---|
891 | ;;; make-instance-standard-method creates and initializes an instance of |
---|
892 | ;;; standard-method without falling into method lookup. However, it cannot |
---|
893 | ;;; be called until standard-method exists. |
---|
894 | |
---|
895 | (defun make-instance-standard-method (method-class |
---|
896 | &key lambda-list qualifiers |
---|
897 | specializers body environment) |
---|
898 | (declare (ignore method-class)) |
---|
899 | (let ((method (std-allocate-instance the-class-standard-method))) |
---|
900 | (setf (method-lambda-list method) lambda-list) |
---|
901 | (setf (method-qualifiers method) qualifiers) |
---|
902 | (setf (method-specializers method) specializers) |
---|
903 | (setf (method-body method) body) |
---|
904 | (setf (method-environment method) environment) |
---|
905 | (setf (method-generic-function method) nil) |
---|
906 | (setf (method-function method) |
---|
907 | (std-compute-method-function method)) |
---|
908 | method)) |
---|
909 | |
---|
910 | ;;; add-method |
---|
911 | |
---|
912 | ;;; N.B. This version first removes any existing method on the generic function |
---|
913 | ;;; with the same qualifiers and specializers. It's a pain to develop |
---|
914 | ;;; programs without this feature of full CLOS. |
---|
915 | |
---|
916 | (defun add-method (gf method) |
---|
917 | (let ((old-method |
---|
918 | (find-method gf (method-qualifiers method) |
---|
919 | (method-specializers method) nil))) |
---|
920 | (when old-method (remove-method gf old-method))) |
---|
921 | (setf (method-generic-function method) gf) |
---|
922 | (push method (generic-function-methods gf)) |
---|
923 | (dolist (specializer (method-specializers method)) |
---|
924 | (pushnew method (class-direct-methods specializer))) |
---|
925 | (finalize-generic-function gf) |
---|
926 | method) |
---|
927 | |
---|
928 | (defun remove-method (gf method) |
---|
929 | (setf (generic-function-methods gf) |
---|
930 | (remove method (generic-function-methods gf))) |
---|
931 | (setf (method-generic-function method) nil) |
---|
932 | ;; (format t "remove-method method-specializers = ~S~%" (method-specializers method)) |
---|
933 | (dolist (class (method-specializers method)) |
---|
934 | (setf (class-direct-methods class) |
---|
935 | (remove method (class-direct-methods class)))) |
---|
936 | (finalize-generic-function gf) |
---|
937 | method) |
---|
938 | |
---|
939 | (defun find-method (gf qualifiers specializers |
---|
940 | &optional (errorp t)) |
---|
941 | (let ((method |
---|
942 | (find-if #'(lambda (method) |
---|
943 | (and (equal qualifiers |
---|
944 | (method-qualifiers method)) |
---|
945 | (equal specializers |
---|
946 | (method-specializers method)))) |
---|
947 | (generic-function-methods gf)))) |
---|
948 | (if (and (null method) errorp) |
---|
949 | (error "No such method for ~S." (generic-function-name gf)) |
---|
950 | method))) |
---|
951 | |
---|
952 | ;;; Reader and write methods |
---|
953 | |
---|
954 | (defun add-reader-method (class fn-name slot-name) |
---|
955 | ;; (format t "add-reader-method ~S~%" fn-name) |
---|
956 | (ensure-method |
---|
957 | (ensure-generic-function fn-name :lambda-list '(object)) |
---|
958 | :lambda-list '(object) |
---|
959 | :qualifiers () |
---|
960 | :specializers (list class) |
---|
961 | :body `(slot-value object ',slot-name) |
---|
962 | :environment (top-level-environment)) |
---|
963 | (values)) |
---|
964 | |
---|
965 | (defun add-writer-method (class fn-name slot-name) |
---|
966 | (ensure-method |
---|
967 | (ensure-generic-function |
---|
968 | fn-name :lambda-list '(new-value object)) |
---|
969 | :lambda-list '(new-value object) |
---|
970 | :qualifiers () |
---|
971 | :specializers (list (find-class 't) class) |
---|
972 | :body `(setf (slot-value object ',slot-name) |
---|
973 | new-value) |
---|
974 | :environment (top-level-environment)) |
---|
975 | (values)) |
---|
976 | |
---|
977 | ;;; subclassp and sub-specializer-p |
---|
978 | |
---|
979 | (defun subclassp (c1 c2) |
---|
980 | (not (null (find c2 (class-precedence-list c1))))) |
---|
981 | |
---|
982 | (defun sub-specializer-p (c1 c2 c-arg) |
---|
983 | (let ((cpl (class-precedence-list c-arg))) |
---|
984 | (not (null (find c2 (cdr (member c1 cpl))))))) |
---|
985 | |
---|
986 | ;;; |
---|
987 | ;;; Generic function invocation |
---|
988 | ;;; |
---|
989 | |
---|
990 | ;;; apply-generic-function |
---|
991 | |
---|
992 | (defun apply-generic-function (gf args) |
---|
993 | (apply (generic-function-discriminating-function gf) args)) |
---|
994 | |
---|
995 | ;;; compute-discriminating-function |
---|
996 | |
---|
997 | (defun std-compute-discriminating-function (gf) |
---|
998 | #'(lambda (&rest args) |
---|
999 | (let* ((classes (mapcar #'class-of |
---|
1000 | (required-portion gf args))) |
---|
1001 | (emfun (gethash classes (classes-to-emf-table gf) nil))) |
---|
1002 | (if emfun |
---|
1003 | (funcall emfun args) |
---|
1004 | (slow-method-lookup gf args classes))))) |
---|
1005 | |
---|
1006 | (defun slow-method-lookup (gf args classes) |
---|
1007 | (let* ((applicable-methods |
---|
1008 | (compute-applicable-methods-using-classes gf classes)) |
---|
1009 | (emfun |
---|
1010 | (funcall |
---|
1011 | (if (eq (class-of gf) the-class-standard-gf) |
---|
1012 | #'std-compute-effective-method-function |
---|
1013 | #'compute-effective-method-function) |
---|
1014 | gf applicable-methods))) |
---|
1015 | (setf (gethash classes (classes-to-emf-table gf)) emfun) |
---|
1016 | (funcall emfun args))) |
---|
1017 | |
---|
1018 | ;;; compute-applicable-methods-using-classes |
---|
1019 | |
---|
1020 | (defun compute-applicable-methods-using-classes (gf required-classes) |
---|
1021 | (sort |
---|
1022 | (copy-list |
---|
1023 | (remove-if-not #'(lambda (method) |
---|
1024 | (every #'subclassp |
---|
1025 | required-classes |
---|
1026 | (method-specializers method))) |
---|
1027 | (generic-function-methods gf))) |
---|
1028 | #'(lambda (m1 m2) |
---|
1029 | (funcall |
---|
1030 | (if (eq (class-of gf) the-class-standard-gf) |
---|
1031 | #'std-method-more-specific-p |
---|
1032 | #'method-more-specific-p) |
---|
1033 | gf m1 m2 required-classes)))) |
---|
1034 | |
---|
1035 | ;;; method-more-specific-p |
---|
1036 | |
---|
1037 | (defun std-method-more-specific-p (gf method1 method2 required-classes) |
---|
1038 | (declare (ignore gf)) |
---|
1039 | (mapc #'(lambda (spec1 spec2 arg-class) |
---|
1040 | (unless (eq spec1 spec2) |
---|
1041 | (return-from std-method-more-specific-p |
---|
1042 | (sub-specializer-p spec1 spec2 arg-class)))) |
---|
1043 | (method-specializers method1) |
---|
1044 | (method-specializers method2) |
---|
1045 | required-classes) |
---|
1046 | nil) |
---|
1047 | |
---|
1048 | ;;; apply-methods and compute-effective-method-function |
---|
1049 | |
---|
1050 | (defun apply-methods (gf args methods) |
---|
1051 | (funcall (compute-effective-method-function gf methods) |
---|
1052 | args)) |
---|
1053 | |
---|
1054 | (defun primary-method-p (method) |
---|
1055 | (null (method-qualifiers method))) |
---|
1056 | (defun before-method-p (method) |
---|
1057 | (equal '(:before) (method-qualifiers method))) |
---|
1058 | (defun after-method-p (method) |
---|
1059 | (equal '(:after) (method-qualifiers method))) |
---|
1060 | (defun around-method-p (method) |
---|
1061 | (equal '(:around) (method-qualifiers method))) |
---|
1062 | |
---|
1063 | (defun std-compute-effective-method-function (gf methods) |
---|
1064 | (let ((primaries (remove-if-not #'primary-method-p methods)) |
---|
1065 | (around (find-if #'around-method-p methods))) |
---|
1066 | (when (null primaries) |
---|
1067 | (error "no primary methods for the generic function ~S" gf)) |
---|
1068 | (if around |
---|
1069 | (let ((next-emfun |
---|
1070 | (funcall |
---|
1071 | (if (eq (class-of gf) the-class-standard-gf) |
---|
1072 | #'std-compute-effective-method-function |
---|
1073 | #'compute-effective-method-function) |
---|
1074 | gf (remove around methods)))) |
---|
1075 | #'(lambda (args) |
---|
1076 | (funcall (method-function around) args next-emfun))) |
---|
1077 | (let ((next-emfun (compute-primary-emfun (cdr primaries))) |
---|
1078 | (befores (remove-if-not #'before-method-p methods)) |
---|
1079 | (reverse-afters |
---|
1080 | (reverse (remove-if-not #'after-method-p methods)))) |
---|
1081 | #'(lambda (args) |
---|
1082 | (dolist (before befores) |
---|
1083 | (funcall (method-function before) args nil)) |
---|
1084 | (multiple-value-prog1 |
---|
1085 | (funcall (method-function (car primaries)) args next-emfun) |
---|
1086 | (dolist (after reverse-afters) |
---|
1087 | (funcall (method-function after) args nil)))))))) |
---|
1088 | |
---|
1089 | ;;; compute an effective method function from a list of primary methods: |
---|
1090 | |
---|
1091 | (defun compute-primary-emfun (methods) |
---|
1092 | (if (null methods) |
---|
1093 | nil |
---|
1094 | (let ((next-emfun (compute-primary-emfun (cdr methods)))) |
---|
1095 | #'(lambda (args) |
---|
1096 | (funcall (method-function (car methods)) args next-emfun))))) |
---|
1097 | |
---|
1098 | ;;; apply-method and compute-method-function |
---|
1099 | |
---|
1100 | (defun apply-method (method args next-methods) |
---|
1101 | (funcall (method-function method) |
---|
1102 | args |
---|
1103 | (if (null next-methods) |
---|
1104 | nil |
---|
1105 | (compute-effective-method-function |
---|
1106 | (method-generic-function method) next-methods)))) |
---|
1107 | |
---|
1108 | (defun std-compute-method-function (method) |
---|
1109 | (let ((form (method-body method)) |
---|
1110 | (lambda-list (method-lambda-list method))) |
---|
1111 | (compile-in-lexical-environment |
---|
1112 | (method-environment method) |
---|
1113 | `(lambda (args next-emfun) |
---|
1114 | (flet ((call-next-method (&rest cnm-args) |
---|
1115 | (if (null next-emfun) |
---|
1116 | (error "no next method for the generic function ~S" |
---|
1117 | (method-generic-function ',method)) |
---|
1118 | (funcall next-emfun (or cnm-args args)))) |
---|
1119 | (next-method-p () |
---|
1120 | (not (null next-emfun)))) |
---|
1121 | (apply #'(lambda ,(kludge-arglist lambda-list) |
---|
1122 | ,form) |
---|
1123 | args)))))) |
---|
1124 | |
---|
1125 | ;;; N.B. The function kludge-arglist is used to pave over the differences |
---|
1126 | ;;; between argument keyword compatibility for regular functions versus |
---|
1127 | ;;; generic functions. |
---|
1128 | |
---|
1129 | (defun kludge-arglist (lambda-list) |
---|
1130 | (if (and (member '&key lambda-list) |
---|
1131 | (not (member '&allow-other-keys lambda-list))) |
---|
1132 | (append lambda-list '(&allow-other-keys)) |
---|
1133 | (if (and (not (member '&rest lambda-list)) |
---|
1134 | (not (member '&key lambda-list))) |
---|
1135 | (append lambda-list '(&key &allow-other-keys)) |
---|
1136 | lambda-list))) |
---|
1137 | |
---|
1138 | ;;; Slot access |
---|
1139 | |
---|
1140 | (defun setf-slot-value-using-class (new-value class instance slot-name) |
---|
1141 | (setf (std-slot-value instance slot-name) new-value)) |
---|
1142 | |
---|
1143 | (defgeneric slot-value-using-class (class instance slot-name)) |
---|
1144 | (defmethod slot-value-using-class ((class standard-class) instance slot-name) |
---|
1145 | (std-slot-value instance slot-name)) |
---|
1146 | |
---|
1147 | (defgeneric (setf slot-value-using-class) (new-value class instance slot-name)) |
---|
1148 | (defmethod (setf slot-value-using-class) |
---|
1149 | (new-value (class standard-class) instance slot-name) |
---|
1150 | (setf (std-slot-value instance slot-name) new-value)) |
---|
1151 | ;;; N.B. To avoid making a forward reference to a (setf xxx) generic function: |
---|
1152 | ;; (defun setf-slot-value-using-class (new-value class object slot-name) |
---|
1153 | ;; (setf (slot-value-using-class class object slot-name) new-value)) |
---|
1154 | |
---|
1155 | (defgeneric slot-exists-p-using-class (class instance slot-name)) |
---|
1156 | (defmethod slot-exists-p-using-class |
---|
1157 | ((class standard-class) instance slot-name) |
---|
1158 | (std-slot-exists-p instance slot-name)) |
---|
1159 | |
---|
1160 | (defgeneric slot-boundp-using-class (class instance slot-name)) |
---|
1161 | (defmethod slot-boundp-using-class |
---|
1162 | ((class standard-class) instance slot-name) |
---|
1163 | (std-slot-boundp instance slot-name)) |
---|
1164 | |
---|
1165 | (defgeneric slot-makunbound-using-class (class instance slot-name)) |
---|
1166 | (defmethod slot-makunbound-using-class |
---|
1167 | ((class standard-class) instance slot-name) |
---|
1168 | (std-slot-makunbound instance slot-name)) |
---|
1169 | |
---|
1170 | ;;; Instance creation and initialization |
---|
1171 | |
---|
1172 | ;; (defgeneric allocate-instance (class)) |
---|
1173 | ;; (defmethod allocate-instance ((class standard-class)) |
---|
1174 | ;; (std-allocate-instance class)) |
---|
1175 | |
---|
1176 | (defgeneric make-instance (class &key)) |
---|
1177 | (defmethod make-instance ((class standard-class) &rest initargs) |
---|
1178 | (let ((instance (allocate-instance class))) |
---|
1179 | (apply #'initialize-instance instance initargs) |
---|
1180 | instance)) |
---|
1181 | (defmethod make-instance ((class symbol) &rest initargs) |
---|
1182 | (apply #'make-instance (find-class class) initargs)) |
---|
1183 | |
---|
1184 | (defgeneric initialize-instance (instance &key)) |
---|
1185 | (defmethod initialize-instance ((instance standard-object) &rest initargs) |
---|
1186 | (apply #'shared-initialize instance t initargs)) |
---|
1187 | |
---|
1188 | (defgeneric reinitialize-instance (instance &key)) |
---|
1189 | (defmethod reinitialize-instance |
---|
1190 | ((instance standard-object) &rest initargs) |
---|
1191 | (apply #'shared-initialize instance () initargs)) |
---|
1192 | |
---|
1193 | (defgeneric shared-initialize (instance slot-names &key)) |
---|
1194 | (defmethod shared-initialize ((instance standard-object) |
---|
1195 | slot-names &rest all-keys) |
---|
1196 | (dolist (slot (class-slots (class-of instance))) |
---|
1197 | (let ((slot-name (slot-definition-name slot))) |
---|
1198 | (multiple-value-bind (init-key init-value foundp) |
---|
1199 | (get-properties |
---|
1200 | all-keys (slot-definition-initargs slot)) |
---|
1201 | (declare (ignore init-key)) |
---|
1202 | (if foundp |
---|
1203 | (setf (slot-value instance slot-name) init-value) |
---|
1204 | (when (and (not (slot-boundp instance slot-name)) |
---|
1205 | (not (null (slot-definition-initfunction slot))) |
---|
1206 | (or (eq slot-names t) |
---|
1207 | (member slot-name slot-names))) |
---|
1208 | (setf (slot-value instance slot-name) |
---|
1209 | (funcall (slot-definition-initfunction slot)))))))) |
---|
1210 | instance) |
---|
1211 | |
---|
1212 | ;;; change-class |
---|
1213 | |
---|
1214 | (defgeneric change-class (instance new-class &key)) |
---|
1215 | (defmethod change-class |
---|
1216 | ((old-instance standard-object) |
---|
1217 | (new-class standard-class) |
---|
1218 | &rest initargs) |
---|
1219 | (let ((new-instance (allocate-instance new-class))) |
---|
1220 | (dolist (slot-name (mapcar #'slot-definition-name |
---|
1221 | (class-slots new-class))) |
---|
1222 | (when (and (slot-exists-p old-instance slot-name) |
---|
1223 | (slot-boundp old-instance slot-name)) |
---|
1224 | (setf (slot-value new-instance slot-name) |
---|
1225 | (slot-value old-instance slot-name)))) |
---|
1226 | (rotatef (std-instance-slots new-instance) |
---|
1227 | (std-instance-slots old-instance)) |
---|
1228 | (rotatef (std-instance-class new-instance) |
---|
1229 | (std-instance-class old-instance)) |
---|
1230 | (apply #'update-instance-for-different-class |
---|
1231 | new-instance old-instance initargs) |
---|
1232 | old-instance)) |
---|
1233 | |
---|
1234 | (defmethod change-class |
---|
1235 | ((instance standard-object) (new-class symbol) &rest initargs) |
---|
1236 | (apply #'change-class instance (find-class new-class) initargs)) |
---|
1237 | |
---|
1238 | (defgeneric update-instance-for-different-class (old new &key)) |
---|
1239 | (defmethod update-instance-for-different-class |
---|
1240 | ((old standard-object) (new standard-object) &rest initargs) |
---|
1241 | (let ((added-slots |
---|
1242 | (remove-if #'(lambda (slot-name) |
---|
1243 | (slot-exists-p old slot-name)) |
---|
1244 | (mapcar #'slot-definition-name |
---|
1245 | (class-slots (class-of new)))))) |
---|
1246 | (apply #'shared-initialize new added-slots initargs))) |
---|
1247 | |
---|
1248 | ;;; |
---|
1249 | ;;; Methods having to do with class metaobjects. |
---|
1250 | ;;; |
---|
1251 | |
---|
1252 | (defmethod print-object ((class standard-class) stream) |
---|
1253 | (print-unreadable-object (class stream :identity t) |
---|
1254 | (format stream "~:(~S~) ~S" |
---|
1255 | (class-name (class-of class)) |
---|
1256 | (class-name class))) |
---|
1257 | class) |
---|
1258 | |
---|
1259 | (defmethod initialize-instance :after ((class standard-class) &rest args) |
---|
1260 | (apply #'std-after-initialization-for-classes class args)) |
---|
1261 | |
---|
1262 | ;;; Finalize inheritance |
---|
1263 | |
---|
1264 | (defgeneric finalize-inheritance (class)) |
---|
1265 | (defmethod finalize-inheritance ((class standard-class)) |
---|
1266 | (std-finalize-inheritance class) |
---|
1267 | (values)) |
---|
1268 | |
---|
1269 | ;;; Class precedence lists |
---|
1270 | |
---|
1271 | (defgeneric compute-class-precedence-list (class)) |
---|
1272 | (defmethod compute-class-precedence-list ((class standard-class)) |
---|
1273 | (std-compute-class-precedence-list class)) |
---|
1274 | |
---|
1275 | ;;; Slot inheritance |
---|
1276 | |
---|
1277 | (defgeneric compute-slots (class)) |
---|
1278 | (defmethod compute-slots ((class standard-class)) |
---|
1279 | (std-compute-slots class)) |
---|
1280 | |
---|
1281 | (defgeneric compute-effective-slot-definition (class direct-slots)) |
---|
1282 | (defmethod compute-effective-slot-definition |
---|
1283 | ((class standard-class) direct-slots) |
---|
1284 | (std-compute-effective-slot-definition class direct-slots)) |
---|
1285 | |
---|
1286 | ;;; |
---|
1287 | ;;; Methods having to do with generic function metaobjects. |
---|
1288 | ;;; |
---|
1289 | |
---|
1290 | (defmethod print-object ((gf standard-generic-function) stream) |
---|
1291 | (print-unreadable-object (gf stream :identity t) |
---|
1292 | (format stream "~:(~S~) ~S" |
---|
1293 | (class-name (class-of gf)) |
---|
1294 | (generic-function-name gf))) |
---|
1295 | gf) |
---|
1296 | |
---|
1297 | (defmethod initialize-instance :after ((gf standard-generic-function) &key) |
---|
1298 | (finalize-generic-function gf)) |
---|
1299 | |
---|
1300 | ;;; |
---|
1301 | ;;; Methods having to do with method metaobjects. |
---|
1302 | ;;; |
---|
1303 | |
---|
1304 | (defmethod print-object ((method standard-method) stream) |
---|
1305 | (print-unreadable-object (method stream :identity t) |
---|
1306 | (format stream "~:(~S~) ~S~{ ~S~} ~S" |
---|
1307 | (class-name (class-of method)) |
---|
1308 | (generic-function-name |
---|
1309 | (method-generic-function method)) |
---|
1310 | (method-qualifiers method) |
---|
1311 | (mapcar #'class-name |
---|
1312 | (method-specializers method)))) |
---|
1313 | method) |
---|
1314 | |
---|
1315 | (defmethod initialize-instance :after ((method standard-method) &key) |
---|
1316 | (setf (method-function method) (compute-method-function method))) |
---|
1317 | |
---|
1318 | ;;; |
---|
1319 | ;;; Methods having to do with generic function invocation. |
---|
1320 | ;;; |
---|
1321 | |
---|
1322 | (defgeneric compute-discriminating-function (gf)) |
---|
1323 | (defmethod compute-discriminating-function ((gf standard-generic-function)) |
---|
1324 | (std-compute-discriminating-function gf)) |
---|
1325 | |
---|
1326 | (defgeneric method-more-specific-p (gf method1 method2 required-classes)) |
---|
1327 | (defmethod method-more-specific-p |
---|
1328 | ((gf standard-generic-function) method1 method2 required-classes) |
---|
1329 | (std-method-more-specific-p gf method1 method2 required-classes)) |
---|
1330 | |
---|
1331 | (defgeneric compute-effective-method-function (gf methods)) |
---|
1332 | (defmethod compute-effective-method-function |
---|
1333 | ((gf standard-generic-function) methods) |
---|
1334 | (std-compute-effective-method-function gf methods)) |
---|
1335 | |
---|
1336 | (defgeneric compute-method-function (method)) |
---|
1337 | (defmethod compute-method-function ((method standard-method)) |
---|
1338 | (std-compute-method-function method)) |
---|
1339 | |
---|
1340 | (defgeneric compute-applicable-methods (gf args)) |
---|
1341 | (defmethod compute-applicable-methods ((gf standard-generic-function) args) |
---|
1342 | (compute-applicable-methods-using-classes gf (mapcar #'class-of args))) |
---|