[comp.sources.unix] v10i082: Common Objects, Common Loops, Common Lisp, Part08/13

rs@uunet.UU.NET (Rich Salz) (08/03/87)

Submitted-by: Roy D'Souza <dsouza%hplabsc@hplabs.HP.COM>
Posting-number: Volume 10, Issue 82
Archive-name: comobj.lisp/Part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 13)."
# Contents:  class-prot.l low.l
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'class-prot.l' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'class-prot.l'\"
else
echo shar: Extracting \"'class-prot.l'\" \(26632 characters\)
sed "s/^X//" >'class-prot.l' <<'END_OF_FILE'
X;;;-*-Mode:LISP; Package:(PCL Lisp 1000); Base:10; Syntax:Common-lisp -*-
X;;;
X;;; *************************************************************************
X;;; Copyright (c) 1985 Xerox Corporation.  All rights reserved.
X;;;
X;;; Use and copying of this software and preparation of derivative works
X;;; based upon this software are permitted.  Any distribution of this
X;;; software or derivative works must comply with all applicable United
X;;; States export control laws.
X;;; 
X;;; This software is made available AS IS, and Xerox Corporation makes no
X;;; warranty about the software, its performance or its conformity to any
X;;; specification.
X;;; 
X;;; Any person obtaining a copy of this software is requested to send their
X;;; name and post office or electronic mail address to:
X;;;   CommonLoops Coordinator
X;;;   Xerox Artifical Intelligence Systems
X;;;   2400 Hanover St.
X;;;   Palo Alto, CA 94303
X;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
X;;;
X;;; Suggestions, comments and requests for improvements are also welcome.
X;;; *************************************************************************
X;;;
X
X(in-package 'pcl)
X
X;;;
X;;; ADD-NAMED-CLASS  proto-class name local-supers local-slot-slotds extra
X;;; protocol: class-definition
X;;;
X;;; Creates or updates the definition of a class with a named class.  If
X;;; there is already a class named name, calls class-for-redefinition to
X;;; find out which class to use for the redefinition.  Once it has a class
X;;; object to use it stores the relevant information from the ds-options in
X;;; the class and calls add-class to add the class to the class
X;;; lattice.
X;;; 
X(defmeth add-named-class ((proto-class basic-class) name
X						    local-supers
X						    local-slot-slotds
X						    extra)
X  ;; First find out if there is already a class with this name.
X  ;; If there is, call class-for-redefinition to get the class
X  ;; object to use for the new definition.  If there is no exisiting
X  ;; class we just make a new instance.
X  (let* ((existing (class-named name t))
X	 (class (if existing
X		    (class-for-redefinition existing proto-class name 
X					    local-supers local-slot-slotds
X					    extra)
X		    (make (class-of proto-class)))))
X
X    (setq local-supers
X	  (mapcar
X	    #'(lambda (ls)
X		(or (class-named ls t)
X		    (error "~S was specified as the name of a local-super~%~
X                            for the class named ~S.  But there is no class~%~
X                            class named ~S." ls name ls)))
X	    local-supers))
X    
X    (setf (class-name class) name)
X    (setf (class-ds-options class) extra)	;This is NOT part of the
X						;standard protocol.
X   
X    (add-class class local-supers local-slot-slotds extra)
X    
X    (setf (class-named name) class)
X    name))
X
X(defmeth add-class
X	 ((class essential-class) new-local-supers new-local-slots extra)
X  (ignore extra)
X  (let ((old-local-supers (class-local-supers class))
X	(old-local-slots (class-local-slots class)))
X    
X    (setf (class-local-supers class) new-local-supers)
X    (setf (class-local-slots class) new-local-slots)
X
X    (if (and old-local-supers			;*** YUCH!! There is a bug
X	     new-local-supers			;*** when old and new are ()
X	     (equal old-local-supers new-local-supers))
X	(if (and old-local-slots
X		 new-local-slots
X		 (equal old-local-slots new-local-slots))
X	    ;; If the supers haven't changed, and the slots haven't changed
X	    ;; then not much has changed and we don't have to do anything.
X	    ()
X	    ;; If only the slots have changed call slots-changed.
X	    (slots-changed class old-local-slots extra t))
X	;; If the supers have changed, first update local-supers and
X	;; direct-subclasses of all the people involved.  Then call
X	;; supers-changed.
X	(progn
X	  (dolist (nls new-local-supers)
X	    (unless (memq nls old-local-supers)
X	      (check-super-metaclass-compatibility class nls)
X	      (push class (class-direct-subclasses nls))))
X	  (dolist (ols old-local-supers)
X	    (unless (memq ols new-local-supers)
X	      (setf (class-direct-subclasses ols)
X		    (delq class (class-direct-subclasses ols)))))
X	  (supers-changed class old-local-supers old-local-slots extra t)))))
X
X
X(defmeth supers-changed ((class basic-class)
X			 old-local-supers
X			 old-local-slots
X			 extra
X			 top-p)
X  (ignore old-local-slots)
X  (let ((cpl (compute-class-precedence-list class)))
X    (setf (class-class-precedence-list class) cpl)
X    (update-slots--class class cpl)		         ;This is NOT part of
X						         ;the essential-class
X						         ;protocol.
X    (dolist (sub-class (class-direct-subclasses class))
X      (supers-changed sub-class
X		      (class-local-supers sub-class)
X		      (class-local-slots sub-class)
X		      extra
X		      nil))
X    (when top-p                                          ;This is NOT part of
X      (update-method-inheritance class old-local-supers));the essential-class
X						         ;protocol.
X    ))
X
X(defmeth slots-changed ((class basic-class)
X			old-local-slots
X			extra
X			top-p)
X  (ignore top-p old-local-slots)
X  ;; When this is called, class should have its local-supers and
X  ;; local-slots slots filled in properly.
X  (update-slots--class class (class-class-precedence-list class))
X  (dolist (sub-class (class-direct-subclasses class))
X    (slots-changed sub-class (class-local-slots sub-class) extra nil)))
X
X(defun update-slots--class (class cpl)
X  (let ((obsolete-class nil))
X    (multiple-value-bind (instance-slots non-instance-slots)
X	(collect-slotds class (class-local-slots class) cpl)
X      ;; If there is a change in the shape of the instances then the
X      ;; old class is now obsolete.  Make a copy of it, then fill
X      ;; ourselves in properly and obsolete it.
X      (when (and (class-has-instances-p class)
X		 (not (same-shape-slots-p (class-instance-slots class)
X					  instance-slots)))
X	(setq obsolete-class (copy-class class)))
X      (setf (class-no-of-instance-slots class) (length instance-slots))
X      (setf (class-instance-slots class) instance-slots)
X      (setf (class-non-instance-slots class) non-instance-slots)
X      (when obsolete-class
X	(flush-class-caches class)
X	(make-class-obsolete class (copy-class class))))))
X
X;;;
X;;; CLASS-FOR-REDEFINITION old-class proto-class name ds-options slotds
X;;; protocol: class definition
X;;; 
X;;; When a class is being defined, and a class with that name already exists
X;;; a decision must be made as to what to use for the new class object, and
X;;; whether to update the old class object.  For this, class-for-redefinition
X;;; is called with the old class object, the prototype of the new class, and
X;;; the name ds-options and slotds corresponding to the new definition.
X;;; It should return the class object to use as the new definition.  It is
X;;; OK for this to be old-class if that is appropriate.
X;;; 
X(defmeth class-for-redefinition ((old-class essential-class)
X				 proto-class
X				 name
X				 local-supers
X				 local-slot-slotds
X				 extra)
X  (ignore local-supers local-slot-slotds extra)
X  (cond ((not (compatible-meta-class-change-p old-class proto-class))
X	 (error "The class ~A already exists; its class is ~A.~%~
X		 The :class argument in the defstruct is ~A.
X		 This is an incompatible meta-class change.~%"
X		name
X		(class-name (class-of old-class))
X		(class-name (class-of proto-class))))
X	(t (values old-class (copy-class old-class)))))
X
X(defmeth update-method-inheritance ((class basic-class) old-local-supers)
X  ;; In the absence of method combination, we have to flush all the
X  ;; discriminators which we used to inherit and all the discriminators
X  ;; which we now inherit.
X  (let ((old-mil
X	  (compute-method-inheritance-list class old-local-supers))
X	(new-mil
X	  (compute-method-inheritance-list class
X					   (class-local-supers class)))
X	(discriminators ())
X	(combined-discriminators ()))
X    (dolist (old-donor old-mil)
X      (when (setq discriminators (class-direct-discriminators old-donor))
X	(dolist (old-discriminator discriminators)	  
X	  (flush-discriminator-caches old-discriminator)
X	  (when (methods-combine-p old-discriminator)
X	    (pushnew old-discriminator combined-discriminators)))))
X    (dolist (new-donor new-mil)
X      (when (setq discriminators (class-direct-discriminators new-donor))
X	(unless (memq new-donor old-mil)
X	  (dolist (new-discriminator discriminators)
X	    (when (methods-combine-p new-discriminator)
X	      (pushnew new-discriminator combined-discriminators))
X	    (flush-discriminator-caches new-discriminator)))))
X    (when (fboundp 'combine-methods)		         ;***
X      (COMBINE-METHODS CLASS COMBINED-DISCRIMINATORS)))) ;***
X
X
X(defmeth discriminator-changed ((discriminator essential-discriminator)
X				method
X				added-p)
X  (ignore method added-p)
X  (make-discriminating-function discriminator)
X  (flush-discriminator-caches discriminator))
X
X
X(defun make-class-obsolete (class obsolete-class)
X  (setf (class-wrapper-class (class-wrapper obsolete-class)) obsolete-class)
X  (setf (class-wrapper class) nil)
X  (setf (class-local-supers obsolete-class) (list class))
X  (setf (class-class-precedence-list obsolete-class)
X        (cons obsolete-class (class-class-precedence-list class)))
X  (setf (class-name obsolete-class)
X	(symbol-append "obsolete-" (class-name class)))
X  (setf (iwmc-class-class-wrapper obsolete-class)
X        (wrapper-of (class-named 'obsolete-class)))
X  obsolete-class)
X
X(defun copy-class (class) 
X  (let* ((no-of-instance-slots (class-no-of-instance-slots (class-of class)))
X         (new-class (%allocate-instance--class no-of-instance-slots)))
X    (setf (iwmc-class-class-wrapper new-class)
X	  (iwmc-class-class-wrapper class))
X    (iterate ((i from 0 below no-of-instance-slots))
X      (let ((index (%convert-slotd-position-to-slot-index i)))
X	(setf (get-static-slot--class new-class index)			
X	      (get-static-slot--class class index))))
X    (setf (iwmc-class-dynamic-slots new-class)
X          (copy-list (iwmc-class-dynamic-slots class)))
X    new-class))
X
X(defun wrapper-of (class)
X  (or (class-wrapper class)
X      (setf (class-wrapper class) (make-class-wrapper class))))
X
X(defmeth collect-slotds ((class basic-class) local-slots cpl)
X  (let ((slots ()))
X    (flet ((add-slotd (slotd)
X	     (let ((entry
X		     (or (assq (slotd-name slotd) slots)
X			 (progn (push (list (slotd-name slotd)) slots)
X				(car slots)))))
X	       (push slotd (cdr entry)))))
X      (dolist (super (reverse (cdr cpl)))	;fix this consing later
X	(dolist (super-slotd (class-local-slots super))
X	  (add-slotd super-slotd)))
X
X      (dolist (local-slotd local-slots)
X	(add-slotd local-slotd)))
X      
X    ;; Now use compute-effective-slotd to condense all the
X    ;; inherited slotds into the one effective slotd.
X    (dolist (slot slots)
X      (setf (car slot)
X	    (compute-effective-slotd class (cdr slot))))
X    ;; Now we need to separate it back out into instance and non-instance
X    ;; slots.
X    (let ((instance ())
X	  (non-instance ()))
X      (dolist (slot slots)
X	(setq slot (car slot))
X	(if (eq (slotd-allocation slot) ':instance)
X	    (push slot instance)
X	    (push slot non-instance)))
X      (values instance non-instance slots))))
X
X(defmethod compute-effective-slotd ((class class) slotds)
X  (ignore class)
X  (let ((slotd  (if (null (cdr slotds))
X		    (car slotds)
X		    (copy-slotd (car slotds)))))
X    (flet ((merge-values (default type read-only accessor allocation)
X	     (macrolet ((merge-value (name value)
X			  `(when (eq (,name slotd) *slotd-unsupplied*)
X			     (setf (,name slotd) ,value))))
X	       (merge-value slotd-default default)
X	       (merge-value slotd-type type)
X	       (merge-value slotd-read-only read-only)
X	       (merge-value slotd-accessor accessor)
X	       (merge-value slotd-allocation allocation))))
X      (dolist (s (cdr slotds))
X	(merge-values (slotd-default s)
X		      (slotd-type s)
X		      (slotd-read-only s)
X		      (slotd-accessor s)
X		      (slotd-allocation s)))
X      (merge-values 'nil      ;default value -- for now
X		    't        ;type
X		    'nil      ;read-only
X		    'nil      ;accessor
X		  :instance)) ;allocation
X	slotd))
X
X(defmethod compute-class-precedence-list ((root class))
X  #+Lucid (declare (optimize (speed 0) (safety 3)))
X  (let ((*cpl* ())
X	(*root* root)
X	(*must-precede-alist* ()))
X    (declare (special *cpl* *root* *must-precede-alist*))
X    ;; We start by computing two values.
X    ;;   CPL
X    ;;     The depth-first left-to-right up to joins walk of the supers tree.
X    ;;     This is equivalent to breadth-first left-to-right walk of the
X    ;;     tree with all but the last occurence of a class removed from
X    ;;     the resulting list.  This is in fact how the walk is implemented.
X    ;;
X    ;;   MUST-PRECEDE-ALIST
X    ;;     An alist of the must-precede relations. The car of each element
X    ;;     of the must-precede-alist is a class, the cdr is all the classes
X    ;;     which either:
X    ;;       have this class as a local super
X    ;;      or
X    ;;       appear before this class in some other class's local-supers.
X    ;;
X    ;;     Thus, the must-precede-alist reflects the two constraints that:
X    ;;       1. A class must appear in the CPL before its local supers.
X    ;;       2. Order of local supers is preserved in the CPL.
X    ;;
X    (labels
X   ;(flet
X       (
X;	(walk-supers (class &optional precedence)
X;	  (let ((elem (assq class must-precede-alist)))
X;	    (if elem
X;		(setf (cdr elem) (union (cdr elem) precedence))
X;		(push (cons class precedence) must-precede-alist)))
X;	  (let ((rsupers (reverse (cons class (class-local-supers class)))))
X;	    (iterate ((sup in rsupers)
X;		      (pre on (cdr rsupers))
X;		      (temp = nil))
X;	      ;; Make sure this element of supers is OK.
X;	      ;;  Actually, there is an important design decision hidden in
X;	      ;;  here.  Namely, at what time should symbols in a class's
X;	      ;;  local-supers be changed to the class objects they are
X;	      ;;  forward referencing.
X;	      ;;   1. At first make-instance (compute-class-precedence-list)?
X;	      ;;   2. When the forward referenced class is first defined?
X;	      ;;  This code does #1.
X;	      (cond ((classp sup))
X;		    ((and (symbolp sup)
X;			  (setq temp (class-named sup t)))
X;		     ;; This is a forward reference to a class which is
X;		     ;; now defined.  Replace the symbol in the local
X;		     ;; supers with the actual class object, and set sup.
X;		     (nsubst temp sup (class-local-supers class))
X;		     (setq sup temp))
X;		    ((symbolp sup)
X;		     (error "While computing the class-precedence-list for ~
X;                             the class ~S.~%~
X;                             The class ~S (from the local supers of ~S) ~
X;                             is undefined."
X;			    (class-name root) sup (class-name class)))
X;		    (t
X;		     (error "INTERNAL ERROR --~%~
X;                             While computing the class-precedence-list for ~
X;                             the class ~S,~%~
X;                             ~S appeared in the local supers of ~S."
X;			    root sup class)))
X;	      (walk-supers sup pre))
X;	    (unless (memq class cpl) (push class cpl))))
X	(must-move-p (element list &aux move)
X	  (dolist (must-precede (cdr (assq element *must-precede-alist*)))
X	    (when (setq move (memq must-precede (cdr list)))
X	      (return move))))
X	(find-farthest-move (element move)
X	  (let ((closure (compute-must-precedes-closure element)))
X	    (dolist (must-precede closure)
X	      (setq move (or (memq must-precede move) move)))
X	    move))
X	(compute-must-precedes-closure (class)
X	  (let ((closure ()))
X	    (labels ((walk (element path)
X		       (when (memq element path)
X			 (class-ordering-error
X			   *root* element path *must-precede-alist*))
X		       (dolist (precede
X				 (cdr (assq element
X					    *must-precede-alist*)))
X			 (unless (memq precede closure)
X			   (pushnew precede closure)
X			   (walk precede (cons element path))))))
X	      (walk class nil)
X	      closure))))
X      
X      (walk-supers *root*)			;Do the walk
X      ;; For each class in the cpl, make sure that there are no classes after
X      ;; it which should be before it.  We do this by cdring down the list,
X      ;; making sure that for each element of the list, none of its
X      ;; must-precedes come after it in the list. If we find one, we use the
X      ;; transitive closure of the must-precedes (call find-farthest-move) to
X      ;; see where the class must really be moved. We use a hand-coded loop
X      ;; so that we can splice things in and out of the CPL as we go.
X      (let ((tail *cpl*)
X	    (element nil)
X	    (move nil))
X	(loop (when (null tail) (return))
X	      (setq element (car tail)
X		    move (must-move-p element tail))
X	      (cond (move
X		     (setq move (find-farthest-move element move))
X		     (setf (cdr move) (cons element (cdr move)))
X		     (setf (car tail) (cadr tail)) ;Interlisp delete is OK
X		     (setf (cdr tail) (cddr tail)) ;since it will never be
X						   ;last element of list.
X		     )
X		    (t
X		     (setq tail (cdr tail)))))
X	(copy-list *cpl*)))))
X
X(defun walk-supers (class &optional precedence)
X  (declare (special *cpl* *root* *must-precede-alist*))
X  (let ((elem (assq class *must-precede-alist*)))
X    (if elem
X	(setf (cdr elem) (union (cdr elem) precedence))
X	(push (cons class precedence) *must-precede-alist*)))
X  (let ((rsupers (reverse (cons class (class-local-supers class)))))
X    (iterate ((sup in rsupers)
X	      (pre on (cdr rsupers))
X	      (temp = nil))
X      ;; Make sure this element of supers is OK.
X      ;;  Actually, there is an important design decision hidden in
X      ;;  here.  Namely, at what time should symbols in a class's
X      ;;  local-supers be changed to the class objects they are
X      ;;  forward referencing.
X      ;;   1. At first make-instance (compute-class-precedence-list)?
X      ;;   2. When the forward referenced class is first defined?
X      ;;  This code does #1.
X      (cond ((classp sup))
X	    ((and (symbolp sup)
X		  (setq temp (class-named sup t)))
X	     ;; This is a forward reference to a class which is
X	     ;; now defined.  Replace the symbol in the local
X	     ;; supers with the actual class object, and set sup.
X	     (nsubst temp sup (class-local-supers class))
X	     (setq sup temp))
X	    ((symbolp sup)
X	     (error "While computing the class-precedence-list for ~
X                             the class ~S.~%~
X                             The class ~S (from the local supers of ~S) ~
X                             is undefined."
X		    (class-name *root*) sup (class-name class)))
X	    (t
X	     (error "INTERNAL ERROR --~%~
X                             While computing the class-precedence-list for ~
X                             the class ~S,~%~
X                             ~S appeared in the local supers of ~S."
X		    *root* sup class)))
X      (walk-supers sup pre))
X    (unless (memq class *cpl*) (push class *cpl*))))
X
X(defun class-ordering-error (root element path must-precede-alist)
X  (ignore root)
X  (setq path (cons element (reverse (memq element (reverse path)))))
X  (flet ((pretty (class) (or (class-name class) class)))
X    (let ((explanations ()))
X      (do ((tail path (cdr tail)))
X	  ((null (cdr tail)))
X	(let ((after (cadr tail))
X	      (before (car tail)))
X	  (if (memq after (class-local-supers before))
X	      (push (format nil
X			    "~% ~A must precede ~A -- ~
X                              ~A is in the local supers of ~A."
X			    (pretty before) (pretty after)
X			    (pretty after) (pretty before))
X		    explanations)
X	      (dolist (common-precede
X			(intersection
X			  (cdr (assq after must-precede-alist))
X			  (cdr (assq before must-precede-alist))))
X		(when (memq after (memq before
X					(class-local-supers common-precede)))
X		  (push (format nil
X				"~% ~A must precede ~A -- ~
X                                  ~A has local supers ~S."
X				(pretty before) (pretty after)
X				(pretty common-precede)
X				(mapcar #'pretty
X					(class-local-supers common-precede)))
X			explanations))))))
X      (error "While computing the class-precedence-list for the class ~A:~%~
X              There is a circular constraint through the classes:~{ ~A~}.~%~
X              This arises because:~{~A~}"
X	     (pretty root)
X	     (mapcar #'pretty path)
X	     (reverse explanations)))))
X
X(defmeth compute-method-inheritance-list ((class essential-class)
X					  local-supers)
X  (compute-class-precedence-list class))
X
X(defmeth compatible-meta-class-change-p (class proto-new-class)
X  (eq (class-of class) (class-of proto-new-class)))
X
X(defmeth check-super-metaclass-compatibility (class new-super)
X  (unless (eq (class-of class) (class-of new-super))
X    (error "The class ~S was specified as a~%super-class of the class ~S;~%~
X            but the meta-classes ~S and~%~S are incompatible."
X	   new-super class (class-of new-super) (class-of class))))
X
X(defun classp (x)
X  (and (iwmc-class-p x) (typep--class x 'essential-class)))
X
X
X
X(defmeth class-standard-constructor ((class basic-class))
X  (dolist (constructor (ds-options-constructors (class-ds-options class)))
X    (when (null (cdr constructor)) (return (car constructor)))))
X
X
X(defmeth flush-class-caches ((class basic-class))
X  (let ((wrapper (class-wrapper class)))
X    (and wrapper (flush-class-wrapper-cache wrapper))
X    (iterate ((subclass in (class-direct-subclasses class)))
X      (flush-class-caches subclass))))
X
X
X  ;;   
X;;;;;; CHANGE-CLASS
X  ;;   
X
X(defun change-class (object new-class)
X  (or (classp new-class)
X      (setq new-class (class-named new-class)))
X  (let ((new-object (make new-class)))
X    ;; Call change-class-internal so that a user-defined method
X    ;; (or the default method) can copy the information from the
X    ;; old instance to the dummy instance of the new class.
X    (change-class-internal object new-object)
X    ;; Now that the dummy new-object has the right information,
X    ;; move all that stuff into the old-instance.
X    (setf (iwmc-class-class-wrapper object)
X	  (wrapper-of new-class))
X    (setf (iwmc-class-static-slots object)
X	  (iwmc-class-static-slots new-object))
X    (setf (iwmc-class-dynamic-slots object)
X	  (iwmc-class-dynamic-slots new-object))
X    object))
X
X(defmeth change-class-internal ((old object) (new object))
X  (let ((all-slots (all-slots old)))
X    (iterate ((name in all-slots by cddr)
X              (value in (cdr all-slots) by cddr))
X      (put-slot-always new name value))))
X
X  ;;   
X;;;;;; WITH-SLOTS
X  ;;
X
X(define-method-body-macro with-slots (instance-forms-and-options
X				       &body body
X				       &environment env)
X  :global (expand-with-slots nil nil instance-forms-and-options env body)
X  :method (expand-with-slots (macroexpand-time-generic-function
X			       macroexpand-time-environment)
X			     (macroexpand-time-method
X			       macroexpand-time-environment)
X			     instance-forms-and-options
X			     env
X			     body))
X
X(defun expand-with-slots (proto-discriminator proto-method first-arg env body)
X  (ignore proto-discriminator)
X  (setq first-arg (iterate ((arg in first-arg))
X		    (collect (if (listp arg) arg (list arg)))))
X  (let ((entries (expand-with-make-entries proto-method first-arg))
X	(gensyms ()))
X    (dolist (arg first-arg)
X      (push (list (if (listp arg) (car arg) arg)
X		  (gensym))
X	    gensyms))
X    `(let ,(mapcar #'reverse gensyms)
X       ,(walk-form (cons 'progn body)
X	  :environment env
X	  :walk-function
X	  #'(lambda (form context &aux temp)
X	      (cond ((and (symbolp form)
X			  (eq context ':eval)
X			  (null (variable-lexical-p form))
X			  (null (variable-special-p form))
X			  (setq temp (assq form entries)))
X		     (if (car (cddddr temp))	;use slot-value?
X			 (let ((get-slot 
X				 `(get-slot ,(cadr (assq (cadr temp) gensyms))
X					    ',(slotd-name (cadddr temp)))))
X			   (optimize-get-slot (caddr temp)
X					      get-slot))
X			 `(,(slotd-accessor (cadddr temp))
X			   ,(cadr (assq (cadr temp) gensyms)))))
X		    ((and (listp form)
X			  (or (eq (car form) 'setq)
X			      (eq (car form) 'setf)))
X		     (cond ((cdddr form)
X			    (cons 'progn
X				  (iterate ((pair on (cdr form) by cddr))
X				    (collect (list (car form)
X						   (car pair)
X						   (cadr pair))))))
X			   ((setq temp (assq (cadr form) entries))
X			    (if (car (cddddr temp))
X				(let ((get-slot 
X					`(setf-of-get-slot
X					   ,(cadr (assq (cadr temp) gensyms))
X					   ',(slotd-name (cadddr temp))
X					   ,(caddr form))))
X				  (optimize-setf-of-get-slot (caddr temp)
X							     get-slot))
X				`(setf (,(slotd-accessor (cadddr temp))
X					,(cadr (assq (cadr temp) gensyms)))
X				       ,(caddr form))))
X			   (t form)))
X		    (t form)))))))
X
X;;; Returns an alist of the form:
X;;; 
X;;;   (<prefix+slot-name> <instance-form> <class> <slotd> <use-slot-value-p>)
X;;;
X(defmeth expand-with-make-entries (method first-arg)
X  (let* ((entries ())
X         (method-arguments
X	   (when (method-p method)
X	     (iterate ((arg in (method-arglist method))
X		       (spec in (method-type-specifiers method)))
X	       (when (classp spec) (collect (cons arg spec)))))))
X    (iterate ((instance-and-keys in first-arg))
X      (keyword-bind ((use-slot-value nil)
X		     (class nil class-specified-p)
X		     (prefix nil prefix-specified-p))
X		    (cdr instance-and-keys)
X	(let ((instance (car instance-and-keys)))
X	  (setq class
X		(or (and class-specified-p
X			 (or (class-named class t)
X			     (error "In WITH-SLOTS the class specified for ~
X                                     ~S, ~S ~%~
X                                     is not the name of a defined class."
X				    instance class)))
X		    (cdr (assq instance method-arguments))
X		    (error "The class of (the value of) ~S was not given in ~
X                           in the call to with-slots and could not be ~
X                           inferred automatically."
X			  instance)))
X	  (iterate ((slotd in (class-slots class)))
X	    (push (list (if (null prefix-specified-p)
X			    (slotd-name slotd)
X			    (intern (string-append prefix
X						   (slotd-name slotd))
X				    (symbol-package
X				      (if (symbolp prefix)
X					  prefix
X					  (slotd-name slotd)))))
X			instance
X			class
X			slotd
X			use-slot-value)
X		  entries)))))
X    entries))
X
X
X(defun named-object-print-function (instance stream depth
X					     &optional (extra nil extra-p))
X  (ignore depth)
X  (printing-random-thing (instance stream)
X    ;; I know I don't have to do this this way.  I know I
X    ;; could use ~[~;~], but how many Common Lisps do you
X    ;; think have that completely debugged?
X    (if extra-p					
X	(format stream "~A ~S ~:S"
X		(capitalize-words (class-name (class-of instance)))
X		(get-slot instance 'name)
X		extra)
X	(format stream "~A ~S"
X		(capitalize-words (class-name (class-of instance)))
X		(get-slot instance 'name)))))
X
END_OF_FILE
if test 26632 -ne `wc -c <'class-prot.l'`; then
    echo shar: \"'class-prot.l'\" unpacked with wrong size!
fi
# end of 'class-prot.l'
fi
if test -f 'low.l' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'low.l'\"
else
echo shar: Extracting \"'low.l'\" \(27849 characters\)
sed "s/^X//" >'low.l' <<'END_OF_FILE'
X;;;-*-Mode:LISP; Package:(PCL (LISP WALKER) 1000); Base:10; Syntax:Common-lisp -*-
X;;;
X;;; *************************************************************************
X;;; Copyright (c) 1985 Xerox Corporation.  All rights reserved.
X;;;
X;;; Use and copying of this software and preparation of derivative works
X;;; based upon this software are permitted.  Any distribution of this
X;;; software or derivative works must comply with all applicable United
X;;; States export control laws.
X;;; 
X;;; This software is made available AS IS, and Xerox Corporation makes no
X;;; warranty about the software, its performance or its conformity to any
X;;; specification.
X;;; 
X;;; Any person obtaining a copy of this software is requested to send their
X;;; name and post office or electronic mail address to:
X;;;   CommonLoops Coordinator
X;;;   Xerox Artifical Intelligence Systems
X;;;   2400 Hanover St.
X;;;   Palo Alto, CA 94303
X;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
X;;;
X;;; Suggestions, comments and requests for improvements are also welcome.
X;;; *************************************************************************
X;;;
X;;; This file contains portable versions of low-level functions and macros
X;;; which are ripe for implementation specific customization.  None of the
X;;; code in this file *has* to be customized for a particular Common Lisp
X;;; implementation. Moreover, in some implementations it may not make any
X;;; sense to customize some of this code.
X;;;
X;;; But, experience suggests that MOST Common Lisp implementors will want
X;;; to customize some of the code in this file to make PCL run better in
X;;; their implementation.  The code in this file has been separated and
X;;; heavily commented to make that easier.
X;;;
X;;; Implementation-specific version of this file already exist for:
X;;; 
X;;;    Symbolics 3600 family       3600-low.lisp
X;;;    Lucid Lisp                  lucid-low.lisp
X;;;    Xerox 1100 family           1100-low.lisp
X;;;    Ti Explorer                 ti-low.lisp
X;;;    Vaxlisp                     vaxl-low.lisp
X;;;    Spice Lisp                  spice-low.lisp
X;;;    Kyoto Common Lisp           kcl-low.lisp
X;;;    ExCL (Franz)                excl-low.lisp
X;;;    H.P. Common Lisp            hp-low.lisp
X;;;    
X;;;
X;;; These implementation-specific files are loaded after this file.  Because
X;;; none of the macros defined by this file are used in functions defined by
X;;; this file the implementation-specific files can just contain the parts of
X;;; this file they want to change.  They don't have to copy this whole file
X;;; and then change the parts they want.
X;;;
X;;; If you make changes or improvements to these files, or if you need some
X;;; low-level part of PCL re-modularized to make it more portable to your
X;;; system please send mail to CommonLoops.pa@Xerox.com.
X;;;
X;;; Thanks.
X;;; 
X
X(in-package 'pcl)
X
X  ;;   
X;;;;;; without-interrupts
X  ;;   
X;;; OK, Common Lisp doesn't have this and for good reason.  But For all of
X;;; the Common Lisp's that PCL runs on today, there is a meaningful way to
X;;; implement this.  WHAT I MEAN IS:
X;;;
X;;; I want the body to be evaluated in such a way that no other code that is
X;;; running PCL can be run during that evaluation.  I agree that the body
X;;; won't take *long* to evaluate.  That is to say that I will only use
X;;; without interrupts around small computations.
X;;;
X;;; OK?
X;;;
X(defmacro without-interrupts (&body body)
X  `(progn ,.body))
X
X  ;;   
X;;;;;; Load Time Eval
X  ;;
X;;;
X;;; #, is woefully inadequate.  You can't use it inside of a macro and have
X;;; the expansion of part of the macro be evaluated at load-time.
X;;;
X;;; load-time-eval is used to provide an interface to implementation
X;;; dependent implementation of load time evaluation.
X;;;
X;;; A compiled call to load-time-eval:
X;;;   should evaluated the form at load time,
X;;;   but if it is being compiled-to-core evaluate it at compile time
X;;; Interpreted calls to load-time-eval:
X;;;   should just evaluate form at run-time.
X;;; 
X;;; The portable implementation just evaluates it every time, and PCL knows
X;;; this.  PCL is careful to only use load-time-eval in places where (except
X;;; for performance penalty) it is OK to evaluate the form every time.
X;;; 
X(defmacro load-time-eval (form)
X  `(progn ,form))
X
X  ;;   
X;;;;;; Memory Blocks (array-like blocks of memory)
X  ;;
X;;; The portable implementation of memory-blocks is as arrays.
X;;;
X;;; The area argument to make-memory-block is based on the area feature of
X;;; LispM's.  As it is used in PCL that argument will always be an unquoted
X;;; symbol.  So a call to make-memory-block will look like:
X;;;     (make-memory-block 100 class-wrapper-area)
X;;; This allows any particular implementation of make-memory-block to look at
X;;; the symbol at compile time (macroexpand time) and know where the memory-
X;;; block should be consed.  Currently the only values ever used as the area
X;;; argument are:
X;;; 
X;;;    CLASS-WRAPPER-AREA        used when making a class-wrapper
X;;;
X;;; NOTE:
X;;;     It is perfectly legitimate for an implementation of make-memory-block
X;;;     to ignore the area argument.  It only exists to try to improve paging
X;;;     performance in systems which do allow control over where memory is
X;;;     allocated.
X;;; 
X(defmacro make-memory-block (size &optional area)
X  (ignore area)
X  `(make-array ,size :initial-element nil))
X
X(defmacro memory-block-size (block)
X  `(array-dimension ,block 0))
X
X(defmacro memory-block-ref (block offset)
X  `(svref ,block ,offset))
X
X(eval-when (compile load eval)
X
X(defun make-memory-block-mask (size &optional (words-per-entry 2))
X  (logxor (1- (expt 2 (floor (log size 2))))
X	  (1- (expt 2 (ceiling (log words-per-entry 2))))))
X
X)
X
X;;;
X;;; clear-memory-block sets all the slots of a memory block to nil starting
X;;; at start.  This really shouldn't be a macro, it should be a function.
X;;; It has to be a macro because otherwise its call to memory-block-ref will
X;;; get compiled before people get a chance to change memory-block-ref.
X;;; This argues one of:
X;;;  - this should be a function in another file.  No, it belongs here.
X;;;  - Common Lisp should have defsubst.  Probably
X;;;  - Implementors should take (proclaim '(inline xxx)) more seriously.
X;;;  
X(defmacro clear-memory-block (block start &optional times)
X  (once-only (block)
X    `(do ((end ,(if times `(+ ,start ,times) `(length ,block)))
X	  (index ,start (+ index 1)))
X	 ((= index end))
X       (setf (memory-block-ref ,block index) nil))))
X
X  ;;   
X;;;;;; CLASS-OF
X  ;;
X;;;
X;;; *class-of* is the lisp code for the definition of class-of.
X;;;
X;;; This version uses type-of to determine the class of an object.  Because
X;;; of the underspecification of type-of, this does not always produce the
X;;; "most specific class of which x is an instance".  But it is the best I
X;;; can do portably.
X;;;
X;;; Specific ports of PCL should feel free to redefine *class-of* to provide
X;;; a more accurate definition.  At some point in any definition of class-of
X;;; there should be a test to determine if the argument is a %instance, and
X;;; if so the %instance-class-of macro should be used to determine the class
X;;; of the instance.
X;;;
X;;; Whenever a new meta-class is defined, the portable code will take care of
X;;; modifying the definition of %instance-class-of and recompiling class-of.
X;;;
X(defvar *class-of*
X	'(lambda (x) 
X	   (or (and (%instancep x)
X		    (%instance-class-of x))
X	      ;(%funcallable-instance-p x)
X	       (class-named (type-of x) t)
X	       (error "Can't determine class of ~S" x))))
X
X(defvar *meta-classes* ())
X
X(defmacro %instance-class-of (arg)
X  `(cond ,@(iterate ((mc in *meta-classes*))
X	     (collect
X	       `((eq (%instance-meta-class ,arg)
X		     ;; %^&$%& KCL has to have this stupid call to
X		     ;; load-time-eval here because their compiler
X		     ;; always creates a file and compiles that file.
X		     #-KCL',(class-named (car mc))
X		     #+KCL (load-time-eval (class-named ',(car mc))))
X		 (funcall (function ,(cdr mc)) ,arg))))
X	 (t
X	  (error
X	    "Internal error in %INSTANCE-CLASS-OF.  The argument to~%~
X             %instance-class-of is a %instance, but its meta-class is~%~
X             not one of the meta-classes defined with define-meta-class."
X	    (%instance-meta-class ,arg)))))
X
X(defmacro define-meta-class (name class-of-function &rest options)
X  (check-type name symbol "a symbol which is the name of a meta-class")
X  (check-type class-of-function function "a function")  
X  `(load-define-meta-class ',name ',class-of-function))
X
X(defun load-define-meta-class (name class-of-function)
X  (or (eq name 'class)
X      (class-named name t)
X      (error "In define-meta-class, there is no class named ~S.~%~
X              The class ~S must be defined before evaluating this~%~
X              define-meta-class form."))
X  (let ((existing (assq name *meta-classes*)))
X    (if existing
X	(setf (cdr existing) class-of-function)
X	(setq *meta-classes* (nconc *meta-classes*
X				    (list (cons name class-of-function)))))
X    (recompile-class-of)))
X
X(defun recompile-class-of ()
X    ;; Change the definition of class-of so that the next time it is
X    ;; called it will recompile itself.
X    ;; NOTE:  This does not have to be written this way.  If we impose
X    ;;        the constraint that any define-meta-class must be loaded
X    ;;        in the same environment as it was compiled then there is
X    ;;        no need for a compiler at run or load time.
X    ;;        By same environment I mean with the same define-meta-class
X    ;;        forms already in force, and this certainly seems like a
X    ;;        reasonable constraint to me.
X    (setf (symbol-function 'class-of)
X	  #'(lambda (x)
X	      (declare (notinline class-of))
X	      ;; Now recompile class-of so that the new definition
X	      ;; of %instance-class-of will take effect.
X	      (compile 'class-of *class-of*)
X	      (class-of x))))
X
X  ;;
X;;;;;; TYPEP and TYPE-OF support.
X  ;;
X;;; Portable CommonLoops makes no changes to typep or type-of.  In order for
X;;; those functions to work with CommonLoops objects each implementation will
X;;; have to fix its typep and type-of.  It shouldn't be hard though, and
X;;; these macros should help.
X
X(defmacro %instance-typep (x type)
X  `(not (null (memq (class-named ,type ())
X                    (class-class-precedence-list (class-of ,x))))))
X
X(defmacro %instance-type-of (x)
X  `(class-name (class-of ,x)))
X
X  ;;   
X;;;;;; The primitive instances.
X  ;;
X;;;
X;;; Conceptually, a %instance is an array-like datatype whose first element
X;;; points to the meta-class of the %instance and whose remaining elements
X;;; are used by the meta-class for whatever purpose it wants.
X;;;
X;;; What would like to do is use defstruct to define a new type with a
X;;; variable number of slots.  Unfortunately, Common Lisp itself does not
X;;; let us do that.  So we have to define a new type %instance, and have
X;;; it point to an array which is the extra slots.
X;;;
X;;; Most any port of PCL should re-implement this datatype.  Implementing it
X;;; as a variable length type so that %instance are only one vector in memory
X;;; (the "extra slots" are in-line with the meta-class) will have significant
X;;; impact on the speed of many CommonLoops programs.  As an example of how
X;;; to do this re-implementation of %instance, please see the file 3600-low.
X;;; 
X
X(defstruct (%instance (:print-function print-instance)
X		      (:constructor %make-instance-1 (meta-class storage))
X		      (:predicate %instancep))
X  meta-class
X  storage)
X
X(defmacro %make-instance (meta-class size)
X  `(%make-instance-1 ,meta-class (make-array ,size)))
X
X(defmacro %instance-ref (instance index)
X  `(aref (%instance-storage ,instance) ,index))
X
X(defun print-instance (instance stream depth) ;This is a temporary definition
X  (ignore depth)			      ;used mostly for debugging the
X  (printing-random-thing (instance stream)    ;bootstrapping code.
X    (format stream "instance ??")))
X
X  ;;
X;;;;;;  Very Low-Level representation of instances with meta-class class.
X  ;;
X;;; As shown below, an instance with meta-class class (iwmc-class) is a three
X;;; *slot* structure.
X;;;   
X;;; 
X;;;                                             /------["Class"]
X;;;                  /-------["Class Wrapper"  /  <slot-and-method-cache>]
X;;;                 /
X;;;  Instance--> [ / , \  ,  \ ]
X;;;                     \     \
X;;;                      \     \---[Instance Slot Storage Block]
X;;;                       \
X;;;                        \-------[Dynamic Slot plist]
X;;;
X;;; Instances with meta-class class point to their class indirectly through
X;;; the class's class wrapper (each class has one class wrapper, not each
X;;; instance).  This is done so that all the extant instances of a class can
X;;; have the class they point to changed quickly.  See change-class.
X;;;
X;;; Static-slots are a 1-d-array-like structure.
X;;; The default PCL implementation is as a memory block as described above.
X;;; Particular ports are free to change this to a lower-level block of memory
X;;; type structure. Once again, the accessor for static-slots storage doesn't
X;;; need to do bounds checking, and static-slots structures don't need to be
X;;; able to change size.  This is because new slots are added using the
X;;; dynamic slot mechanism, and if the class changes or the class of the
X;;; instance changes a new static-slot structure is allocated (if needed).
X;;
X;;; Dynamic-slots are a plist-like structure.
X;;; The default PCL implementation is as a plist.
X;;;
X;;; *** Put a real discussion here of where things should be consed.
X;;;  - if all the class wrappers in the world are on the same page that
X;;;    would be good because during method lookup we only use the wrappers
X;;;    not the classes and once a slot is cached, we only use the wrappers
X;;;    too.  So a page of just wrappers would stay around all the time and
X;;;    you would never have to page in the classes at least in "tight" loops.
X;;;
X
X(defmacro iwmc-class-p (x)
X  `(and (%instancep ,x)
X	(eq (%instance-meta-class ,x)
X	    (load-time-eval (class-named 'class)))))
X
X;(defmacro %allocate-iwmc-class ()
X;  `(%make-instance (load-time-eval (class-named 'class)) 3))
X
X(defmacro iwmc-class-class-wrapper (iwmc-class)
X  `(%instance-ref ,iwmc-class 0))
X
X(defmacro iwmc-class-static-slots (iwmc-class)
X  `(%instance-ref ,iwmc-class 1))
X
X(defmacro iwmc-class-dynamic-slots (iwmc-class)
X  `(%instance-ref ,iwmc-class 2))
X
X
X(defmacro %allocate-instance--class (no-of-slots &optional class-class)
X  `(let ((iwmc-class
X	   (%make-instance ,(or class-class
X				'(load-time-eval (class-named 'class)))
X			   3)))
X     (%allocate-instance--class-1 ,no-of-slots iwmc-class)
X     iwmc-class))
X
X(defmacro %allocate-instance--class-1 (no-of-slots instance)
X  (once-only (instance)
X    `(progn 
X       (setf (iwmc-class-static-slots ,instance)
X	     (%allocate-static-slot-storage--class ,no-of-slots))
X       (setf (iwmc-class-dynamic-slots ,instance)
X	     (%allocate-dynamic-slot-storage--class)))))
X
X
X(defmacro %allocate-class-class (no-of-slots)	;This is used to allocate the
X  `(let ((i (%make-instance nil 3)))		;class class.  It bootstraps
X     (setf (%instance-meta-class i) i)		;the call to class-named in
X     (setf (class-named 'class) i)		;%allocate-instance--class.
X     (%allocate-instance--class-1 ,no-of-slots i)
X     i))
X
X(defmacro %convert-slotd-position-to-slot-index (slotd-position)
X  slotd-position)
X
X
X(defmacro %allocate-static-slot-storage--class (no-of-slots)
X  `(make-memory-block ,no-of-slots))
X
X(defmacro %static-slot-storage-get-slot--class (static-slot-storage
X						slot-index)
X  `(memory-block-ref ,static-slot-storage ,slot-index))
X
X(defmacro %allocate-dynamic-slot-storage--class ()
X  ())
X
X(defmacro %dynamic-slot-storage-get-slot--class (dynamic-slot-storage
X						 name
X						 default)
X  `(getf ,dynamic-slot-storage ,name ,default))
X
X(defmacro %dynamic-slot-storage-remove-slot--class (dynamic-slot-storage
X						    name)
X  `(remf ,dynamic-slot-storage ,name))
X
X
X
X(defmacro class-of--class (iwmc-class)
X  `(class-wrapper-class (iwmc-class-class-wrapper ,iwmc-class)))
X
X(define-meta-class class (lambda (x) (class-of--class x)))
X
X
X  ;;   
X;;;;;; Class Wrappers  (the Watercourse Way algorithm)
X  ;;
X;;; Well, we had this really cool scheme for keeping multiple different
X;;; caches tables in the same block of memory.  Unfortunately, we only
X;;; cache one thing in class wrappers these days, and soon class wrappers
X;;; will go away entirely so its kind of lost generality.  I am leaving
X;;; the old comment here cause the hack is worth remembering.
X;;;
X;;; * Old Comment
X;;; * The key point are:
X;;; *
X;;; *  - No value in the cache can be a key for anything else stored
X;;; *    in the cache.
X;;; *
X;;; *  - When we invalidate a wrapper cache, we flush it so that when
X;;; *    it is next touched it will get a miss.
X;;; *
X;;; * A class wrapper is a block of memory whose first two slots have a
X;;; * deadicated (I just can't help myself) purpose and whose remaining
X;;; * slots are the shared cache table.  A class wrapper looks like:
X;;; *
X;;; *  slot 0:   <pointer to class>
X;;; *  slot 1:   T if wrapper is valid, NIL otherwise.
X;;; *   .
X;;; *   .          shared cache
X;;; *   .
X;;;
X
X(eval-when (compile load eval)
X
X(defconstant class-wrapper-cache-size 32
X  "The size of class-wrapper caches.")
X
X(defconstant class-wrapper-leader 2
X  "The number of slots at the beginning of a class wrapper which have a
X   special purpose.  These are the slots that are not part of the cache.")
X
X; due to a compiler bug, the extra "2" default argument has been added
X; to the following function invocation, for HP Lisp. rds 3/6/87
X(defconstant class-wrapper-cache-mask 
X	     (make-memory-block-mask class-wrapper-cache-size 2))
X
X)
X
X(defmacro make-class-wrapper (class)
X  `(let ((wrapper (make-memory-block ,(+ class-wrapper-cache-size
X					 class-wrapper-leader)
X				     class-wrapper-area)))
X     (setf (class-wrapper-class wrapper) ,class)
X     (setf (class-wrapper-valid-p wrapper) t)
X     wrapper))
X
X(defmacro class-wrapper-class (class-wrapper)
X  `(memory-block-ref ,class-wrapper 0))
X
X(defmacro class-wrapper-valid-p (class-wrapper)
X  `(memory-block-ref ,class-wrapper 1))
X
X(defmacro class-wrapper-cached-key (class-wrapper offset)
X  `(memory-block-ref ,class-wrapper ,offset))
X
X(defmacro class-wrapper-cached-val (class-wrapper offset)
X  `(memory-block-ref ,class-wrapper (+ ,offset 1)))
X
X(defmacro class-wrapper-get-slot-offset (class-wrapper slot-name)
X  (ignore class-wrapper)
X  `(+ class-wrapper-leader
X      0
X      (symbol-cache-no ,slot-name ,class-wrapper-cache-mask)))
X
X
X(defmacro flush-class-wrapper-cache (class-wrapper)
X  `(clear-memory-block ,class-wrapper
X		       ,class-wrapper-leader
X		       ,class-wrapper-cache-size))
X
X(defmacro class-wrapper-cache-cache-entry (wrapper offset key val)
X  (once-only (wrapper offset key val)
X    `(without-interrupts
X       (setf (class-wrapper-cached-key ,wrapper ,offset) ,key)	 ;store key
X       (setf (class-wrapper-cached-val ,wrapper ,offset) ,val))));store value
X
X(defmacro class-wrapper-cache-cached-entry (wrapper offset key)
X  (once-only (wrapper offset)
X    `(and (eq (class-wrapper-cached-key ,wrapper ,offset) ,key)
X	  (class-wrapper-cached-val ,wrapper ,offset))))
X
X(defmacro invalidate-class-wrapper (wrapper)
X  (once-only (wrapper)
X    `(progn (flush-class-wrapper-cache ,wrapper)
X	    (setf (class-wrapper-valid-p ,wrapper) nil))))
X
X(defmacro validate-class-wrapper (iwmc-class)	          ;HAS to be a macro!
X  `(let ((wrapper (iwmc-class-class-wrapper ,iwmc-class)));So that xxx-low
X     (if (class-wrapper-valid-p wrapper)	          ;can redefine the
X	 wrapper				          ;macros we use.
X	 (progn (setf (iwmc-class-class-wrapper ,iwmc-class)
X		      (class-wrapper (class-wrapper-class wrapper)))
X		(setf (class-wrapper-valid-p wrapper) t)))))
X
X  ;;   
X;;;;;; Generating CACHE numbers
X  ;;
X;;; These macros should produce a CACHE number for their first argument
X;;; masked to fit in their second argument.  A useful cache number is just
X;;; the symbol or object's memory address.  The memory address can either
X;;; be masked to fit the mask or folded down with xor to fit in the mask.
X;;; See some of the other low files for examples of how to implement these
X;;; macros. Except for their illustrative value, the portable versions of
X;;; these macros are nearly worthless.  Any port of CommonLoops really
X;;; should redefine these to be faster and produce more useful numbers.
X
X(defvar *warned-about-symbol-cache-no* nil)
X(defvar *warned-about-object-cache-no* nil)
X
X(defmacro symbol-cache-no (symbol mask)
X  (unless *warned-about-symbol-cache-no*
X    (setq *warned-about-symbol-cache-no* t)
X    (warn
X      "Compiling PCL without having defined an implementation-specific~%~
X       version of SYMBOL-CACHE-NO.  This is likely to have a significant~%~
X       effect on slot-access performance.~%~
X       See the definition of symbol-cache-no in the file low to get an~%~
X       idea of how to implement symbol-cache-no."))
X  `(logand (sxhash ,symbol) ,mask))
X
X(defmacro object-cache-no (object mask)
X  (ignore object)
X  (unless *warned-about-object-cache-no*
X    (setq *warned-about-object-cache-no* t)
X    (warn
X      "Compiling PCL without having defined an implementation-specific~%~
X       version of OBJECT-CACHE-NO.  This effectively disables method.~%~
X       lookup caching.  See the definition of object-cache-no in the file~%~
X       low to get an idea of how to implement object-cache-no."))
X  `(logand 0 ,mask))
X
X
X  ;;   
X;;;;;; FUNCTION-ARGLIST
X  ;;
X;;; Given something which is functionp, function-arglist should return the
X;;; argument list for it.  PCL does not count on having this available, but
X;;; MAKE-SPECIALIZABLE works much better if it is available.  Versions of
X;;; function-arglist for each specific port of pcl should be put in the
X;;; appropriate xxx-low file. This is what it should look like:
X;(defun function-arglist (function)
X;  (<system-dependent-arglist-function> function))
X
X(defun function-pretty-arglist (function)
X  (ignore function)
X  ())
X
X(defsetf function-pretty-arglist set-function-pretty-arglist)
X
X(defun set-function-pretty-arglist (function new-value)
X  (ignore function)
X  new-value)
X
X
X
X  ;;   
X;;;;;; Templated functions
X  ;;   
X;;; In CommonLoops there are many program-generated functions which
X;;; differ from other, similar program-generated functions only in the
X;;; values of certain in-line constants.
X;;;
X;;; A prototypical example is the family of discriminating functions used by
X;;; classical discriminators.  For all classical discriminators which have
X;;; the same number of required arguments and no &rest argument, the
X;;; discriminating function is the same, except for the value of the
X;;; "in-line" constants (the cache and discriminator).
X;;;
X;;; Naively, whenever we want one of these functions we have to produce and
X;;; compile separate lambda. But this is very expensive, instead what we
X;;; would like to do is copy the existing compiled code and replace the
X;;; values of the inline constants with the right new values.
X;;;
X;;; Templated functions provide a nice interface to this abstraction of
X;;; copying an existing compiled function and replacing certain constants
X;;; with others.  Templated functions are based on the assumption that for
X;;; any given CommonLisp one of the following is true:
X;;;   Either:
X;;;     Funcalling a lexical closure is fast, and lexical variable access
X;;;     is as fast (or about as fast) in-line constant access.  In this
X;;;     case we implement templated functions as lexical closures closed
X;;;     over the constants we want to change from one instance of the
X;;;     templated function to another.
X;;;   Or:
X;;;     Code can be written to take a compiled code object, copy it and
X;;;     replace references to certain in-line constants with references
X;;;     to other in-line constants.
X;;;
X;;; Actually, I believe that for most Lisp both of the above assumptions are
X;;; true.  For certain lisps the explicit copy and replace scheme *may be*
X;;; more efficient but the lexical closure scheme is completely portable and
X;;; is likely to be more efficient since the lexical closure it returns are
X;;; likely to share compiled code objects and only have separate lexical
X;;; environments.
X;;;
X;;; Another thing to notice about templated functions is that they provide
X;;; the modularity to support special objects which a particular
X;;; implementation's low-level function-calling code might know about.   As
X;;; an example, when a classical discriminating function is created, the
X;;; code says "make a classical discriminating function with 1 required
X;;; arguments". It then uses whatever comes back from the templated function
X;;; code as the the discriminating function So, a particular port can easily
X;;; make this return any sort of special data structure instead of one of
X;;; the lexical closures the portable implementation returns.
X;;;
X(defvar *templated-function-types* ())
X(defmacro define-function-template (name
X				    template-parameters
X				    instance-parameters
X				    &body body)
X  `(progn
X     (pushnew ',name *templated-function-types*)
X     ;; Get rid of all the cached constructors.
X     (setf (get ',name 'templated-fn-constructors) ())
X     ;; Now define the constructor constructor.
X     (setf (get ',name 'templated-fn-params)
X	   (list* ',template-parameters ',instance-parameters ',body))
X     (setf (get ',name 'templated-fn-constructor-constructor)
X	   ,(make-templated-function-constructor-constructor
X	      template-parameters instance-parameters body))))
X
X(defun reset-templated-function-types ()
X  (dolist (type *templated-function-types*)
X    (setf (get type 'templated-fn-constructors) ())))
X
X(defun get-templated-function-constructor (name &rest template-parameters)
X  (setq template-parameters (copy-list template-parameters)) ;Groan.
X  (let ((existing (assoc template-parameters
X			 (get name 'templated-fn-constructors)
X			 :test #'equal)))
X    (if existing
X	(progn (setf (nth 3 existing) t)	;Mark this constructor as
X						;having been used.
X	       (cadr existing))			;And return the actual
X						;constructor.
X	(let ((new-constructor
X		(apply (get name 'templated-fn-constructor-constructor)
X		       template-parameters)))
X	  (push (list template-parameters new-constructor 'made-on-the-fly t)
X		(get name 'templated-fn-constructors))
X	  new-constructor))))
X
X(defmacro pre-make-templated-function-constructor (name
X						   &rest template-parameters)
X  (setq template-parameters (copy-list template-parameters))	;Groan.
X  (let* ((params (get name 'templated-fn-params))
X	 (template-params (car params))
X	 (instance-params (cadr params))
X	 (body (cddr params))
X	 (dummy-fn-name (gensym)))   ;For the 3600, which doesn't bother to 
X				     ;compile top-level forms, we do the
X				     ;top-level form compilation by hand.
X    (progv template-params
X	   template-parameters
X      `(progn
X	 (defun ,dummy-fn-name ()
X	   (let ((entry
X		   (or (assoc ',template-parameters 
X			      (get ',name 'templated-fn-constructors)
X			      :test #'equal)
X		       (let ((new-entry
X			       (list ',template-parameters () () ())))
X			 (push new-entry
X			       (get ',name 'templated-fn-constructors))
X			 new-entry))))
X	     (setf (caddr entry) 'pre-made)
X	     (setf (cadr entry)
X		   (function (lambda ,(eval instance-params)
X			       ,(eval (cons 'progn body)))))))
X	 (,dummy-fn-name)))))
X
X(defun make-templated-function-constructor-constructor (template-params
X							instance-params
X							body)
X  `(function
X     (lambda ,template-params
X       (compile () (list 'lambda ,instance-params ,@body)))))
X
X  ;;   
X;;;;;; 
X  ;;   
X
X(defun record-definition (name type &rest args)
X  (ignore name type args)
X  ())
X
X(defun compile-time-define (&rest ignore)
X  (ignore ignore))
X
END_OF_FILE
if test 27849 -ne `wc -c <'low.l'`; then
    echo shar: \"'low.l'\" unpacked with wrong size!
fi
# end of 'low.l'
fi
echo shar: End of archive 8 \(of 13\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 13 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 

Rich $alz			"Anger is an energy"
Cronus Project, BBN Labs	rsalz@bbn.com
Moderator, comp.sources.unix	sources@uunet.uu.net