slot-value to proto-slot-value, within all names.
[CommonLispStat.git] / src / objsys / lsobjects.lsp
blob3fa14e2ebe6c0a87d867db086072bbf9de84214a
1 ;;; -*- mode: lisp -*-
2 ;;; Copyright (c) 2005--2007, by A.J. Rossini <blindglobe@gmail.com>
3 ;;; See COPYRIGHT file for any additional restrictions (BSD license).
4 ;;; Since 1991, ANSI was finally finished. Edited for ANSI Common Lisp.
6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;;;
9 ;;;; LISP-STAT Object System
10 ;;;;
11 ;;;;
12 ;;;; Simple CL implementation of the object system for Lisp-Stat (LSOS)
13 ;;;; as described in Tierney (1990).
14 ;;;;
15 ;;;; Copyright (c) 1991--, by Luke Tierney. Permission is granted for
16 ;;;; unrestricted use.
17 ;;;;
18 ;;;;
19 ;;;; NOTES:
20 ;;;;
21 ;;;; If your CL's handling of packages is compliant with CLtL, 2nd
22 ;;;; Edition (like Macintosh CL version 2), add the feature :CLtL2
23 ;;;; before loading or compiling this code.
24 ;;;;
25 ;;;; This implementation does not make use of CLOS. It can coexist
26 ;;;; with CLOS, but there are two name conflicts: slot-value and
27 ;;;; call-next-method.
28 ;;;; FIXED-ME: SLOT-VALUE now becomes PROTO-SLOT-VALUE...
29 ;;;; These two symbols are shadowed in the LSOS
30 ;;;; package and must be shadowed in any package that uses LSOS.
31 ;;;; Evaluating the function (lsos::use-lsos) from a package after
32 ;;;; loading this code shadows these two symbols and does a
33 ;;;; use-package for LSOS.
34 ;;;;
35 ;;;; The :compile-method method uses function-lambda-expression
36 ;;;; defined in CLtL, 2nd Edition. (This method is only needed if
37 ;;;; you want to force compilation of an interpreted method. It is
38 ;;;; not used by the compiler.)
39 ;;;;
40 ;;;; The efficiency of this code could be improved by low level
41 ;;;; coding of the dispatching functions send, call-method and
42 ;;;; call-next-method to avoid creating an argument list. Other
43 ;;;; efficiency improvements are possible as well, in particular
44 ;;;; by good use of declarations. It may also be possible to build
45 ;;;; a more efficient implementation using the CLOS metaclass
46 ;;;; protocol.
47 ;;;;
48 ;;;; There are a few minimal tools for experimenting with constraints
49 ;;;; in the code; they are marked by #+:constreinthooks. Sometime
50 ;;;; soon I hope to augment or replace these hooks with a CORAL-like
51 ;;;; constraint system (as used in GARNET).
52 ;;;;
53 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
54 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56 (in-package :lisp-stat-object-system)
58 (defun use-lsos ()
59 "Formerly set up to import lisp-stat-object-system into current package."
60 (shadowing-import (package-shadowing-symbols 'lisp-stat-object-system))
61 (use-package 'lisp-stat-object-system))
63 ;;; Structure Implementation of Lisp-Stat Object System
65 ;; We might consider a global rewrite if it doesn't seem to break
66 ;; anything. In particular, the real name ought to be
67 ;; proto-sys-object or similar so that we can ensure that the right
68 ;; interpretation is made for this. Call it the prototype object
69 ;; system, and possibly be done with it then.
71 (defvar *object-serial* 0)
73 (defstruct (ls-object
74 (:constructor make-object-structure) ;; why not make-ls-object?
75 (:print-function print-object-structure)
76 (:predicate objectp)) ;; why not ls-object-p?
77 slots
78 methods
79 parents
80 preclist ;; precedence list
81 (serial (incf *object-serial*)))
83 (defun print-object-structure (object stream depth)
84 (declare (ignore depth))
85 (send object :print stream))
86 ;;; The following is probably more useful than the above...
87 (defmethod print-object ((object ls-object) stream)
88 (send object :print stream))
90 (setf (documentation 'objectp 'function)
91 "Args: (x)
92 Returns T if X is an object, NIL otherwise.")
94 (defvar *object* (make-object-structure)
95 "*object* is the global root object.")
97 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98 ;;;;
99 ;;;; Utilities
100 ;;;;
101 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
103 ;;; AJR:FIXME:Is this going to cause issues with
104 ;;; concurrency/threading?
105 ;;; (need to appropriately handle interrupts). Could change this to
106 ;;; a stack or list that could handle modifications and interrupts
107 ;;; in-line.
108 (defvar *self* nil
109 "special variable holding current value of SELF.
110 Assigned with current object being worked on.")
112 (defun get-self ()
113 "Return object we are operating on, otherwise throw error."
114 (if (not (objectp *self*))
115 (error "Error: no method focused on."))
116 *self*)
118 (defun has-duplicates (list)
119 "predicate: takes a list, and returns true if duplicates.
120 This should be simpler, right?"
121 (do ((next list (rest next)))
122 ((not (consp next)) nil)
123 (if (member (first next) (rest next)) (return t))))
125 (defun assoc-eq (item alist)
126 "Version of assoc using eq -- should be faster than regular assoc."
127 (declare (inline car eq))
128 (dolist (i alist)
129 (if (eq (car i) item) (return i))))
131 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
133 ;;; Predicates for Consistency Checking
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
137 (defun check-non-nil-symbol (x)
138 (unless (and x (symbolp x)) (error "bad symbol - ~s" x)))
140 (defun check-object (x)
141 "Returns self if true, throws an error otherwise."
142 (if (objectp x) x (error "bad object - ~s" x)))
144 (defun kind-of-p (x y)
145 "Args: (x y)
147 Returns T if X and Y are objects and X inherits from Y, NIL otherwise."
149 (if (and (objectp x) (objectp y))
150 (if (member y (ls-object-preclist x)) t nil)
151 nil))
153 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154 ;;;;
155 ;;;; Precedence List Functions
156 ;;;;
157 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159 (defun find-SC (object)
160 "find set of object and ancestors. (diff from this and find-S?)"
161 (copy-list (ls-object-preclist (check-object object))))
163 (defun find-S (object)
164 "find set of object and ancestors. (diff from this and find-SC?)"
165 (do ((result nil)
166 (parents (ls-object-parents object) (cdr parents)))
167 ((not (consp parents))
168 (delete-duplicates (cons object result)))
169 (setf result (nconc (find-SC (first parents)) result))))
171 (defun find-RC (object)
172 "find local precedence ordering."
173 (let ((list (copy-list (ls-object-parents (check-object object)))))
174 (do ((next list (rest next)))
175 ((not (consp next)) list)
176 (setf (first next) (cons object (first next)))
177 (setf object (rest (first next))))))
179 (defun find-R (S)
180 "find partial precedence ordering."
181 (do ((result nil)
182 (S S (rest S)))
183 ((not (consp S))
184 (delete-duplicates result))
185 (setf result (nconc result (find-RC (first S))))))
187 (defun has-predecessor (x R)
188 "check if x has a predecessor according to R."
189 (dolist (cell R nil)
190 (if (and (consp cell) (eq x (rest cell))) (return t))))
192 (defun find-no-predecessor-list (S R)
193 "find list of objects in S without predecessors, by R."
194 (let ((result nil))
195 (dolist (x S result)
196 (unless (has-predecessor x R) (setf result (cons x result))))))
198 (defun child-position (x P)
199 "find the position of child, if any, of x in P, the list found so
200 far."
201 (let ((count 0))
202 (declare (fixnum count))
203 (dolist (next P -1)
204 (if (member x (ls-object-parents next)) (return count))
205 (incf count))))
207 (defun next-object (no-preds P)
208 "find the next object in the precedence list from objects with no
209 predecessor and current list."
210 (cond
211 ((not (consp no-preds)) nil)
212 ((not (consp (rest no-preds))) (first no-preds))
214 (let ((count -1)
215 (result nil))
216 (declare (fixnum count))
217 (dolist (x no-preds result)
218 (let ((tcount (child-position x P)))
219 (declare (fixnum tcount))
220 (when (> tcount count)
221 (setf result x)
222 (setf count tcount))))))))
224 (defun trim-S (x S)
225 "Remove object x from S."
226 (delete x S))
228 (defun trim-R (x R)
229 "Remove all pairs containing x from R. x is assumed to have no
230 predecessors, so only the first position is checked."
231 (delete x R :key #'first))
233 (defun precedence-list (object)
234 "Calculate the object's precedence list."
235 (do* ((S (find-S object))
236 (R (find-R S))
237 (P nil)
238 (no-preds nil)
239 (next nil))
240 ((not (consp S)) P)
241 (setf no-preds (find-no-predecessor-list S R))
242 (setf next (next-object no-preds P))
243 (if (null next) (error "inconsistent precedence order"))
244 (setf P (nconc P (list next)))
245 (setf S (trim-S next S))
246 (setf R (trim-R next R))))
248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
249 ;;;;
250 ;;;; Object Construction Functions
251 ;;;;
252 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
254 (defun calculate-preclist (object)
255 "Return the precedence list for the object."
256 (let ((parents (ls-object-parents (check-object object))))
257 (if (not (consp parents)) (error "bad parent list - ~s" parents))
258 (if (consp (rest parents))
259 (precedence-list object)
260 (let ((parent (check-object (first parents))))
261 (cons object (ls-object-preclist parent))))))
263 (defun check-parents (parents)
264 "Ensure valid parents: They must be null, object, or consp without duplicates."
265 (cond
266 ((or (null parents) (objectp parents)) parents)
267 ((consp parents)
268 (dolist (x parents) (check-object x))
269 (if (has-duplicates parents)
270 (error "parents may not contain duplicates")))
271 (t (error "bad parents - ~s" parents))))
273 (defun make-basic-object (parents object)
274 "Creates a basic object for the prototype system by ensuring that it
275 can be placed into the storage heirarchy.
276 If object is not initialized, instantiate the structure.
277 Place into parental structure.
278 If parents is null, use root *object*,
279 if parents is a single object, use it (encapsulate as list)
280 otherwise, use parents"
282 (check-parents parents)
284 (if (not (objectp object)) (setf object (make-object-structure)))
286 (setf (ls-object-preclist object) (ls-object-preclist *object*))
287 (setf (ls-object-parents object)
288 (cond ((null parents) (list *object*))
289 ((objectp parents) (list parents))
290 (t parents)))
291 (setf (ls-object-preclist object) (calculate-preclist object))
293 object)
295 (defun make-object (&rest parents)
296 "Args: (&rest parents)
297 Returns a new object with parents PARENTS. If PARENTS is NIL, (list *OBJECT*) is used."
298 (make-basic-object parents NIL))
300 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
301 ;;;;
302 ;;;; Constraint Hook Functions
303 ;;;;
304 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
306 (pushnew :constrainthooks *features*)
308 #+:constrainthooks
309 (progn
310 (defvar *message-hook* nil)
311 (defvar *set-slot-hook* nil)
313 (defun check-constraint-hooks (object sym slot)
314 (let ((hook (if slot *set-slot-hook* *message-hook*)))
315 (if hook
316 (if slot
317 (let ((*set-slot-hook* nil))
318 (funcall hook object sym))
319 (let ((*message-hook* nil))
320 (funcall hook object sym)))))))
322 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
324 ;;; Slot Access Functions
326 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
328 (defun make-slot-entry (x y) (cons x y))
329 (defun slot-entry-p (x) (consp x))
330 (defun slot-entry-key (x) (first x))
331 (defun slot-entry-value (x) (rest x))
332 (defun set-slot-entry-value (x v) (setf (rest x) v))
333 (defsetf slot-entry-value set-slot-entry-value)
335 (defun find-own-slot (x slot)
336 (if (objectp x) (assoc-eq slot (ls-object-slots x))))
338 (defun find-slot (x slot)
339 (if (objectp x)
340 (let ((preclist (ls-object-preclist x)))
341 (dolist (object preclist)
342 (let ((slot-entry (find-own-slot object slot)))
343 (if slot-entry (return slot-entry)))))))
345 (defun add-slot (x slot value)
346 (check-object x)
347 (check-non-nil-symbol slot)
348 (let ((slot-entry (find-own-slot x slot)))
349 (if slot-entry
350 (setf (slot-entry-value slot-entry) value)
351 (setf (ls-object-slots x)
352 (cons (make-slot-entry slot value) (ls-object-slots x)))))
353 nil)
355 (defun delete-slot (x slot)
356 (check-object x)
357 (setf (ls-object-slots x)
358 (delete slot (ls-object-slots x) :key #'slot-entry-key)))
360 (defun get-proto-slot-value (x slot &optional no-err)
361 (check-object x)
362 (let ((slot-entry (find-slot x slot)))
363 (if (slot-entry-p slot-entry)
364 (slot-entry-value slot-entry)
365 (unless no-err (error "no slot named ~s in this object" slot)))))
367 (defun set-proto-slot-value (x slot value)
368 (check-object x)
369 (let ((slot-entry (find-own-slot x slot)))
370 (cond
371 ((slot-entry-p slot-entry)
372 (set-slot-entry-value slot-entry value)
373 #+:constrainthooks (check-constraint-hooks x slot t))
375 (if (find-slot x slot) ;; either way we error...?
376 (error "object does not own slot ~s" slot)
377 (error "no slot named ~s in this object" slot))))))
379 ;;; FIXME: THIS WAS EVIL -- renamed SLOT-VALUE to PROTO-SLOT-VALUE, so
380 ;;; that we can do CLOS simultaneously.
381 (defun proto-slot-value (slot)
382 "Args: (slot)
384 Must be used in a method. Returns the value of current objects slot
385 named SLOT.
386 EVIL -- it conflicts with CLOS object slots."
387 (get-proto-slot-value (get-self) slot))
389 (defun proto-slot-value-setf (slot value)
390 (set-proto-slot-value (get-self) slot value))
392 (defsetf proto-slot-value proto-slot-value-setf)
394 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
395 ;;;;
396 ;;;; Method Access Functions;
397 ;;;;
398 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
400 (defun make-method-entry (x y) (cons x y))
401 (defun method-entry-p (x) (consp x))
402 (defun method-entry-key (x) (first x))
403 (defun method-entry-method (x) (rest x))
404 (defun set-method-entry-method (x v) (setf (rest x) v))
405 (defsetf method-entry-method set-method-entry-method)
407 (defun find-own-method (x selector)
408 (if (objectp x) (assoc-eq selector (ls-object-methods x)))) ;; prev was assoc not assoc-eq
410 (defun find-lsos-method (x selector)
411 (if (objectp x)
412 (let ((preclist (ls-object-preclist x)))
413 (dolist (object preclist)
414 (let ((method-entry (find-own-method object selector)))
415 (if method-entry (return method-entry)))))))
417 (defun add-lsos-method (x selector value)
418 "x = object; selector = name of method; value = form computing the method."
419 (check-object x)
420 (check-non-nil-symbol selector)
421 (let ((method-entry (find-own-method x selector)))
422 (if method-entry
423 (setf (method-entry-method method-entry) value)
424 (setf (ls-object-methods x)
425 (cons (make-method-entry selector value) (ls-object-methods x)))))
426 nil)
428 (defun delete-method (x selector)
429 (check-object x)
430 (setf (ls-object-methods x)
431 (delete selector (ls-object-methods x) :key #'method-entry-key)))
433 (defun get-message-method (x selector &optional no-err)
434 (check-object x)
435 (let ((method-entry (find-lsos-method x selector)))
436 (if (method-entry-p method-entry)
437 (method-entry-method method-entry)
438 (unless no-err (error "no method for selector ~s" selector)))))
440 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
442 ;;; Message Sending Functions
444 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
446 (defvar *current-preclist* nil)
447 (defvar *current-selector* nil)
449 (defun sendmsg (object selector preclist args)
450 (let ((method-entry nil)
451 (method nil))
453 ;; look for the message in the precedence list
454 (loop
455 (setf method-entry (find-own-method (first preclist) selector))
456 (if (or method-entry (not (consp preclist))) (return))
457 (setf preclist (rest preclist)))
458 (cond
459 ((null method-entry) (error "no method for selector ~s" selector))
460 ((not (method-entry-p method-entry)) (error "bad method entry"))
461 (t (setf method (method-entry-method method-entry))))
463 ;; invoke the method
464 (let ((*current-preclist* preclist)
465 (*current-selector* selector)
466 (*self* object))
467 (multiple-value-prog1
468 (apply method object args)
469 #+:constrainthooks (check-constraint-hooks object selector nil)))))
471 ;;;; built-in send function
472 (defun send (object selector &rest args)
473 "Args: (object selector &rest args)
474 Applies first method for SELECTOR found in OBJECT's precedence list to
475 OBJECT and ARGS."
476 (sendmsg object selector (ls-object-preclist object) args))
478 ;;;FIXME: Need to include a "setter" for "send".
482 ;;;; call-next-method - call inherited version of current method
483 (defun call-next-method (&rest args)
484 "Args (&rest args)
485 Funcalls next method for current selector and precedence list. Can only be
486 used in a method."
487 (sendmsg *self* *current-selector* (rest *current-preclist*) args))
489 (defun call-method (object selector &rest args)
490 "Args (object selector &rest args)
491 Funcalls method for SELECTOR found in OBJECT to SELF. Can only be used in
492 a method.
493 Call method belonging to another object on current object."
494 (sendmsg *self* selector (ls-object-preclist object) args))
496 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
498 ;;; Object Documentation
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
502 (defun find-documentation (x sym add)
503 (if (objectp x)
504 (let ((doc (find-own-slot x 'documentation)))
505 (if (and (null doc) add) (add-slot x 'documentation nil))
506 (if (slot-entry-p doc) (assoc sym (slot-entry-value doc))))))
508 (defun add-documentation (x sym value)
509 (check-object x)
510 (check-non-nil-symbol sym)
511 (let ((doc-entry (find-documentation x sym t)))
512 (cond
513 ((not (null doc-entry))
514 (setf (rest doc-entry) value))
516 (set-proto-slot-value x
517 'documentation
518 (cons (cons sym value)
519 (get-proto-slot-value x 'documentation))))))
520 nil)
522 (defun get-documentation (x sym)
523 (check-object x)
524 (dolist (object (ls-object-preclist x))
525 (let ((doc-entry (find-documentation object sym nil))) ;; FIXME: verify
526 (if doc-entry (return (rest doc-entry))))))
528 ;;;;
529 ;;;; DEFMETH Macro
530 ;;;;
532 (defmacro defmeth (object name arglist first &rest body)
533 "Syntax: (defmeth object method-name lambda-list [doc] {form}*)
534 OBJECT must evaluate to an existing object. Installs a method for NAME in
535 the value of OBJECT and installs DOC in OBJECTS's documentation.
536 RETURNS: method-name."
537 ;; (declare (ignorable self)) ;; compiler hint that it isn't always used.
538 (if (and body (stringp first))
539 `(progn ;; first=docstring + body
540 (add-lsos-method ,object ,name
541 #'(lambda (self ,@arglist) (block ,name ,@body)))
542 (add-documentation ,object ,name ,first)
543 ,name)
544 `(progn ;; first=code + body
545 (add-lsos-method ,object ,name
546 #'(lambda (self ,@arglist) (block ,name ,first ,@body)))
547 ,name)))
550 ;;; Prototype Construction
553 (defun find-instance-slots (x slots)
554 (let ((result (nreverse (delete-duplicates (copy-list slots)))))
555 (dolist (parent (ls-object-parents x) (nreverse result))
556 (dolist (slot (get-proto-slot-value parent 'instance-slots))
557 (pushnew slot result)))))
559 (defun get-initial-proto-slot-value (object slot)
560 (let ((entry (find-slot object slot)))
561 (if (slot-entry-p entry) (slot-entry-value entry))))
563 (defun make-prototype (object name ivars cvars doc set)
564 (setf ivars (find-instance-slots object ivars))
565 (add-slot object 'instance-slots ivars)
566 (add-slot object 'proto-name name)
567 (dolist (slot ivars)
568 (add-slot object slot (get-initial-proto-slot-value object slot)))
569 (dolist (slot cvars)
570 (add-slot object slot nil))
572 (if (and doc (stringp doc))
573 (add-documentation object 'proto doc))
574 (if set (setf (symbol-value name) object)))
577 (defmacro defproto (name &optional ivars cvars parents doc)
578 "Syntax (defproto name &optional ivars cvars (parent *object*) doc)
579 Makes a new object prototype with instantiated/set variables IVARS,
580 'class' variables CVARS (class or 'cleared'??)) and parents
581 PARENT. PARENT can be a single object or a list of objects. IVARS and
582 CVARS must be lists."
583 (let ((obsym (gensym))
584 (namesym (gensym))
585 (parsym (gensym)))
586 `(progn
587 (let* ((,namesym ',name)
588 (,parsym ,parents)
589 (,obsym (make-basic-object (if (listp ,parsym)
590 ,parsym
591 (list ,parsym)) ;; should this be ,@parsym ?
592 nil)))
593 (make-prototype ,obsym ,namesym ,ivars ,cvars ,doc t)
594 ,namesym))))
597 ;; Infrastructure for new defproto from Common-Lisp Cookbook! Thanks!
599 ;(defmacro odd-define (name buildargs)
600 ; `(progn (defun ,(build-symbol make-a- (:< name))
601 ; ,buildargs
602 ; (vector ,(length buildargs) ',name ,@buildargs))
603 ; (defun ,(build-symbol test-whether- (:< name)) (x)
604 ; (and (vectorp x) (eq (aref x 1) ',name))
605 ; (defun ,(build-symbol (:< name) -copy) (x)
606 ; ...)
607 ; (defun ,(build-symbol (:< name) -deactivate) (x)
608 ; ...))))
610 ;(defmacro for (listspec exp)
611 ; (cond ((and (= (length listspec) 3)
612 ; (symbolp (car listspec))
613 ; (eq (cadr listspec) ':in))
614 ; `(mapcar (lambda (,(car listspec))
615 ; ,exp)
616 ; ,(caddr listspec)))
617 ; (t (error "Ill-formed: ~s" `(for ,listspec ,exp)))))
619 ;(defmacro symstuff (l)
620 ; `(concatenate 'string
621 ; ,@(for (x :in l)
622 ; (cond ((stringp x)
623 ; `',x)
624 ; ((atom x)
625 ; `',(format nil "~a" x))
626 ; ((eq (car x) ':<)
627 ; `(format nil "~a" ,(cadr x)))
628 ; ((eq (car x) ':++)
629 ; `(format nil "~a" (incf ,(cadr x))))
630 ; (t
631 ; `(format nil "~a" ,x))))))
633 ;(defmacro build-symbol (&rest l)
634 ; (let ((p (find-if (lambda (x)
635 ; (and (consp x)
636 ; (eq (car x) ':package)))
637 ; l)))
638 ; (cond (p
639 ; (setq l (remove p l))))
640 ; (let ((pkg (cond ((eq (cadr p) 'nil)
641 ; nil)
642 ; (t `(find-package ',(cadr p))))))
643 ; (cond (p
644 ; (cond (pkg
645 ; `(values (intern ,(symstuff l) ,pkg)))
646 ; (t
647 ; `(make-symbol ,(symstuff l)))))
648 ; (t
649 ; `(values (intern ,(symstuff l))))))))
651 (defmacro defproto2 (name &optional ivars cvars parents doc)
652 "Syntax (defproto name &optional ivars cvars (parent *object*) doc)
653 Makes a new object prototype with instance variables IVARS, 'class'
654 variables CVARS and parents PARENT. PARENT can be a single object or
655 a list of objects. IVARS and CVARS must be lists."
656 (if (boundp name)
657 (error "can not rebind a prototype object yet")
658 (let ((namesym (gensym))
659 (obsym (gensym))
660 (parsym (gensym)))
661 `(progn
662 (let* ((,namesym ,name)
663 (,parsym ,parents)
664 (,obsym (make-basic-object
665 (if (listp ,parsym)
666 ,parsym
667 (list ,@parsym)) ;; should this be ,@parsym ?
668 nil)))
669 (make-prototype ,obsym ,name ,ivars ,cvars ,doc t)
670 ,name)))))
672 ;; recall:
673 ;; , => turn on evaluation again (not macro substitution)
674 ;; ` => template comes (use , to undo template and restore eval
675 ;; ' => regular quote (not special in this context), 'ted => (quote ted)
678 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
680 ;;; Initialize the Root Object
682 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
684 (setf (ls-object-preclist *object*) (list *object*))
685 (add-slot *object* 'instance-slots nil)
686 (add-slot *object* 'proto-name '*object*)
687 (add-slot *object* 'documentation nil)
689 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
691 ;;; *OBJECT* Methods
693 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
695 (defmeth *object* :nop (&rest args)
696 "Method args: ()
698 Do a NOP. Used to quiet compiler warnings."
699 (format nil "NOP"))
701 (defmeth *object* :isnew (&rest args)
702 "Method args: (&rest args)
703 Checks ARGS for keyword arguments matching slots and uses them to
704 initialize slots."
705 (if args
706 (dolist (slot-entry (ls-object-slots self))
707 (let* ((slot (slot-entry-key slot-entry))
708 (key (intern (symbol-name slot) (find-package 'keyword)))
709 (val (proto-slot-value slot))
710 (new-val (getf args key val)))
711 (unless (eq val new-val) (setf (proto-slot-value slot) new-val)))))
712 self)
714 (defmeth *object* :has-slot (slot &key own)
715 "Method args: (slot &optional own)
716 Returns T if slot SLOT exists, NIL if not. If OWN is not NIL
717 only checks the object; otherwise check the entire precedence list."
718 (let ((entry (if own (find-own-slot self slot) (find-slot self slot))))
719 (if entry t nil)))
721 (defmeth *object* :add-slot (slot &optional value)
722 "Method args: (slot &optional value)
723 Installs slot SLOT in object, if it does not already exist, and
724 sets its value to VLAUE."
725 (add-slot self slot value)
726 value)
728 (defmeth *object* :delete-slot (slot)
729 "Method args: (slot)
730 Deletes slot SLOT from object if it exists."
731 (delete-slot self slot)
732 nil)
734 (defmeth *object* :own-slots ()
735 "Method args: ()
736 Returns list of names of slots owned by object."
737 (mapcar #'slot-entry-key (ls-object-slots self)))
739 (defmeth *object* :has-method (selector &key own)
740 "Method args: (selector &optional own)
741 Returns T if method for SELECTOR exists, NIL if not. If OWN is not NIL
742 only checks the object; otherwise check the entire precedence list."
743 (let ((entry (if own
744 (find-own-method self selector)
745 (find-lsos-method self selector))))
746 (if entry t nil)))
748 (defmeth *object* :add-method (selector method)
749 "Method args: (selector method)
750 Installs METHOD for SELECTOR in object."
751 (add-lsos-method self selector method)
752 nil)
754 (defmeth *object* :delete-method (selector)
755 "Method args: (selector)
756 Deletes method for SELECTOR in object if it exists."
757 (delete-method self selector)
758 nil)
760 (defmeth *object* :get-method (selector)
761 "Method args: (selector)
762 Returns method for SELECTOR symbol from object's precedence list."
763 (get-message-method self selector))
765 (defmeth *object* :own-methods ()
766 "Method args ()
767 Returns copy of selectors for methods owned by object."
768 (mapcar #'method-entry-key (ls-object-methods self)))
770 (defmeth *object* :parents ()
771 "Method args: ()
772 Returns copy of parents list."
773 (copy-list (ls-object-parents self)))
775 (defmeth *object* :precedence-list ()
776 "Method args: ()
777 Returns copy of the precedence list."
778 (copy-list (ls-object-preclist self)))
780 (defmeth *object* :show (&optional (stream t))
781 "Method Args: ()
782 Prints object's internal data."
783 (format stream "Slots = ~s~%" (ls-object-slots self))
784 (format stream "Methods = ~s~%" (ls-object-methods self))
785 (format stream "Parents = ~s~%" (ls-object-parents self))
786 (format stream "Precedence List = ~s~%" (ls-object-preclist self))
787 nil)
789 (defmeth *object* :reparent (&rest parents)
790 "Method args: (&rest parents)
791 Changes precedence list to correspond to PARENTS. Does not change descendants."
792 (make-basic-object parents self))
794 (defmeth *object* :make-prototype (name &optional ivars)
795 (make-prototype self name ivars nil nil nil)
796 self)
798 (defmeth *object* :internal-doc (sym &optional new)
799 "Method args (topic &optional value)
800 Retrieves or installs documentation for topic."
801 (if new (add-documentation self sym new))
802 (get-documentation self sym))
804 (defmeth *object* :new (&rest args)
805 "Method args: (&rest args)
806 Creates new object using self as prototype."
807 (let* ((object (make-object self)))
808 (if (proto-slot-value 'instance-slots)
809 (dolist (s (proto-slot-value 'instance-slots))
810 (send object :add-slot s (proto-slot-value s))))
811 (apply #'send object :isnew args)
812 object))
814 (defmeth *object* :retype (proto &rest args)
815 "Method args: (proto &rest args)
816 Changes object to inherit directly from prototype PROTO. PROTO
817 must be a prototype and SELF must not be one."
818 (if (send self :has-slot 'instance-slots :own t)
819 (error "can't retype a prototype"))
820 (if (not (send proto :has-slot 'instance-slots :own t))
821 (error "not a prototype - ~a" proto))
822 (send self :reparent proto)
823 (dolist (s (send proto :proto-slot-value 'instance-slots))
824 (send self :add-slot s (proto-slot-value s)))
825 (apply #'send self :isnew args)
826 self)
828 (defmeth *object* :print (&optional (stream *standard-output*))
829 "Method args: (&optional (stream *standard-output*))
830 Default object printing method."
831 (cond
832 ((send self :has-slot 'proto-name)
833 (format stream
834 "#<Object: ~D, prototype = ~A>"
835 (ls-object-serial self)
836 (proto-slot-value 'proto-name)))
837 (t (format stream "#<Object: ~D>" (ls-object-serial self)))))
839 (defmeth *object* :proto-slot-value (sym &optional (val nil set))
840 "Method args: (sym &optional val)
842 Sets and retrieves value of slot named SYM. Signals an error if slot
843 does not exist."
844 (send self :nop)
845 (if set (setf (proto-slot-value sym) val))
846 (proto-slot-value sym))
848 (defmeth *object* :slot-names ()
849 "Method args: ()
850 Returns list of slots available to the object."
851 (apply #'append
852 (mapcar #'(lambda (x) (send x :own-slots))
853 (send self :precedence-list))))
855 (defmeth *object* :method-selectors ()
856 "Method args: ()
857 Returns list of method selectors available to object."
858 (apply #'append
859 (mapcar #'(lambda (x) (send x :own-methods))
860 (send self :precedence-list))))
863 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
864 ;;;;
865 ;;;; Object Help Methods
866 ;;;;
867 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
869 (defmeth *object* :doc-topics ()
870 "Method args: ()
871 Returns all topics with documentation for this object."
872 (remove-duplicates
873 (mapcar #'car
874 (apply #'append
875 (mapcar
876 #'(lambda (x)
877 (if (send x :has-slot 'documentation :own t)
878 (send x :proto-slot-value (quote documentation))))
879 (send self :precedence-list))))))
881 (defmeth *object* :documentation (topic &optional (val nil set))
882 "Method args: (topic &optional val)
883 Retrieves or sets object documentation for topic."
884 (if set (send self :internal-doc topic val))
885 (let ((val (dolist (i (send self :precedence-list))
886 (let ((val (send i :internal-doc topic)))
887 (if val (return val))))))
888 val))
890 (defmeth *object* :delete-documentation (topic)
891 "Method args: (topic)
892 Deletes object documentation for TOPIC."
893 (setf (proto-slot-value 'documentation)
894 ;;(remove :title nil :test #'(lambda (x y) (eql x (first y)))) ;; original
895 (remove topic (send self :documentation) :test #'(lambda (x y) (eql x (first y))))) ;; AJR:PROBLEM?
896 nil)
898 (defmeth *object* :help (&optional topic)
899 "Method args: (&optional topic)
900 Prints help message for TOPIC, or genreal help if TOPIC is NIL."
901 (if topic
902 (let ((doc (send self :documentation topic)))
903 (cond
904 (doc (princ topic) (terpri) (princ doc) (terpri))
905 (t (format t "Sorry, no help available on ~a~%" topic))))
906 (let ((topics (stable-sort (copy-seq (send self :doc-topics))
907 #'(lambda (x y)
908 (string-lessp (string x) (string y)))))
909 (proto-doc (send self :documentation 'proto)))
910 (if (send self :has-slot 'proto-name)
911 (format t "~s~%" (proto-slot-value 'proto-name)))
912 (when proto-doc (princ proto-doc) (terpri))
913 (format t "Help is available on the following:~%~%")
914 (dolist (i topics) (format t "~s " i))
915 (terpri)))
916 (values))
918 (defmeth *object* :compile-method (name)
919 "Method args: (name)
920 Compiles method NAME unless it is already compiled. The object must
921 own the method."
922 (unless (send self :has-method name)
923 (error "No ~s method in this object" name))
924 (unless (send self :has-method name :own t)
925 (error "Object does not own ~s method" name))
926 (let ((fun (send self :get-method name)))
927 (unless (compiled-function-p fun)
928 (multiple-value-bind (form env) (function-lambda-expression fun)
929 (if env
930 (error
931 "method may have been defined in non-null environment"))
932 (send self :add-method name (compile nil form))))))