git.fiddlerwoaroof.com
kr/kr.lisp
6e35003d
 ;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: KR; Base: 10 -*-
 
 ;; The Garnet User Interface Development Environment
 ;;
 ;; This code was written as part of the Garnet project at Carnegie
 ;; Mellon University, and has been placed in the public domain.
 
 ;;; Slot bits assignment:
 ;; 0-9 types encoding
 ;;   - inherited
 ;;   - is-parent
 ;;   - constant-slot
 ;;   - is-update-slot
 ;;   - local-only-slot    ; not implemented
 ;;   - parameter-slot     ; not implemented
 
 (in-package :kr)
 
 (declaim (inline clear-one-slot))
 
 (defun clear-one-slot (schema slot entry)
   "Completely clear a slot, including dependencies, inherited, etc...
    BUT... Leave around the declarations (constant, type, update,...)"
   (locally (declare #.*special-kr-optimization*)
     (let ((the-entry (or entry (slot-accessor schema slot))))
       (when the-entry
 	(setf (sl-value the-entry) *no-value*
 	      (sl-bits the-entry)  (logand (sl-bits the-entry)
 					   *clear-slot-mask*))))))
 
 (declaim (inline clear-schema-slots))
 (defun clear-schema-slots (schema)
   "Completely clear ALL the slots in the <schema>."
   (locally (declare #.*special-kr-optimization*)
     (clrhash (schema-bins schema))))
 
 (defun value-fn (schema slot)
   "Does the actual work of G-VALUE."
   (g-value-body schema slot T T))
 
 (defun g-local-value-fn (schema slot)
   "Similar to g-value-fn, but no inheritance."
   (g-value-body schema slot NIL T))
 
 (let ((list-of-one (list nil)))
   (defun get-dependents (schema slot)
     "RETURNS: the formulas which depend on the <slot> of the <schema>."
     (let ((value (slot-dependents (slot-accessor schema slot))))
       (if (listp value)
 	  value
 	  (progn
 	    (setf (car list-of-one) value)
 	    list-of-one)))))
 
 (declaim (inline get-lambda))
 (defun get-lambda (formula)
   "Returns the lambda expression in a formula, or NIL."
   (when (formula-p formula)
     (a-formula-lambda formula)))
 
 (defun enable-a-demon (demon)
   "Turns ON a demon if it was turned off.  If all demons are currently
 disabled, the variable *demons-disabled* is made of the form
 (T demon), where the names following the T are, in fact, enabled."
   (cond ((eq *demons-disabled* T)
 	 (list T demon))
 	((eq *demons-disabled* NIL))	; nothing is disabled
 	((listp *demons-disabled*)
 	 ;; A list
 	 (if (eq (car *demons-disabled*) T)
 	     ;; Special format
 	     (if (memberq demon (cdr *demons-disabled*))
 		 *demons-disabled*	; nothing is needed
 		 (cons T (cons demon (cdr *demons-disabled*))))
 	     ;; Normal format
 	     (if (memberq demon *demons-disabled*)
 		 (remove demon *demons-disabled*)
 		 *demons-disabled*)))
 	((eq demon *demons-disabled*)
 	 NIL)
 	(t
 	 *demons-disabled*)))
 
 (defun disable-a-demon (demon)
   (if (eq *demons-disabled* T)
       T					; everything is already turned off
       (if (eq *demons-disabled* NIL)
 	  demon
 	  (if (listp *demons-disabled*)
 	      ;; A list
 	      (if (eq (car *demons-disabled*) T)
 		  ;; Special format used by with-demon-enable
 		  (if (memberq demon *demons-disabled*)
 		      (let ((new-value (delete demon *demons-disabled*)))
 			(if (null (cdr new-value))
 			    T
 			    new-value))
 		      ;; Already disabled
 		      *demons-disabled*)
 		  ;; Normal format
 		  (cons demon *demons-disabled*))
 	      ;; A single value - make a list.
 	      (list demon *demons-disabled*)))))
 
 
 (defun demon-is-disabled (demon)
   "Is the <demon> currently enabled?"
   (if (listp *demons-disabled*)
       (if (eq (car *demons-disabled*) T)
 	  ;; Special format
 	  (not (memberq demon (cdr *demons-disabled*)))
 	  ;; Normal format
 	  (memberq demon *demons-disabled*))
       (eq demon *demons-disabled*)))
 
 
 (defun g-value-inherit-values (schema slot is-leaf slot-structure)
   "Search up the tree for inherited slot.
 RETURNS: the inherited value, or NIL."
   (declare (ftype (function (t &optional t) t) formula-fn))
   (let (has-parents)
     (when (a-local-only-slot slot)	; These CANNOT be inherited.
       (return-from g-value-inherit-values NIL))
     (dolist (relation *inheritance-relations*)
       (dolist (parent (if (eq relation :IS-A)
 			  (get-local-value schema :IS-A)
 			  (get-local-value schema relation)))
 	(setf has-parents T)
 	(let ((entry (slot-accessor parent slot))
 	      (value *no-value*)
 	      bits			; parent bits
 	      (intermediate-constant NIL))
 	  (when entry
 	    (setf value (sl-value entry))
 	    (when (is-constant (sl-bits entry))
 	      (setf intermediate-constant T)))
 	  (if (eq value *no-value*)
 	      ;; Attempt to inherit from its ancestors.
 	      (multiple-value-setq (value bits)
 		(g-value-inherit-values parent slot NIL nil))
 	      ;; If value, just set bits.
 	      (setf bits (sl-bits entry)))
 	  (unless (eq value *no-value*)
 	    (if (and bits (is-parent bits))
 		;; Clear the parent bit, since we will set the child.
 		(setf bits (logand bits *not-parent-mask*))
 		;; Set the bit in the parent which says that the value was
 		;; inherited by someone.
 		(if entry
 		    ;; Destructively set the bits.
 		    (setf (sl-bits entry) (logior bits *is-parent-mask*))
 		    (set-slot-accessor parent slot value
 				       (logior bits *is-parent-mask*) nil)))
 	    ;; Copy the value down to the inheriting slot, unless the value
 	    ;; contains a formula.
 	    (let ((was-formula (formula-p value)))
 	      (when was-formula
 		;; Inherit the formula, making a copy of it.
 		(setf value (formula-fn value (a-formula-cached-value value)))
 		(setf (a-formula-schema value) schema)
 		(setf (a-formula-slot value) slot)
 		(set-cache-is-valid value NIL))
 	      ;; Copy down, mark as inherited if inherited
 	      (when (and is-leaf slot-structure)	; slot had constant bit
 		(setf bits (logior bits (sl-bits slot-structure))))
 	      (setf bits (logior *inherited-mask* bits
 				 #+TEST
 				 (logand bits *not-parent-constant-mask*)))
 	      (when intermediate-constant
 		(setf bits (logior *constant-mask* bits)))
 	      (set-slot-accessor schema slot value bits
 				 (slot-dependents slot-structure)))
 	    (return-from g-value-inherit-values (values value bits))))))
     ;; We didn't find anything, so return an appropriate null value and set
     ;; the local cache (even though we have no value) to avoid further
     ;; inheritance search.
     (set-slot-accessor schema slot
 		       (if has-parents NIL *no-value*)
 		       (cond (is-leaf
 			      (if slot-structure
 				  (logior *inherited-mask*
 					  (sl-bits slot-structure))
 				  *inherited-mask*))
 			     (has-parents *inherited-parent-mask*)
 			     (t		; top-level, no parents
 			      *is-parent-mask*))
 		       (slot-dependents slot-structure))
     *no-value*))
 
 ;; G-CACHED-VALUE
 ;;
 (declaim (inline g-cached-value))
 (defun g-cached-value (schema slot)
   "Returns the value of the <slot> in the <schema>.  If this is a formula, it
   returns the cached value of the formula, without ever recomputing the
   formula."
   ;; Note use of GET-VALUE
   (let ((g-cached-value-val (get-value schema slot)))
     (if (formula-p g-cached-value-val)
 	(cached-value g-cached-value-val)
 	g-cached-value-val)))
 
 
 (defun g-value-no-copy (schema slot &optional skip-local)
   "This is a specialized function which does inheritance but does NOT copy
 values down.  It is used by the :INITIALIZE method, which is called exactly
 once per object and should NOT copy down anything (since the method will
 never be used again)."
   (unless skip-local
     ;; Is there a local value?
     (let ((value (slot-accessor schema slot)))
       (when value
 	(return-from g-value-no-copy (sl-value value)))))
   ;; Now try inherited values.
   (dolist (relation *inheritance-relations*)
     (dolist (*schema-self* (if (eq relation :IS-A)
 			       (get-local-value schema :IS-A)
 			       (get-local-value schema relation)))
       (unless (eq *schema-self* schema)	; avoid infinite loops!
 	(let ((value (g-value-no-copy *schema-self* slot)))
 	  (when value
 	    (return-from g-value-no-copy value)))))))
 
 
 ;;; PRINTING AND DEBUGGING
 
 (declaim (fixnum *debug-names-length* *debug-index*))
 (defparameter *debug-names-length* 500)
 
 (defvar *debug-names* (make-array *debug-names-length* :initial-element nil))
 
 (defvar *debug-index* -1)
 
 (defvar *intern-unnamed-schemata* T
   "This variable may be set to NIL to prevent PS from automatically creating
   any unnamed schemata it prints out.")
 
 (defun cache-schema-name (schema name)
   "This does not cause any creation of symbols.  It simply records
 the schema in an array, thus creating a semi-permanent way to refer
 to a schema."
   (unless (find-if #'(lambda (x)
 		       (and x (eql (schema-name x) name)))
 		   *debug-names*)
     ;; A new schema.  Store it in the next position (cycle if
     ;; we reach the end of the array).
     (setf (aref *debug-names*
 		(setf *debug-index*
 		      (mod (incf *debug-index*) *debug-names-length*)))
 	  schema)))
 
 ;;
 (defun make-new-schema-name (schema name)
   "Creates symbols for all automatic schema names that happen to
 be printed out."
   (let* ((debug-package (find-package "KR-DEBUG"))
 	 parent
 	 (symbol
 	  (intern (cond ((stringp name)
 			 ;; a name-prefix schema
 			 (format nil "~A-~D"
 				 name (incf *schema-counter*)))
 			((setf parent
 			       (if (formula-p schema)
 				   (a-formula-is-a schema)
 				   (when (not-deleted-p schema)
 				     (car (get-local-value schema :IS-A)))))
 			 (let ((parent-name (when parent (schema-name parent))))
 			   (when (or (integerp parent-name)
 				     (stringp parent-name))
 			     ;; Parent is unnamed yet - force a name.
 			     (with-output-to-string
 				 (bit-bucket)
 			       (print-the-schema parent bit-bucket 0))
 			     (setf parent-name (schema-name parent)))
 			   (format nil "~A-~D" parent-name name)))
 			(t
 			 (format nil "~C~D"
 				 (if (formula-p schema) #\F #\S)
 				 name)))
 		  debug-package)))
     (set symbol schema)
     (setf (schema-name schema) symbol)
     (export symbol debug-package)))
 
 (defun print-the-slot (slot stream level)
   (declare (ignore level))
   (format stream "<slot ~S  value ~S, bits ~S"
 	  (sl-name slot) (sl-value slot) (sl-bits slot))
   (if (full-sl-p slot)
       (format stream ",  dependents ~S>" (full-sl-dependents slot))
       (format stream ">")))
 
 (defun print-the-schema (schema stream level)
   (declare (ignore level))
   (let ((name (schema-name schema))
 	(destroyed (not (not-deleted-p schema))))
     ;; This version is for debugging.  Record the latest schemata in the
     ;; array.
     (cond ((or (integerp name) (stringp name))
 	   ;; This is a nameless schema.  Print it out, and record it in the
 	   ;; debugging array.
 	   (when *intern-unnamed-schemata*
 	     (make-new-schema-name schema name))
 	   (cache-schema-name schema name)
 	   ;; This gives control over whether unnamed schemata are interned.
 	   (setf name (schema-name schema)))
 	  ((null name)
 	   ;; This was a deleted schema
 	   (setf name '*DESTROYED*)))
     (when destroyed (format stream "*DESTROYED*(was "))
     (if *print-as-structure*
 	(progn
 	  (format stream "#k<~S" name)
 	  (dolist (slot *print-structure-slots*)
 	    (let ((value (g-value schema slot)))
 	      (when value
 		(format stream " (~S ~S)" slot value))))
 	  (format stream ">")
 	  (when destroyed (format stream ")")))
 	(progn
 	  (format stream "~S" name)
 	  (when destroyed (format stream ")"))))))
 
 
 (defun name-for-schema (schema)
   "Given a schema, returns its printable name as a string.  The string
 CANNOT be destructively modified.
 Note that this returns the pure name, without the #k<> notation."
   (let ((name (schema-name schema)))
     (when (or (integerp name) (stringp name))
       ;; This is a nameless schema.  Print it out, and record it in the
       ;; debugging array.
       (when *intern-unnamed-schemata*
 	(make-new-schema-name schema name))
       (cache-schema-name schema name)
       ;; This gives control over whether unnamed schemata are interned.
       (setf name (schema-name schema)))
     (symbol-name name)))
 
 
 (defun s (number)
   "This is a debugging function which returns a schema, given its internal
 number.  It only works if the schema was printed out rather recently,
 i.e., if it is contained in the temporary array of names."
   (setf number (format nil "~D" number))
   (find-if #'(lambda (x)
 	       (and x
 		    (symbolp (schema-name x))
 		    (do* ((name (symbol-name (schema-name x)))
 			  (i (1- (length name)) (1- i))
 			  (j (1- (length number)) (1- j)))
 			 ((minusp j)
 			  (unless (digit-char-p (schar name i))
 			    x))
 		      (unless (char= (schar name i) (schar number j))
 			(return nil)))))
 	   *debug-names*))
 
 
 
 ;;; RELATIONS
 
 (defun unlink-one-value (schema slot value)	    ; e.g., child :is-a parent
   "Remove the inverse link from <value> to <schema>, following the inverse
 of <slot>."
   (let ((inverse (cadr (assocq slot *relations*)))) ; e.g., is-a-inv
     (when inverse
       ;; If the relation has an INVERSE slot, remove <schema> from the
       ;; inverse slot.
       (let ((entry (slot-accessor value inverse)) ; e.g., A child B
 	    values)
 	(when entry
 	  (setf values (sl-value entry))
 	  (if (eq (car values) schema)
 	      ;; <schema> is first in the inverse list
 	      (set-slot-accessor value inverse (delete schema values)
 				 (sl-bits entry) (slot-dependents entry))
 	      ;; just do a destructive operation
 	      (setf (cdr values) (delete schema (cdr values)))))))))
 
 (defun unlink-all-values (schema slot)
   "Same as above, but unlinks all schemata that are in <slot>."
   (let ((inverse (cadr (assocq slot *relations*))))
     (when inverse
       (let ((entry (if (eq slot :IS-A)
 		       (slot-accessor schema :IS-A)
 		       (if (eq slot :IS-A-INV)
 			   (slot-accessor schema :IS-A-INV)
 			   (slot-accessor schema slot)))))
 	(when entry
 	  (dolist (parent (sl-value entry))
 	    (when (not-deleted-p parent) ; parent is not destroyed
 	      ;; If the terminal has an INVERSE slot, remove <schema> from the
 	      ;; inverse slot.
 	      (let ((entry (if (eq inverse :is-a-inv)
 			       (slot-accessor parent :is-a-inv) ; e.g., A child B
 			       (slot-accessor parent inverse)))
 		    values)
 		(when entry
 		  (setf values (sl-value entry))
 		  (if (eq (car values) schema)
 		      (pop (sl-value entry))
 		      (setf (cdr values) (delete schema (cdr values)))))))))))))
 
 
 (defun link-in-relation (schema slot values)
   "Since the <values> are being added to <slot>, see if we need to put in an
 inverse link to <schema> from each of the <values>.
 This happens when <slot> is a relation with an inverse."
   (let ((inverse (if (eq slot :is-a)
 		     :is-a-inv
 		     (cadr (assocq slot *relations*)))))
     (when inverse
       ;; <values> is a list: cycle through them all
       (dolist (value values)
 	(let* ((entry (if (eq slot :is-a)
 			  (slot-accessor value :is-a-inv)
 			  (slot-accessor value inverse)))
 	       (previous-values (when entry (sl-value entry))))
 	  (if entry
 	      ;; Create the back-link.  We use primitives here to avoid looping.
 	      (if (or *schema-is-new*
 		      (not (memberq schema previous-values)))
 		  ;; Handle an important special case efficiently.
 		  (if (eq (sl-value entry) *no-value*)
 		      ;; There was no value after all!
 		      (setf (sl-value entry) (list schema))
 		      ;; There were real values.
 		      (push schema (sl-value entry))))
 	      ;; There was no inverse in the parent yet.
 	      (set-slot-accessor value inverse (list schema) *local-mask* nil)))))))
 
 
 (defun check-relation-slot (schema slot values)
   "We are setting the <slot> (a relation) to <values>.  Check that the
 latter contains valid relation entries.
 RETURNS: <values> (or a list of a single value, if <values> is not a list)
 if success; *no-value* if failure."
   (unless (listp values)
     (format
      t "S-VALUE: relation ~s in schema ~S should be given a list of values!~%"
      slot schema)
     (if (schema-p values)
 	(setf values (list values))	; go ahead, use anyway.
 	(return-from check-relation-slot *no-value*)))
   (dolist (value values)
     (unless (is-schema value)
       (when-debug
        (format
 	t
 	"S-VALUE: value ~s for relation ~s in ~s is not a schema!  Ignored.~%"
 	value slot schema))
       (return-from check-relation-slot *no-value*)))
   (do ((value values (cdr value)))
       ((null value))
     (when (memberq (car value) (cdr value))
       (format
        t
        "Trying to set relation slot ~S in schema ~S with duplicate value ~S!~%"
        slot schema (car value))
       (format t "  The slot was not set.~%")
       (return-from check-relation-slot *no-value*)))
   values)
 
 
 (declaim (inline inherited-p))
 (defun inherited-p (schema slot)
   "Similar to HAS-SLOT-P, but when there is a formula checks whether this is
 an inherited formula."
   (let ((entry (slot-accessor schema slot)))
     (when entry
       (or (is-inherited (sl-bits entry))
 	  (and (formula-p (sl-value entry))
 	       (formula-p (a-formula-is-a (sl-value entry))))))))
 
 (defun formula-push (f)
   (bordeaux-threads:with-lock-held  (*formula-lock*)
     (push f *formula-pool*)))
 
 ;;; encode types
 (defparameter *types-table* (make-hash-table :test #'equal)
   "Hash table used to look up a Lisp type and returns its code")
 
 (defparameter *types-table-lock* (bordeaux-threads:make-recursive-lock)
   "Lock to synchonize access to *types-table*")
 
 (defmacro with-types-table-lock-held ((table) &body body)
   `(let ((,table *types-table*))
      (bordeaux-threads:with-recursive-lock-held (*types-table-lock*)
      ,@body)))
 
 (declaim (fixnum *types-array-inc*))
 (defparameter *types-array-inc* 255) ;; allocate in blocks of this size
 
 (declaim (fixnum *next-type-code*))
 (defparameter *next-type-code*  0)   ;; next code to allocate
 
 (defparameter types-array NIL
   "Array used to decode a number into its corresponding Lisp type.")
 
 (defparameter type-fns-array NIL
   "Array used to decode a number into its corresponding type-fn.")
 
 (defparameter type-docs-array NIL
   "Array used to decode a number into its corresponding documentation string.")
 
 (declaim (inline code-to-type code-to-type-fn code-to-type-doc check-kr-type))
 
 (defun code-to-type (type-code)
   (svref types-array type-code))
 
 (defun code-to-type-fn (type-code)
   (svref type-fns-array type-code))
 
 (defun code-to-type-doc (type-code)
   (svref type-docs-array type-code))
 
 (defun check-kr-type (value code)
   (funcall (code-to-type-fn code) value))
 
 
 (declaim (inline find-lisp-predicate))
 (defun find-lisp-predicate (simple-type)
   "Given simple type ('NULL, 'KEYWORD, etc...), returns the name of
 the lisp predicate to test this ('NULL, 'KEYWORDP, etc....)"
   (let ((p-name (concatenate 'string (symbol-name simple-type) "P"))
 	(-p-name (concatenate 'string (symbol-name simple-type) "-P")))
     (cond ((memberq simple-type '(NULL ATOM)) simple-type)
 	  (T (or (find-symbol p-name 'common-lisp)
 		 (find-symbol -p-name 'common-lisp)
 		 (find-symbol p-name)
 		 (find-symbol -p-name)
 		 (error "Could not find predicate for simple-type ~S~%"
 			simple-type))))))
 
 (defun make-lambda-body (complex-type)
   (with-types-table-lock-held (types-table)
     (let (code)
       (cond ((consp complex-type)	;; complex type (a list)
 	     (let ((fn   (first complex-type))
 		   (args (rest  complex-type)))
 	       (case fn
 		 ((OR AND NOT)
 		  (cons fn (mapcar #'make-lambda-body args)))
 		 (MEMBER
 		  `(memberq value ',args))
 		 ((IS-A-P IS-A)
 		  `(is-a-p value ,(second complex-type)))
 		 (SATISFIES
 		  `(,(second complex-type) value))
 		 ((INTEGER REAL)
 		  (let* ((pred (find-lisp-predicate fn))
 			 (lo (first args))
 			 (lo-expr (when (and lo (not (eq lo '*)))
 				    (if (listp lo)
 					`((< ,(car lo) value))
 					`((<= ,lo value)))))
 			 (hi (second args))
 			 (hi-expr (when (and hi (not (eq hi '*)))
 				    (if (listp hi)
 					`((> ,(car hi) value))
 					`((>= ,hi value))))))
 		    (if (or lo-expr hi-expr)
 			`(and (,pred value) ,@lo-expr ,@hi-expr)
 			`(,pred value))))
 		 (T  (error "Unknown complex-type specifier: ~S~%" fn)))))
 	    ((setq code (gethash (symbol-name complex-type) types-table))
 	     ;; is this a def-kr-type?
 	     (make-lambda-body (code-to-type code)))
 	    (T ;; simple-type
 	     (list (find-lisp-predicate complex-type) 'value))))))
 
 (defun type-to-fn (type)
   "Given the Lisp type, construct the lambda expr, or return the
    built-in function"
   (with-types-table-lock-held (types-table)
     (let (code)
       (cond ((consp type)			; complex type
 	     (if (eq (car type) 'SATISFIES)
 		 (let ((fn-name (second type)))
 		   `',fn-name) ;; koz
 		 `(function (lambda (value)
 		    (declare #.*special-kr-optimization*)
 		    ,(make-lambda-body type)))))
 	    ((setq code (gethash (symbol-name type) types-table))
 	     ;; is this a def-kr-type?
 	     (code-to-type-fn code))
 	    (T
 	     `',(find-lisp-predicate type))))))
 
 (declaim (inline copy-extend-array))
 (defun copy-extend-array (oldarray oldlen newlen)
   (let ((result (make-array newlen)))
     (dotimes (i oldlen)
       (setf (svref result i) (svref oldarray i)))
     result))
 
 
 (defun get-next-type-code ()
   "Return the next available type-code, and extend the type arrays
 if necessary."
   (let ((curlen (length types-array)))
    (when (>= *next-type-code* curlen)
     ;; out of room, allocate more space
     (let ((newlen (+ curlen *types-array-inc*)))
      (setf types-array     (copy-extend-array types-array     curlen newlen)
            type-fns-array  (copy-extend-array type-fns-array  curlen newlen)
            type-docs-array (copy-extend-array type-docs-array curlen newlen))))
    ;; in any case, return current code, then add one to it
    (prog1
        *next-type-code*
      (incf *next-type-code*))))
 
 
 (defun add-new-type (typename type-body type-fn &optional type-doc)
   "This adds a new type, if necessary
 Always returns the CODE of the resulting type (whether new or not)"
   (with-types-table-lock-held (types-table)
     (let ((code (gethash (or typename type-body) types-table)))
       (if code
 	  ;; redefining same name
 	  (if (equal (code-to-type code) type-body)
 	      ;; redefining same name, same type
 	      (progn
 		(format t "Ignoring redundant def-kr-type of ~S to ~S~%"
 			typename type-body)
 		(return-from add-new-type code))
 	      ;; redefining same name, new type --> replace it!
 	      (format t "def-kr-type redefining ~S from ~S to ~S~%"
 		      typename (code-to-type code) type-body))
 	  ;; defining a new name, establish new code
 	  (progn
 	    (setq code (or (gethash type-body types-table)
 			   (get-next-type-code)))
 	    (setf (gethash typename types-table) code)))
       (unless (gethash type-body types-table)
 	(setf (gethash type-body types-table) code))
       (setf (svref types-array code)
 	    (if typename
 		(if (stringp typename)
 		    (intern typename (find-package "KR"))
 		    typename)
 		type-body))
       (setf (svref type-docs-array code) (or type-doc NIL))
       (setf (svref type-fns-array  code)
 	    (if (and (symbolp type-fn) ;; koz
 		     (fboundp type-fn))
 		(symbol-function type-fn)
 		type-fn))
       code)))
 
 (defun kr-type-error (type)
   (error "Type ~S not defined; use~% (def-kr-type ... () '~S)~%" type type))
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun encode-type (type)
     "Given a LISP type, returns its encoding."
     (with-types-table-lock-held (types-table)
       ;; if there, just return it!
       (cond ((gethash type types-table))
 	    ((and (listp type) (eq (car type) 'SATISFIES))
 	     ;; add new satisfies type
 	     (add-new-type NIL type (type-to-fn type)))
 	    ((symbolp type)
 	     (or (gethash (symbol-name type) types-table)
 		 (let ((predicate (find-lisp-predicate type)))
 		   (when predicate
 		     (add-new-type NIL type predicate)))
 		 (kr-type-error type)))
 	    (T (kr-type-error type))))))
 
 (defun set-type-documentation (type string)
   "Add a human-readable description to a Lisp type."
    (setf (aref type-docs-array (encode-type type)) string))
 
 
 (defun get-type-documentation (type)
   "RETURNS: the documentation string for the internal number <type>."
   (aref type-docs-array (encode-type type)))
 
 
 
 ;;; Formula and slot code.
 
 ;; Helper function
 ;;
 (defun eliminate-constant-formula ()
   (declare (ftype (function (t t) t) destroy-constraint))
   ;; This was a constant formula!  Commit suicide.
   (with-demons-disabled
     (destroy-constraint *schema-self* *schema-slot*))
   (when *warning-on-evaluation*
       (format t "formula (~S ~S) is constant - eliminated~%"
 	      *schema-self* *schema-slot*))
   (let ((entry (slot-accessor *schema-self* *schema-slot*)))
     (if entry
 	(setf (sl-bits entry)
 	      (logior *constant-mask* (sl-bits entry))))))
 
 (declaim (inline slot-is-constant))
 (defun slot-is-constant (schema slot)
   (let ((entry (slot-accessor schema slot)))
     (is-constant (sl-bits entry))))
 
 
 (declaim (fixnum *warning-level*))
 (defparameter *warning-level* 0)
 
 ;; Helper function
 ;;
 (defun re-evaluate-formula (schema-self schema-slot current-formula entry #+EAGER eval-type)
   (let ((*schema-self* schema-self)
 	(*schema-slot* schema-slot)
 	(*current-formula* current-formula)
 	#+EAGER (*eval-type* eval-type)
 	)
     (when *warning-on-evaluation*
       (dotimes (i *warning-level*) (write-string " "))
       (format t "evaluating ~S (on ~S, slot ~S)~%"
 	      *current-formula* *schema-self* *schema-slot*)
       (incf *warning-level* 2))
     (let* ((*within-g-value* T)
 	   (*check-constants*		; only for the first evaluation!
 	    (unless *constants-disabled*
 	      (zerop (the fixnum (a-formula-number *current-formula*)))))
 	   (*accessed-slots* NIL)
 	   (*is-constant* T)
 	   (declared-constant (when *check-constants*
 				(when (or entry
 					  (setf entry (slot-accessor
 						       *schema-self*
 						       *schema-slot*)))
 				  (is-constant (sl-bits entry))))))
       (when declared-constant		; save work, since we know the answer
 	(setf *check-constants* nil))
       (set-cache-mark *current-formula* *sweep-mark*)
       (let ((the-result
 	     (catch 'no-link
 	       ;; If no-link, return cached-value anyway.
 	       ;; Evaluate the formula.
 	       (let ((new-v (funcall (coerce (a-formula-function *current-formula*) 'function))))
 		 (if (and *types-enabled*
 			  (multiple-value-bind (value result)
 			      (check-slot-type *schema-self* *schema-slot* new-v
 					       T entry)
 			    (cond ((eq result :REPLACE)
 				   (setf new-v value)
 				   NIL)
 				  ((eq result T) T)
 				  (T NIL))))
 		     ;; A type error
 		     (setf new-v NIL)
 		     ;; OK
 		     (unless (eq new-v (cached-value *current-formula*))
 		       ;; Do nothing if value has not changed.
 		       (let ((*check-constants* *check-constants*))
 			 ;; Call the pre-set-demon function on this schema if
 			 ;; this slot is an interesting slot.
 			 (run-pre-set-demons *schema-self* *schema-slot* new-v
 					     :CURRENT-FORMULA :FORMULA-EVALUATION)
 			 #+EAGER
 			 (do-eager-reeval new-v)
 			 ;; Set the cache to the new value
 			 (setf (cached-value *current-formula*) new-v))))
 		 new-v))))
 	(if (or declared-constant
 		(and *check-constants* *is-constant* *accessed-slots*))
 	    ;; Eliminate constant formulas, if needed.
 	    (eliminate-constant-formula)
 	    ;; Mark formula as valid here.
 	    (unless *setting-formula-p*
 	      (set-cache-is-valid *current-formula* t)))
 	(when *warning-on-evaluation* (decf *warning-level* 2))
 	the-result))))
 
 ;; We are working with a formula.  Note that broken links leave
 ;; the formula valid.
 ;;
 (defun g-value-formula-value (schema-self slot formula entry)
   (let ((*schema-self* schema-self))
     (if (cache-is-valid formula)
 	(a-formula-cached-value formula)
 	(progn
 	  (unless *within-g-value*
 	    ;; Bump the sweep mark only at the beginning of a chain of formula
 	    ;; accesses.  Increment by 2 since lower bit is "valid" flag.
 	    (incf *sweep-mark* 2))
 	  (if (= (cache-mark formula) *sweep-mark*)
 	      ;; If the sweep mark is the same as the current one, WE ARE IN THE
 	      ;; MIDDLE OF A CIRCULARITY.  Just use the old value, and mark it
 	      ;; valid.
 	      (progn
 		(when *warning-on-circularity*
 		  (format t "Warning - circularity detected on ~S, slot ~S~%"
 			  *schema-self* slot))
 		(unless *setting-formula-p*
 		  (set-cache-is-valid formula T))
 		(a-formula-cached-value formula))
 	      ;; Compute, cache and return the new value.
 	      (re-evaluate-formula *schema-self* slot formula entry))))))
 
 
 ;;; Inheritance
 
 (defun copy-to-all-instances (schema a-slot value &optional (is-first T))
   "Forces the <value> to be physically copied to the <a-slot> of all
 instances of the <schema>, even though local values were defined.
 However, if there was a local formula, do nothing."
   (s-value schema a-slot value)
   ;; Do not create copies of formulas, but set things up for inheritance
   (when (and is-first (formula-p value))
     (setf value *no-value*))
   (dolist (inverse *inheritance-inverse-relations*)
     (let ((children (if (eq inverse :IS-A-INV) ; for efficiency
 			(let ((entry (slot-accessor schema :IS-A-INV)))
 			  (when entry (sl-value entry)))
 			(get-local-value schema inverse))))
       (unless (eq children *no-value*)
 	(dolist (child children)
 	  ;; force new inheritance
 	  (unless (formula-p (get-value child a-slot))
 	    ;; Do not override if the user has specified a local formula!
 	    (copy-to-all-instances child a-slot value NIL)))))))
 
 (defun update-inherited-internal (child a-slot entry)
   (let ((old-value (sl-value entry)))
     (unless (eq old-value *no-value*)
       (let ((child-bits (sl-bits entry)))
 	(when (is-inherited child-bits)
 	  ;; NOTE: we erase the inherited value in all cases, even if it might
 	  ;; have been inherited from somewhere else (in the case of multiple
 	  ;; inheritance).  In any case, this is correct; at worst, it may
 	  ;; cause the value to be needlessly inherited again.
 	  ;; Force the children to re-inherit.
 	  (when (formula-p old-value)
 	    (delete-formula old-value T))
 	  (clear-one-slot child a-slot entry)
 	  ;; Recursively change children.
 	  (update-inherited-values child a-slot *no-value* NIL))))))
 
 (defun update-inherited-values (schema a-slot value is-first)
   "This function is used when a value is changed in a prototype.  It makes
 sure that any child schema which inherited the previous value is updated
 with the new value.
 INPUTS:
   - <value>: the new (i.e., current) value for the <schema>
   - <old-bits>: the setting of the slot bits for the <schema>, before the
     current value-setting operation.
   - <is-first>: if non-nil, this is the top-level call.
 "
   (let ((*schema-self* schema))
     (unless is-first
       ;; Invoke demons and propagate change around.
       (run-pre-set-demons schema a-slot value NIL :INHERITANCE-PROPAGATION)
       (run-invalidate-demons schema a-slot NIL)
       (propagate-change schema a-slot))
     (dolist (inverse *inheritance-inverse-relations*)
       (let ((children (if (eq inverse :IS-A-INV) ; for efficiency
 			  (let ((entry (slot-accessor schema :IS-A-INV)))
 			    (when entry (sl-value entry)))
 			  (get-local-value schema inverse))))
 	(unless (eq children *no-value*)
 	  (dolist (child children)
 	    (let ((entry (slot-accessor child a-slot)))
 	      (when entry
 		;; If child had no value, no need to propagate down
 		(setf is-first NIL)
 		;; force new inheritance
 		(update-inherited-internal child a-slot entry)))))))))
 
 
 ;;; Slot and formula change  code.
 
 
 (declaim (inline mark-as-changed))
 (defun mark-as-changed (schema slot)
   "Forces formulas which depend on the <slot> in the <schema> to be
 invalidated.  Mostly used for internal implementation.
 This function can be used when manually changing a slot (without using
 s-value).  It will run the demons and propagate the invalidate wave
 to all the ordinary places."
   (let ((entry (slot-accessor schema slot)))
     (run-invalidate-demons schema slot entry)
     (when (and entry (is-parent (sl-bits entry)))
       (update-inherited-values schema slot (sl-value entry) T)))
   (propagate-change schema slot))
 
 
 (declaim (inline mark-as-invalid))
 (defun mark-as-invalid (schema slot)
   "Invalidates the value of the formula at <position> in the <slot> of the
   <schema>.  If the value is not a formula, nothing happens."
   (let ((value (get-value schema slot)))
     (when (formula-p value)
       (set-cache-is-valid value NIL))))
 
 
 (defun recompute-formula (schema slot)
   "Forces the formula installed on the <slot> of the <schema> to be
 recomputed, propagating the change as needed.
 This may be used for implementation of formulas which depend on some
 non-KR value."
   (let* ((entry (slot-accessor schema slot))
 	 (formula (when entry (sl-value entry))))
     (when (formula-p formula)
       (let ((bits (sl-bits entry)))
 	(unless *within-g-value*
 	  (incf *sweep-mark* 2))
 	(re-evaluate-formula schema slot formula entry #+EAGER *eval-type*)
 	(run-invalidate-demons schema slot entry)
 	(when (is-parent bits)
 	  (update-inherited-values schema slot formula T))
 	#-EAGER
 	(propagate-change schema slot)
 	#+EAGER
 	(propagate)))))
 
 
 (defun propagate-change (schema slot)
   "Since the <slot> of the <schema> was modified, we need to propagate the
 change to all the formulas which depended on the old value."
   (let ((entry (slot-accessor schema slot)))
     ;; access the dependent formulas.
     (do-one-or-list (formula (slot-dependents entry) T)
       ;; Stop propagating if this dependent formula was already marked dirty.
       (if (and (not-deleted-p formula) (cache-is-valid formula))
 	(let* ((new-schema (on-schema formula))
 	       (new-slot (on-slot formula))
 	       (schema-ok (schema-p new-schema))
 	       (new-entry  NIL))
 	  (unless (and new-schema new-slot)
 	    (when *warning-on-disconnected-formula*
 	      (format
 	       t
 	       "Warning: disconnected formula ~S in propagate-change ~S ~S~%"
 	       formula schema slot))
 	    (continue-out))
 	  (if schema-ok
 	    (progn
 	      (setf new-entry (slot-accessor new-schema new-slot))
 	      (run-invalidate-demons new-schema new-slot new-entry))
 	    #+GARNET-DEBUG
 	    (progn
 	      (format
 	       t
 	       "propagate-change: formula ~S on destroyed object ~S ~S~%    ~
 	from change in schema ~S, slot ~S.~%"
 	       formula new-schema new-slot schema slot)))
 	  ;; The formula gets invalidated here.
 	  (set-cache-is-valid formula nil)
 	  ;; Notify all children who used to inherit the old value of the
 	  ;; formula.
 	  (if (and schema-ok new-entry)
 	    (if (slot-dependents new-entry)
 	      (propagate-change new-schema new-slot))))))))
 
 
 (defun visit-inherited-values (schema a-slot function)
   "Similar to update-inherited-values, but used when the hierarchy is
 modified or when an inheritable slot is destroyed.
 SIDE EFFECTS:
  - the <function> is called on all children which actually inherit the
    values in the <a-slot> of the <schema>.  This is determined by a fast
    check (the list of values should be EQ to that of the parent).
 Note that the <function> is called after all children have been visited..
 This allows it to be a destructive function."
   (let* ((entry (slot-accessor schema a-slot))
 	 (parent-entry (when entry (sl-value entry))))
     (dolist (inverse *inheritance-inverse-relations*)
       (dolist (child (if (eq inverse :IS-A-INV)
 			 (get-local-value schema :IS-A-INV)
 			 (get-local-value schema inverse)))
 	(let* ((entry (slot-accessor child a-slot))
 	       (value (when entry (sl-value entry))))
 	  (when (and value
 		     (is-inherited (sl-bits entry))
 		     (eq value parent-entry))
 	    (visit-inherited-values child a-slot function)
 	    (funcall function child a-slot)))))))
 
 
 (defun run-demons-and-set-value (schema slot new-value old-value is-relation
 				 is-formula was-formula the-bits entry)
   "Internal function which runs demons as appropriate (before changing the
 value) and then physically sets the <slot> in the <schema> to be
 <new-value>."
   (run-invalidate-demons schema slot entry)
   ;; Now set the value in the slot to be new-value.
   (cond ((and was-formula (not is-formula))
 	 ;; This is the case when we allow temporary overwriting
 	 (setf (cached-value old-value) new-value)
 	 ;; Set this to NIL, temporarily, in order to cause propagation
 	 ;; to leave the value alone.  It will be validated by s-value.
 	 (set-cache-is-valid old-value NIL))
 	(t
 	 ;; All other cases
 	 (when (and is-formula (null (cached-value new-value)))
 	   ;; place old value in the cache only if an initial value
 	   ;; was not provided for the new formula
 	   ;; Set value, but keep formula invalid.
 	   (setf (cached-value new-value)
 		 (if was-formula (cached-value old-value) old-value)))
 	 ;; Take care of relations.
 	 (when is-relation
 	   (when old-value (unlink-all-values schema slot))
 	   (link-in-relation schema slot new-value))
 	 (let ((new-bits (or the-bits *local-mask*)))
 	   (if entry
 	       ;; This is a special slot - just set it
 	       (setf (sl-value entry) new-value
 		     (sl-bits entry) new-bits)
 	       ;; This is not a special slot.
 	       (set-slot-accessor schema slot new-value new-bits nil)))))
   ;; Now propagate the change to all the children which used to
   ;; inherit the previous value of this slot from the schema.
   (when (and the-bits (is-parent the-bits))
     (let ((*setting-formula-p* T))
       (update-inherited-values schema slot new-value T))))
 
 
 (defun constant-slot-error (schema slot)
   (cerror "Set the slot anyway"
 	  "Schema ~S - trying to set slot ~S, which is constant."
 	  schema slot))
 
 
 (declaim (inline check-not-constant))
 (defun check-not-constant (schema slot entry)
   "Signals an error if the <slot> of the <schema> is not constant."
   (and (not *constants-disabled*)
        entry
        (is-constant (sl-bits entry))
        (constant-slot-error schema slot)))
 
 
 (declaim (inline slot-constant-p))
 (defun slot-constant-p (schema slot)
   "RETURN: T if the <slot> in the <schema> is constant, nil otherwise"
   (let ((entry (slot-accessor schema slot)))
     (when entry
       (is-constant (sl-bits entry)))))
 
 
 (defun set-formula-error (schema slot formula)
   "Called to give error message on multiply-installed formulas."
   ;; Formulas can only be installed on one slot!
   (format t "(s-value ~S ~S): formula ~S is already installed on~%~
 	schema ~S, slot ~S.  Ignored.~%"
 	  schema slot formula (on-schema formula) (on-slot formula))
   formula)
 
 
 (defun s-value-fn (schema slot value)
   "Does all the work of the macro S-VALUE.
 
 RGA --- no, returns two values: the value function set to and t if there
 was an error.  Note that in the case of a type error, it returns the
 current value of the slot."
   (locally (declare #.*special-kr-optimization*)
     (unless (schema-p schema)
       #+GARNET-DEBUG
       (if schema
 	  (error "S-VALUE called with the non-object  ~S  (slot ~S, value ~S)."
 		 schema slot value)
 	  (error "S-VALUE called with a null schema:  (slot ~S, value ~S)."
 		 slot value))
       ;; RGA added t for error return.
       (return-from s-value-fn (values value t)))
     (let* ((entry (slot-accessor schema slot))
 	   (old-value (when entry
 			;; Slot position is known at compile time
 			(sl-value entry)))
 	   the-bits is-depended)
       ;; give error if setting constant slot
       (check-not-constant schema slot entry)
 
       (if entry
 	  (setf the-bits (sl-bits entry)
 		is-depended (slot-dependents entry))
 	  (setf the-bits 0))
 
       (when (and the-bits
 		 (not (is-inherited the-bits))
 		 (eq old-value value)
 		 value)
 	;; We are setting to the same value as the old one!  Do nothing.
 	;; RGA --- Error return function
 	(return-from s-value-fn (values value nil)))
 
       (when (and *types-enabled* (not (formula-p value)))
 	(multiple-value-bind (result error-p)
 	    (check-slot-type schema slot value T entry)
 	  (cond ((not error-p))  ; Everything is OK
 		((eq error-p T)
 		 ;; A type error - user wants to do nothing
 		 ;; RGA --should return old-value, added a second
 		 ;; value indicating error.
 		 (return-from s-value-fn (values old-value t)))
 		(T
 		 ;; A type error - user supplied new value
 		 (setf value result)))))
 
       (let ((is-formula nil) (is-relation nil)
 	    (was-formula (formula-p old-value)))
 	;; Check for special cases in relation slots.
 	(when (and (setf is-relation (relation-p slot))
 		   (eq (setf value (check-relation-slot schema slot value)) *no-value*))
 	  ;; RGA --- added no-error return code
 	  (return-from s-value-fn (values old-value nil)))
 
 	;; If we are installing a formula, make sure that the formula
 	;; points to the schema and slot.
 	(when (formula-p value)
 	  (when (on-schema value)
 	    ;; RGA --- added error return code.
 	    (return-from s-value-fn
 	      (values (set-formula-error schema slot value) T)))
 	  (setf is-formula T)
 	  (setf (on-schema value) schema)
 	  (setf (on-slot value) slot)
 	  (unless (schema-name value)
 	    ;; This is an obscure case.  It may happen if somebody stores a
 	    ;; formula away, deletes the formula from its original slot, and
 	    ;; then restores the formula.  This is generally bad practice, but
 	    ;; there are cases when it may be necessary.
 	    (incf *schema-counter*)
 	    (setf (schema-name value) *schema-counter*)))
 
 	;; Now we call a demon to perform redisplay activities if the new
 	;; value is not a formula. If the new value is a formula, it has
 	;; not been evaluated yet so we do not know what its result is.
 	;; Since the display demon needs to know the new result to determine
 	;; if the object's bounding box should be merged with a clip region,
 	;; it does not make sense to call the display demon until the new
 	;; result is known
 	(unless is-formula
 	  (run-pre-set-demons schema slot value NIL :S-VALUE))
 
 	;; Now we can set the new value.
 	(setf the-bits (logand the-bits *not-inherited-mask*))
 	(run-invalidate-demons schema slot entry)
 	;; Now set the value in the slot to be <value>.
 	(cond
 	  ((and was-formula (not is-formula))
 	   (when (zerop (a-formula-number old-value))
 	     (format t "*** Warning: you are setting the value of slot ~S of
  object ~S.  This slot contains a formula which was never evaluated.
  The formula is now valid, but its dependencies are not set up properly.  As
  a result, the formula will never be evaluated.
  In order for this formula to work properly, you should have used
  (g-value ~S ~S) before using S-VALUE.  If you want to fix things now,
  re-install the formula using s-value.~%"
 		     slot schema schema slot))
 	   ;; This is the case when we allow temporary overwriting
 	   (setf (cached-value old-value) value)
 	   ;; Set this to NIL, temporarily, in order to cause propagation
 	   ;; to leave the value alone.  It will be validated by s-value.
 	   (set-cache-is-valid old-value NIL))
 	  (t
 	   ;; All other cases
 	   (when (and is-formula (null (cached-value value)))
 	     ;; place old value in the cache only if an initial value
 	     ;; was not provided for the new formula
 	     ;; Set value, but keep formula invalid.
 	     (setf (cached-value value)
 		   (if was-formula (cached-value old-value) old-value)))
 	   ;; Take care of relations.
 	   (when is-relation
 	     (when old-value (unlink-all-values schema slot))
 	     (link-in-relation schema slot value))
 	   (let ((new-bits (or the-bits *local-mask*)))
 	     (if entry
 		 ;; This is a special slot - just set it
 		 (setf (sl-value entry) value
 		       (sl-bits entry) new-bits)
 		 ;; This is not a special slot.
 		 (setf entry (set-slot-accessor schema
 						slot value new-bits nil))))))
 
 	;; Now propagate the change to all the children which used to
 	;; inherit the previous value of this slot from the schema.
 	(when (and the-bits (is-parent the-bits))
 	  (let ((*setting-formula-p* T))
 	    (update-inherited-values schema slot value T)))
 
 	;; Notify all dependents that the value changed.
 	(when is-depended
 	  (let ((*warning-on-disconnected-formula* nil))
 	    (propagate-change schema slot)))
 	(when (and was-formula (not is-formula))
 	  ;; We validate now, rather than earlier, because of a technicality
 	  ;; in demons-and-old-values.
 	  (set-cache-is-valid old-value T))
 
 	;; Was the old value a formula?
 	(when (and was-formula is-formula)
 	  ;; This is replacing a formula with another.  Eliminate the dependency
 	  ;; to the old one.
 	  (delete-formula old-value T))
 
 	(when is-relation
 	  ;; A relation slot is being changed.  We may need to invalidate all
 	  ;; inherited values.
 	  (reset-inherited-values schema))
 	;; RGA added nil error return flag.
 	(values value nil)))))
 
 
 (defun internal-s-value (schema slot value)
   "This is a stripped-down version of s-value-fn which is used by
 create-schema and friends.  It skips a lot of the stuff that is
 unnecessary at schema creation time."
   (let ((is-formula (formula-p value))
 	(is-relation (relation-p slot)))
     (when is-relation
       (unless (listp value)
 	(setf value (list value)))
       ;; Check for special cases in relation slots.
       (when (eq (setf value (check-relation-slot schema slot value)) *no-value*)
 	(return-from internal-s-value NIL)))
 
     ;; If we are installing a formula, make sure that the formula
     ;; points to the schema and slot.
     (when is-formula
       (when (on-schema value)
 	(return-from internal-s-value (set-formula-error schema slot value)))
       (setf (on-schema value) schema)
       (setf (on-slot value) slot))
 
     (set-slot-accessor schema slot value *local-mask* nil)
 
     ;; Take care of relations.
     (when is-relation
       (link-in-relation schema slot value))
     value))
 
 
 (defun set-is-a (schema value)
   "A specialized version of internal-s-value"
   ;; Check for special cases in relation slots.
   (when (eq (setf value (check-relation-slot schema :is-a value)) *no-value*)
     (return-from set-is-a NIL))
   ;; Set slot
   (set-slot-accessor schema :IS-A value *local-mask* NIL)
   (link-in-relation schema :IS-A value)
   value)
 
 
 (defun eliminate-formula-dependencies (formula except-schema)
   "If <except-schema> is non-nil, it indicates that a schema is in the
 process of being destroyed, and hence dependencies to THAT schema should
 not be tracked down."
   (do-one-or-list (schema (a-formula-depends-on formula))
     (unless (or (eq schema except-schema)
 		(deleted-p schema))	; schema is destroyed
       (iterate-slot-value (schema T T T)
 	slot value			; suppress warning
 	(let ((formulas (slot-dependents iterate-slot-value-entry)))
 	  (if (listp formulas)
 	      ;; Several dependents
 	      (when (memberq formula formulas)
 		(setf (full-sl-dependents iterate-slot-value-entry)
 		      (delete formula formulas)))
 	      ;; One dependent
 	      (when (eq formula formulas)
 		(setf (full-sl-dependents iterate-slot-value-entry) NIL))))))))
 
 
 (defun delete-formula (formula remove-from-parent)
   "Eliminate all dependency pointers from the <formula>, since it is no
 longer installed on a slot.
 
 INPUTS:
  - <formula>: the formula to get rid of
  - <hard-p>: if T, do a more thorough job of deleting everything, and
    destroy the <formula> schema itself."
   (when (a-formula-number formula)
     (eliminate-formula-dependencies formula NIL)
     (when remove-from-parent
       ;; Eliminate the <formula> from its parent's list of children.
       (let ((parent (a-formula-is-a formula)))
 	(when parent
 	  (delete-one-or-list formula (a-formula-is-a-inv parent)))))
     ;; Formula was not destroyed yet
     (setf (a-formula-bins formula) nil	; mark as destroyed.
 	  (a-formula-schema formula) nil
 	  (a-formula-slot formula) nil
 	  (a-formula-lambda formula) nil
 	  (a-formula-depends-on formula) nil)
     (let ((meta (a-formula-meta formula)))
       (when meta
 	(setf (a-formula-meta formula) NIL)
 	(destroy-schema meta)))
     (formula-push formula)))
 
 
 (defun destroy-slot-helper (x slot)
   ;; Make sure formulas are updated properly
   (mark-as-changed x slot)
   ;; Physically remove the slot in the child.
   (clear-one-slot x slot NIL))
 
 (defparameter *in-destroy-slot* 0)
 (defparameter *invalid-destroy-slot* 0)
 
 
 (defun destroy-slot (schema slot)
   "Eliminates the <slot>, and all the values it contains, from the <schema>,
 taking care of possible constraints."
   ;; Take care of all formulas which used to depend on this slot.
   (let ((entry (slot-accessor schema slot))
 	old-value)
     (when entry
       (setf old-value (sl-value entry))
       (check-not-constant schema slot entry)
       (let ((bits (sl-bits entry))
 	    (dependents (slot-dependents entry))
 	    new-value)
 	(run-invalidate-demons schema slot entry)
 	(when dependents
 	  ;; Access all dependent formulas.
 	  (do-one-or-list (formula dependents)
 	    #+EAGER
 	    (setf formula (car formula))
 	    #+EAGER
 	    (setf in-pq (eval-bit-set formula))
 	    (incf *in-destroy-slot*)
 	    (unless (cache-is-valid formula)
 	      (incf *invalid-destroy-slot*))
 	    ;; If this value is depended on by others, replace their value
 	    ;; by the current value.
 	    (let ((the-schema (on-schema formula))
 		  (the-slot (on-slot formula)))
 	      (when (and the-schema
 			 (schema-p the-schema) ; not destroyed
 			 (not (formula-p (g-value the-schema the-slot))))
 		;; Avoid complications with shared formulas.
 		(s-value the-schema the-slot
 			 (g-value the-schema the-slot))))
 	    ;; The formula is then marked invalid.
 	    #-EAGER
 	    (set-cache-is-valid formula NIL)
 	    #+EAGER
 	    (progn
 	      ;; set the formula's fixed bit back to nil to indicate it should
 	      ;; be evaluated during this iteration of the constraint solver
 	      (set-fixed-bit formula nil)
 	      (when (not in-pq)
 		(setf *eval-queue* (insert-pq formula *eval-queue*))))))
 
 	;; Destroy the formula, if this was a constrained slot.
 	(when (formula-p old-value)
 	  (delete-formula old-value T))
 
 	(when (relation-p slot)
 	  (unlink-all-values schema slot))
 
 	(setf new-value (g-value-inherit-values schema slot T entry))
 	;; Call the pre-set-demon function on this schema if
 	;; this slot is an interesting slot and the value it is
 	;; now inheriting is different from its previous value
 	(run-pre-set-demons schema slot new-value old-value :DESTROY-SLOT)
 
 	#+EAGER
 	;; Add this slot's dependents to the evaluation queue if its
 	;; new inherited value is different from its old value.
 	(unless (equal old-value new-value)
 	  (add-to-reeval schema slot))
 
 	(let ((was-parent (and bits (is-parent bits))))
 	  (when was-parent
 	    ;; Was this slot inherited by other schemata?  If so, make sure
 	    ;; they will inherit the right value afterwards.
 	    (update-inherited-values schema slot new-value T)
 	    (visit-inherited-values schema slot #'destroy-slot-helper))))
       ;; Now go ahead and physically destroy the slot.
       (clear-one-slot schema slot NIL)
       NIL)))
 
 
 (defun delete-schema (schema recursive-p)
   "Internal function.  If <recursive-p>, this is being called from within
 recursive-destroy-schema, so there is no need to maintain upwards
 relations properly."
   (when (not-deleted-p schema)	; do nothing if schema is already destroyed
     ;; Remove all inverse links.
     (if (formula-p schema)
 	;; Formulas do not use regular relations.
 	(let ((parent (a-formula-is-a schema))
 	      children)
 	  (when parent
 	    (setf children (a-formula-is-a-inv parent))
 	    (setf (a-formula-is-a-inv parent)
 		  (if (listp children)
 		    (delete schema children)
 		    (if (eq schema children)
 		      NIL
 		      children))))
 	  (do-one-or-list (child (a-formula-is-a-inv schema))
 	    ;; ? What exactly should happen here ?
 	    (setf (a-formula-is-a child) NIL)))
 	;; A normal schema
 	(progn
 	  (unless recursive-p
 	    (iterate-slot-value (schema NIL NIL NIL)
 	      value                     ; eliminate warning
 	      (when (relation-p slot)
 		(unlink-all-values schema slot))))
 	  (iterate-slot-value (schema NIL NIL NIL)
 	    slot value			; eliminate warning
 	    ;; Delete any formula value.
 	    (when (formula-p value)
 	      ;; This is a formula.  Get rid of it.
 	      (delete-formula value (not recursive-p))
 	      (delete-schema value recursive-p)))
 	  ;; Physically delete all the slots
 	  (clear-schema-slots schema)))
     ;; Now wipe out the symbol value as well.
     (when (symbolp (schema-name schema))
       (makunbound (schema-name schema)))
     ;; This is used as a marker for deleted schemas.
     (setf (schema-bins schema) nil)))
 
 
 (defun find-direct-dependency (expression target)
   "RETURNS: T if the given <expression>, or one of its subexpressions,
 directly depends on the <target>.  This must be a direct dependency,
 i.e., one which does not use a link."
   (when (listp expression)
     (or (and (eq (car expression) 'GV)
 	     (eq (cadr expression) target))
 	(dolist (thing expression)
 	  (when (find-direct-dependency thing target)
 	    (return T))))))
 
 
 (defun destroy-schema (schema &optional (send-destroy-message NIL) recursive-p)
   "Destroys the <schema>, eliminates all dependencies to and from it."
   (unless (schema-p schema)
     ;; If schema is already destroyed, do nothing.
     (return-from destroy-schema))
   (let ((done nil)
 	bizarre)
     (iterate-slot-value (schema T T NIL)
       slot				; eliminate warning
       (unless (eq value *no-value*)
 	;; Look at all formulas which depend on this slot.
 	(do-one-or-list (formula (slot-dependents iterate-slot-value-entry))
 	  (when (and formula		; defensive programming
 		     (not (memberq formula done)))
 	    ;; If this is a value depended on by others, replace their
 	    ;; value by the current value.  Do this, however, only if the
 	    ;; dependency is a DIRECT one, i.e., if the name of the
 	    ;; schema we are destroying is wired into the formula.  If
 	    ;; this is a link, leave things as they are.
 	    (let ((the-form
 		   (or (a-formula-lambda formula) ; for o-formulas
 		       (and (setf bizarre
 				  ;; This should always be a
 				  ;; list, but be prudent just
 				  ;; in case.
 				  (a-formula-function formula))
 			    (listp bizarre)
 			    (cddr bizarre)))))
 	      (when (find-direct-dependency the-form schema)
 		;; This is indeed a direct-dependency formula.  Install the
 		;; appropriate value.
 		(s-value (on-schema formula) (on-slot formula)
 			 (g-value (on-schema formula) (on-slot formula)))
 		(push formula done)
 		;; The formula now commits suicide.
 		(delete-formula formula (not recursive-p))))))
 	;; If this is a formula, eliminate dependencies to it, so we
 	;; do not get warnings in propagate-change.
 	(when (formula-p value)
 	  (delete-formula value T))))
     (when send-destroy-message
       ;; Call the :DESTROY method.
       (kr-call-initialize-method schema :DESTROY))
     ;; Physically delete the schema.
     (delete-schema schema recursive-p)))
 
 
 (defun recursive-destroy-schema (schema level)
   "This is an internal function used by CREATE-INSTANCE.  The purpose is to
 destroy not only the <schema> itself, but also its instances (and so on,
 recursively)."
   (unless (or (formula-p schema)	; safety check
 	      (deleted-p schema))
     (let* ((entry (slot-accessor schema :IS-A-INV))
 	   (children (if entry (sl-value entry))))
       (unless (eq children *no-value*)
 	(dolist (child children)
 	  (unless (eq child schema)
 	    (recursive-destroy-schema child (1+ level)))))
       (when *warning-on-create-schema*
 	(if (zerop level)
 	    (format t "Warning - create-schema is destroying the old ~S.~%"
 		    schema)
 	    (format t "Warning - create-schema is recursively destroying ~S.~%"
 		    schema)))))
   (destroy-schema
    schema
    NIL
    (if (zerop level)
        ;; if this is a top-level schema which has no prototype, use an
        ;; indiscriminate destroy.
        (null (slot-accessor schema :is-a))
        T)))
 
 
 (defun reset-inherited-values (schema)
   "Since the <relation> slot was changed, all children of the <schema> may
 have to inherit different values."
   (iterate-slot-value (schema T NIL T)	; use inheritance!
     (unless (relation-p slot)
       (unless (eq value *no-value*)
 	(when (is-inherited (sl-bits iterate-slot-value-entry))
 	  (destroy-slot schema slot))))))
 
 
 
 ;;; SCHEMA PRINTING
 
 
 (defun print-one-value (value type)
   (let ((string (cond ((formula-p value)
 		       (let ((cached (cached-value value))
 			     (valid (cache-is-valid value)))
 			 (if (or valid cached)
 			     (format nil "~S(~S . ~D)"
 				     value
 				     cached
 				     valid)
 			     (format nil "~S(nil . NIL)" value))))
 		      ((eq value *no-value*)
 		       "")
 		      (t
 		       (format nil "~S" value)))))
     (when type
       (setf string (concatenate 'simple-string string
 				(format nil "  ~([~S]~)" type))))
     (write-string string)
     (length string)))
 
 
 (defun print-one-slot-helper (value column indent space-p type)
   (when (> column 78)
     (format t "~%    ")
     (setf column (indent-by indent)))
   (when space-p
     (write-string " "))
   (incf column (print-one-value value type))
   column)
 
 
 (defun print-meta (formula)
   "Print the meta-information associated with a formula."
   (let ((meta (a-formula-meta formula)))
     (when (and meta (schema-p meta))
       (format t "  ---- meta information (~A):~%" meta)
       (call-on-ps-slots meta 'SLOT-PRINTER))))
 
 
 (defun indent-by (indent)
   (dotimes (i indent)
     (write-string "   "))
   (* indent 3))
 
 
 (defun force-down-helper (schema original-slots slots)
   (iterate-slot-value (schema T T NIL)
     value				; eliminate warning
     (unless (memberq slot original-slots)
       (pushnew slot slots)))
   (dolist (parent (get-local-value schema :IS-A))
     (setf slots (force-down-helper parent original-slots slots)))
   slots)
 
 
 (defun force-down-all-inheritance (schema)
   "A potentially VERY expensive operation.  It is done by PS when it wants
 to print out all inherited and inheritable slots of an object."
   (let ((original-slots nil))
     (iterate-slot-value (schema T NIL NIL)
       value				; eliminate warning
       (push slot original-slots))
     (dolist (slot (force-down-helper schema original-slots nil))
       (get-value schema slot))))
 
 
 (defun call-func-on-one-slot (schema slot inherited-ok function
 				     types-p indent limits)
   "Helper function for the following.
 The <function> is called with:
 (schema slot formula inherited valid real-value types-p indent limits)"
   (let* ((entry (slot-accessor schema slot))
 	 (values (when entry (sl-value entry)))
 	 (bits (when entry (sl-bits entry)))
 	 form valid real-value)
     (when bits
       (let ((are-inherited (and (is-inherited bits)
 				;; inherited formulas are printed anyway.
 				(not (formula-p values)))))
 	(unless (and (not inherited-ok) are-inherited)
 	  (unless (eq values *no-value*)
 	    (if (formula-p values)
 		(let ((cached (cached-value values))
 		      (is-valid (cache-is-valid values)))
 		  (setq form values)
 		  (setq valid is-valid)
 		  (setq real-value cached))
 		;; else not a formula
 		(setq real-value values))
 	    (funcall function
 		     schema slot form are-inherited valid real-value
 		     types-p bits indent limits)))
 	;; Indicate that the function was called.
 	T))))
 
 
 (defun call-on-ps-slots (schema function
 			 &key (control t)
 			      inherit
 			      (indent NIL)
 			      types-p
 			      all-p)
   "Apply the <function> to slots, the way PS would."
   (declare (special print-schema-control))
   (let ((is-ps (eq function 'SLOT-PRINTER))) ; true if inside PS
     (when (null indent)
       (setf indent 0))
     (when (numberp schema)
       (setf schema (s schema)))
     (unless (or (schema-p schema) (formula-p schema))
       (when is-ps
 	(format t "~S~%" schema))
       (return-from call-on-ps-slots nil))
     (when is-ps
       (indent-by indent))
     (cond ((formula-p schema)
 	   (setf control NIL))
 	  ((eq control :default)
 	   ;; use default control schema
 	   (setf control PRINT-SCHEMA-CONTROL))
 	  ((eq control T)
 	   ;; use schema itself as the control schema (i.e., use hierarchy)
 	   (setf control schema)))
     (let ((slots-ignored (when control (g-value-no-copy control :IGNORED-SLOTS)))
 	  (sorted (when control (g-value-no-copy control :SORTED-SLOTS)))
 	  (limit-values (when control (g-value-no-copy control :LIMIT-VALUES)))
 	  (global-limit (if control
 			    (g-value-no-copy control :GLOBAL-LIMIT-VALUES)
 			    most-positive-fixnum))
 	  (*print-as-structure*
 	   (if (and control (g-value-no-copy control :print-as-structure))
 	       ;; value is defined
 	       (g-value-no-copy control :print-as-structure)
 	       ;; value is undefined
 	       *print-as-structure*))
 	  (*print-structure-slots*
 	   (when control (g-value-no-copy control :print-slots))))
       (when is-ps
 	(format t "{~S~%" schema))
       ;; Print out all the sorted slots, first.
       (dolist (o sorted)
 	(call-func-on-one-slot schema o inherit function types-p indent
 			       (or (second (assocq o limit-values)) global-limit)))
       ;; Now print the remaining slots.
       (unless (listp slots-ignored)
 	(setf slots-ignored (list slots-ignored)))
       ;; Pre-inherit all slots that are inheritable.
       (unless (a-formula-p schema)
 	(when inherit
 	  (force-down-all-inheritance schema))
 	(if all-p
 	    (iterate-slot-value (schema T T NIL)
 	      (unless (or (memberq slot slots-ignored) (memberq slot sorted)
 			  (eq value *no-value*))
 		(call-func-on-one-slot
 		 schema slot inherit function types-p indent
 		 (or (second (assocq slot limit-values)) global-limit))))
 	    (iterate-slot-value (schema T NIL NIL)
 	      (unless (or (memberq slot slots-ignored) (memberq slot sorted)
 			  (eq value *no-value*))
 		(call-func-on-one-slot
 		 schema slot inherit function types-p indent
 		 (or (second (assocq slot limit-values)) global-limit))))))
       (when (and slots-ignored is-ps)
 	(indent-by indent)
 	(format t "  List of ignored slots:  ~{ ~A~}~%" slots-ignored))
       ;; special formula slots?
       (when (a-formula-p schema)
 	(if is-ps
 	    (progn
 	      (indent-by indent)
 	      (format t "  lambda:        ~(~S~)~%" (a-formula-lambda schema))
 	      (format t "  cached value:  (~S . ~S)~%"
 		      (cached-value schema) (cache-is-valid schema))
 	      (format t "  on schema ~S, slot ~S~%"
 		      (on-schema schema) (on-slot schema))
 	      (indent-by indent))
 	    (dolist (name '(:lambda :cached-value-valid :cached-value
 			    :schema :slot))
 	      (funcall function schema name nil nil T ; valid
 		       (g-formula-value schema name) nil 0 indent nil)))))
     (when is-ps
       (format t "  }~%"))))
 
 
 (defun call-on-one-slot (schema slot function)
   "Similar to CALL-ON-PS-SLOTS, but works on one slot only."
   (call-func-on-one-slot schema slot T function NIL 0 NIL))
 
 
 (defun slot-printer (schema name formula are-inherited valid values
 			    type-p bits indent limit-values)
   "Used by PS to print out one slot."
   (declare (ignore schema))
   (let ((number 0)
 	(printed nil)
 	(column (+ 20 (indent-by indent)))
 	(*print-length* 10)		; do not print out very long arrays!
 	type)
     (if are-inherited
       (format t "  ~(~S~) (inherited): " name)
       (format t "  ~S = " name))
     (when type-p
       (setf type (code-to-type (extract-type-code bits))))
     (when formula
       (format t "~S(" formula))
     (cond ((eq values *no-value*)
 	   (if type-p
 	     (setf column (print-one-slot-helper ; print types
 			   *no-value* column indent T type)))
 	   (setf printed T))
 	  ((and values (listp values) (listp (cdr values)))
 	   (format t "(")
 	   (dolist (value values)
 	     (setf printed t)
 	     (setf column (print-one-slot-helper value column indent
 						 (> number 0) nil))
 	     (incf number)
 	     (when (and limit-values (> number limit-values))
 	       ;; Too many values: use ellipsis form.
 	       (format t " ...")
 	       (return nil)))
 	   (format t ")")
 	   (when formula
 	     (format t " . ~S)" valid))
 	   (when type
 	     (print-one-slot-helper	; print out type
 	      *no-value* column indent nil type)))
 	  ((null values)
 	   (if formula
 	     (format t "nil . ~S)" valid)
 	     (format t " NIL"))
 	   (setf printed T)
 	   (when type
 	     (print-one-slot-helper	; print out type
 	      *no-value* column indent nil type)))
 	  (t
 	   (setf printed t)
 	   (setf column
 		 (print-one-slot-helper values column indent (not formula) type))
 	   (when formula
 	     (format t " . ~S)" valid))))
     (if printed
       (terpri)
       (format t " NIL~%"))))
 
 
 (defun ps (schema &key (control t) inherit (indent 0) types-p all-p
 		  (stream *standard-output*))
   "PS prints the <schema>.  The optional arguments allow fancy control of
   what is printed.
 
   A control schema may be used to determine which options are printed, which
   ones are ignored, etc.  See the manual for details.
 
   <control> can be one of the following:
  -  T, which means that the <schema> itself is used as the control schema;
  -  :DEFAULT, which means that the schema KR:PRINT-SCHEMA-CONTROL is used;
  -  any schema, which is used as the control schema.
  -  NIL, which means that the <schema> is printed in its entirety (i.e. no
     schema control.)
 
   If <inherit> is non-nil, slots that have been inherited are also printed.
   <indent> is used for debugging and should not be set by the user."
 
   (let ((*standard-output* stream))
     (call-on-ps-slots schema 'SLOT-PRINTER
 		      :control control :inherit inherit :indent indent
 		      :types-p types-p :all-p all-p)
     (when (formula-p schema)
       (print-meta schema)))
   schema)
 
 
 (defun the-bits (bits)
   (if (integerp bits)
       ;; The normal case
       (let ((type (extract-type-code bits)))
 	(format t "~:[-~;p~]~:[-~;l~]~:[-~;C~]~:[-~;P~]~:[-~;u~]~:[-~;i~] "
 		(is-parameter bits) (is-local-only bits)
 		(is-constant bits) (is-parent bits)
 		(is-update-slot bits) (is-inherited bits))
 	(unless (zerop type)
 	  (format t "[~(~S~)]   " (code-to-type type))))
       ;; A special case for formula slots which are stored in a special way
       (format t "---- ")))
 
 
 (defun full-normal-slot (schema slot)
   "Helper function for FULL."
   (format t "~(~24S~) " slot)
   (let* ((entry (slot-accessor schema slot))
 	 (values (if entry (sl-value entry)))
 	 (bits (if entry (sl-bits entry)))
 	 (dependents (slot-dependents entry)))
     (the-bits bits)
     (if entry
 	;; Slot is there
 	(let ((first t))
 	  (when (eq values *no-value*)
 	    (setf values NIL))
 	  (if (and (listp values) (listp (cdr values)))
 	      (when values
 		(format t " (")
 		(dolist (value values)
 		  (if first
 		      (setf first nil)
 		      (write-string " "))
 		  (print-one-value value NIL))
 		(format t ")"))
 	      (progn
 		(write-string " ")
 		(print-one-value values NIL)))
 	  ;; Show dependent formulas, if any
 	  (when dependents
 	    (format t "   ****--> ")
 	    (do-one-or-list (f dependents)
 	      (format t " ~s" f)))
 	  (terpri))
 	;; No slot???
 	(terpri)))
   (values))
 
 
 (defun full (&rest schemata)
   "Internal debugging - print out schemata in gory detail."
   (dolist (schema schemata)
     (when (numberp schema)
       (setf schema (s schema)))
     (let ((is-formula (a-formula-p schema)))
       ;; use iterators to get inherited slots as well
       (if is-formula
 	  ;; This is a formula
 	  (progn
 	    (format
 	     t "---------------------------------------------- formula ~S~%"
 	     schema)
 	    ;; print special formula slots.
 	    (format t "Schema, slot:           ~S  ~S~%"
 		    (on-schema schema) (on-slot schema))
 	    (format t "Cached value:           (~S . ~S)~%"
 		    (cached-value schema) (a-formula-number schema))
 	    (format t "Depends on:             ~S~%"
 		    (a-formula-depends-on schema))
 	    (format t "Lambda:                 ~(~S~)~%"
 		    (a-formula-lambda schema))
 	    (when (a-formula-is-a schema)
 	      (format t
 		      "parent formula:         ~S~%"
 		      (a-formula-is-a schema)))
 	    (when (a-formula-is-a-inv schema)
 	      (format t "children:               ~S~%"
 		      (a-formula-is-a-inv schema)))
 	    (print-meta schema))
 	  ;; This is a normal slot
 	  (progn
 	    (format
 	     t "---------------------------------------------- schema ~S~%"
 	     schema)
 	    (iterate-slot-value (schema T T nil)
 	      value ;; eliminate warning
 	      (full-normal-slot schema slot))))))
   (values))
 
 
 
 ;;; O-O PROGRAMMING
 
 
 (defun find-parent (schema slot)
   "Find a parent of <schema> from which the <slot> can be inherited."
   (dolist (relation *inheritance-relations*)
     (dolist (a-parent (if (eq relation :is-a)
 			  (get-local-value schema :IS-A)
 			  (get-local-value schema relation)))
       (when a-parent
 	(let ((value (g-local-value a-parent slot)))
 	  (if value
 	      (return-from find-parent (values value a-parent))
 	      (multiple-value-bind (value the-parent)
 		  (find-parent a-parent slot)
 		(when value
 		  (return-from find-parent (values value the-parent))))))))))
 
 
 (defun old-kr-send-function (schema slot &rest args)
   "Same as KR-SEND, but as a function."
   (let ((the-function (g-value schema slot)))
     (when the-function
       ;; Bind these in case call prototype method is used.
       (let ((*kr-send-self* schema)
 	    (*kr-send-slot* slot)
 	    (*demons-disabled* T))
 	(apply the-function args)))))
 
 
 (defun kr-call-initialize-method (schema slot)
   "This is similar to kr-send-function, except that it is careful NOT to
 inherit the method, which is only used once.  This is to reduce unnecessary
 storage in every object."
   (let ((the-function (g-value-no-copy schema slot)))
     (when the-function
       ;; Bind these in case call prototype method is used.
       (let ((*kr-send-self* schema)
 	    (*kr-send-slot* slot)
 	    (*kr-send-parent* NIL)
 	    (*demons-disabled* T))
 	(funcall the-function schema)))))
 
 
 (defun kr-init-method (schema the-function)
   "Similar, but even more specialized.  It is only called by create-schema
 and friends, which know whether an :initialize method was specified locally."
   (let ((*kr-send-parent* nil))
     (if the-function
       (setf *kr-send-parent* schema)
       (multiple-value-setq (the-function *kr-send-parent*)
 	(find-parent schema :INITIALIZE)))
     (when the-function
       ;; Bind these in case call prototype method is used.
       (let ((*kr-send-self* schema)
 	    (*kr-send-slot* :INITIALIZE)
             (*kr-send-parent* NIL)
 	    #-(and) (*demons-disabled* T))
 	(funcall the-function schema)))))
 
 
 (defun call-prototype-function (&rest args)
   "Functional version of CALL-PROTOTYPE-METHOD."
   (let (parent)
     (if (get-local-value *kr-send-self* *kr-send-slot*)
 	(setf parent *kr-send-self*)
 	(multiple-value-bind (method real-parent)
 			     (find-parent *kr-send-self* *kr-send-slot*)
 	  (declare (ignore method))
 	  (setf parent real-parent)))
     (multiple-value-bind (function- the-parent)
 			 (find-parent parent *kr-send-slot*)
       (when function-
 	(let ((*kr-send-self* the-parent)
 	      (*kr-send-parent* NIL))
 	  (apply function- args))))))
 
 ;;; Schemas
 
 (defun allocate-schema-slots (schema)
   (locally (declare #.*special-kr-optimization*)
     (setf (schema-bins schema)
 	  (make-hash-table :test #'eq #+sbcl :synchronized #+sbcl t)))
   schema)
 
 
 (defun make-a-new-schema (name)
   "Creates a schema with the given <name>, making sure to destroy the old
 one by that name if it exists.  The initial number of slots is
 <needed-slots>."
   (locally (declare #.*special-kr-optimization*)
     (when (keywordp name)
       (setf name (symbol-name name)))
     (cond ((null name)
 	   ;; An unnamed schema.
 	   (let ((schema (make-schema)))
 	     (setf *schema-counter* (1+ *schema-counter*))
 	     (setf (schema-name schema) *schema-counter*)
 	     (allocate-schema-slots schema)
 	     schema))
 
 	  ((stringp name)
 	   ;; This clause must precede the next!
 	   (let ((schema (make-schema)))
 	     (allocate-schema-slots schema)
 	     (setf (schema-name schema) name)
 	     schema))
 
 	  ;; Is this an existing schema?  If so, destroy the old one and its
 	  ;; children.
 	  ((and (boundp name)
 		(symbolp name))
 	   (let ((schema (symbol-value name)))
 	     (if (is-schema schema)
 		 (progn
 		   (recursive-destroy-schema schema 0)
 		   (allocate-schema-slots schema))
 		 (progn
 		   (setf schema (make-schema))
 		   (allocate-schema-slots schema)
 		   (eval `(defvar ,name))))
 	     ;; Assign the new schema as the value of the variable <name>.
 	     (setf (schema-name schema) name)
 	     (set name schema)))
 
 	  ((symbolp name)
 	   (eval `(defvar ,name))
 	   (let ((schema (make-schema)))
 	     (allocate-schema-slots schema)
 	     (setf (schema-name schema) name)
 	     (set name schema)))
 	  (t
 	   (format t "Error in CREATE-SCHEMA - ~S is not a valid schema name.~%"
 		   name)))))
 
 
 ;;; Constant slots
 
 
 (defun process-one-constant (schema slot)
   "The <slot> in <schema> was declared constant."
   ;; set slot information
   (let ((entry (slot-accessor schema slot)))
     (if (null entry)
 	;; Slot is not present - create it, mark constant.
 	(set-slot-accessor schema slot *no-value* *constant-mask* nil)
 	;; Slot is present
 	(setf (sl-bits entry) (logior *constant-mask* (sl-bits entry))))))
 
 
 (defun declare-constant (schema slot)
   "Declare slot constants AFTER instance creation time."
   (unless *constants-disabled*
     (if (eq slot T)
       ;; This means that all constants declared in :MAYBE-CONSTANT should be
       ;; made constant
       (let ((maybe (g-value-no-copy schema :MAYBE-CONSTANT)))
 	(dolist (m maybe)
 	  (declare-constant schema m)))
       ;; This is the normal case - only 1 slot.
       (let ((constant-list (g-value schema :CONSTANT))
 	    (positive T))
 	(do ((list constant-list (if (listp list) (cdr list) NIL))
 	     (prev nil list)
 	     c)
 	    ((null list)
 	     (setf constant-list (cons slot (if (listp constant-list)
 					      constant-list
 					      (list constant-list))))
 	     (s-value schema :CONSTANT constant-list)
 	     (process-one-constant schema slot))
 	  (setf c (if (listp list) (car list) list))
 	  (cond ((eq c :EXCEPT)
 		 (setf positive NIL))
 		((eq c slot)
 		 (when positive
 		   ;; Slot is already marked constant, so there's nothing
 		   ;; to do.
 		   (process-one-constant schema slot)
 		   (return nil))
 		 ;; Slot was explicitly excepted from constant list.
 		 (setf (cdr prev) (cddr prev)) ; remove from :EXCEPT
 		 (when (and (null (cdr prev))
 			    (eq (car prev) :EXCEPT))
 		   ;; We are removing the last exception to the constant list
 		   (let ((end (nthcdr (- (length constant-list) 2)
 				      constant-list)))
 		     (setf (cdr end) nil)))
 		 (setf constant-list (cons c constant-list))
 		 (s-value schema :CONSTANT constant-list)
 		 (process-one-constant schema slot)
 		 (return))))))))
 
 
 (defun merge-prototype-values (object slot parents values)
   "Process declarations such as :DECLARE (:PARAMETERS T :EXCEPT :WIDTH),
 which modify the prototype's specification of a declaration by adding or
 removing new slot names."
   (unless values
     (setf values (when parents (g-value (car parents) slot))))
   (let ((exceptions nil)
 	(add-prototype nil)
 	(results nil))
     ;; process declarations
     (when (and values (not (eq values :NONE)))
       (if (listp values)
 	  (progn
 	    (if (eq (car values) 'QUOTE)
 		(format
 		 t "The ~S list for schema ~S is specified incorrectly - too many quotes:~%   ~S~%" slot object values))
 	    ;; Normal case - a list
 	    (do ((c values (cdr c)))
 		((null c))
 	      (cond ((eq (car c) T)
 		     (setf add-prototype T))
 		    ((eq (car c) :EXCEPT) ; following is list of exceptions.
 		     (setf exceptions (cdr c))
 		     (return))
 		    (t
 		     (if (eq slot :CONSTANT)
 			 (process-one-constant object (car c))
 			 (pushnew (car c) results))))))
 	  ;; For the case (:CONSTANT T), for example - single value
 	  (if (eq values T)
 	      (setf add-prototype T)
 	      (if (eq slot :CONSTANT)
 		  (process-one-constant object values)
 		  (setf results (list values)))))
       (when add-prototype			; Add slots declared in prototype
 	(let ((maybe-constant
 	       (if (eq slot :CONSTANT)
 		   (g-value-no-copy object :MAYBE-CONSTANT)
 		   (g-value (car parents) slot))))
 	  (do-one-or-list (c maybe-constant)
 	    (unless (memberq c exceptions)
 	      (if (eq slot :CONSTANT)
 		  (process-one-constant object c)
 		  (pushnew c results))))))
       (unless (eq slot :CONSTANT)
 	(setf results (nreverse results))
 	(unless (equal results values)
 	  (s-value object slot results))))))
 
 
 (defun process-constant-slots (the-schema parents constants do-types)
   "Process local-only and constant declarations."
   (locally (declare #.*special-kr-optimization*)
     ;; Install all update-slots entries, and set their is-update-slot bits
     (dolist (slot (g-value-no-copy the-schema :UPDATE-SLOTS))
       (let ((entry (slot-accessor the-schema slot)))
 	(if entry
 	    (setf (sl-bits entry) (set-is-update-slot (sl-bits entry)))
 	    (set-slot-accessor the-schema slot *no-value*
 			       (set-is-update-slot *local-mask*)
 			       NIL))))
     ;; Mark the local-only slots.
     (dolist (parent parents)
       (dolist (local (g-value-no-copy parent :LOCAL-ONLY-SLOTS))
 	(unless (listp local)
 	  (cerror "Ignore the declaration"
 		  "create-instance (object ~S, parent ~S):  :local-only-slots
 declarations should consist of lists of the form (:slot T-or-NIL).  Found
 the expression ~S instead."
 		  the-schema parent local)
 	  (return))
 	;; Set the slots marked as local-only
 	(let ((slot (car local)))
 	  (unless (slot-accessor the-schema slot)
 	    (if (second local)
 		;; Copy down the parent value, once and for all.
 		(let* ((entry (slot-accessor parent slot))
 		       (value (if entry (sl-value entry))))
 		  (unless (formula-p value)
 		    ;; Prevent inheritance from ever happening
 		    (internal-s-value the-schema slot (g-value parent slot))))
 		;; Avoid inheritance and set the slot to NIL.
 		(internal-s-value the-schema slot NIL))))))
     ;; Now process constant declarations.
     (unless *constants-disabled*
       (merge-prototype-values the-schema :CONSTANT parents constants))
     ;; Now process type declarations
     (when (and do-types *types-enabled*)
       ;; Copy type declarations down from the parent(s), unless overridden
       ;; locally.
       (dolist (parent parents)
 	(iterate-slot-value (parent T T nil)
 	  value						 ;; suppress warning
 	  (let ((bits (sl-bits iterate-slot-value-entry))) ; get parent's bits
 	    ;; keep only the type information
 	    (setf bits (logand bits *type-mask*))
 	    (unless (zerop bits)
 	      (let ((the-entry (slot-accessor the-schema slot)))
 		(if the-entry
 		    (let ((schema-bits (sl-bits the-entry)))
 		      (when (zerop (extract-type-code schema-bits))
 			;; Leave type alone, if one was declared locally.
 			(setf (sl-bits the-entry)
 			      (logior (logand schema-bits *all-bits-mask*)
 				      bits))))
 		    (set-slot-accessor the-schema slot *no-value* bits NIL)))))))
       ;; Typecheck
       (iterate-slot-value (the-schema T T nil)
 	(unless (eq value *no-value*)
 	  (unless (formula-p value)	; don't bother with formulas.
 	    (multiple-value-bind (new-value result)
 		(check-slot-type the-schema slot value)
 	      (when (eq result :REPLACE)
 		(s-value the-schema slot new-value)))))))))
 
 
 (defun add-update-slot (schema slot &optional turn-off)
   "Turn the <slot> of the <schema> into an :update-slot; add the <slot> to
 the contents of :update-slots, and turn on the internal bit.  If
 <turn-off> is T, make the <slot> be no longer an update slot."
   (let ((entry (slot-accessor schema slot)))
     (if entry
       ;; There is an entry - manipulate the bits directly
       (if turn-off
 	;; Turn bit off
 	(setf (sl-bits entry) (logand (sl-bits entry)
 				      (lognot *is-update-slot-mask*)))
 	;; Turn bit on
 	(setf (sl-bits entry) (set-is-update-slot (sl-bits entry))))
       ;; There is no entry
       (unless turn-off
 	(set-slot-accessor schema slot *no-value*
 			   (set-is-update-slot *local-mask*)
 			   NIL))))
   (if turn-off
     (setf (g-value schema :UPDATE-SLOTS)
 	  (delete slot (g-value schema :UPDATE-SLOTS)))
     (pushnew slot (g-value schema :UPDATE-SLOTS))))
 
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun cannot-be-quoted (value)
     (or (listp value)
 	(and (symbolp value)
 	     (not (keywordp value))))))
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun process-slot-descriptor (x)
     (if (listp x)
 	(if (find-if #'cannot-be-quoted (cdr x))
 	    (cons 'list x)
 	    `',x)
 	x)))
 
 (defun merge-declarations (declaration keyword output)
   (let ((old (find keyword output :key #'second)))
     (if old
 	(setf (cadr (third old)) (union (cdr declaration)
 					(cadr (third old))))
 	(push `(cons ,keyword ',(cdr declaration)) output)))
   output)
 
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun process-slots (slots)
     "This function processes all the parameters of CREATE-INSTANCE and returns
 an argument list suitable for do-schema-body.  It is called at compile time.
 
 RETURNS: a list, with elements as follows:
  - FIRST: the prototype (or list of prototypes), or NIL;
  - SECOND: the list of type declarations, in the form (type slot slot ...)
    or NIL (if there were no type declarations) or :NONE (if the declaration
    (type) was used, which explicitly turns off all types declared in the
    prototype(s).
  - REST OF THE LIST: all slot specifiers, with :IS-A removed (because that
    information is moved to the prototype list)."
     (let ((output nil)
 	  (is-a nil))
       (do ((head slots (cdr head))
 	   (types nil)
 	   (had-types nil)		; true if there is a declaration
 	   slot)
 	  ((null head)
 	   (if types
 	       (push `(quote ,types) output)
 	       (if had-types
 		   (push :NONE output)
 		   (push NIL output))))
 	(setf slot (car head))
 	(cond ((null slot)
 	       ;; This is an error.
 	       (cerror
 		"Ignore the specification"
 		"Error in CREATE-SCHEMA: NIL is not a valid slot ~
 		 specifier; ignored.~%~
 	         Object ~S, slot specifiers are ~S~%"
 		kr::*create-schema-schema* head))
 	      ((keywordp slot)
 	       ;; Process declarations and the like.
 	       (case slot
 		 (:NAME-PREFIX
 		  (pop head))
 		 (:DECLARE
 		  (pop head)
 		  (dolist (declaration (if (listp (caar head))
 					   (car head)
 					   (list (car head))))
 		    (case (car declaration)
 		      (:TYPE
 		       (setf had-types T)
 		       (dolist (spec (cdr declaration))
 			 (push spec types)))
 		      ((:CONSTANT :IGNORED-SLOTS :LOCAL-ONLY-SLOTS
 				  :MAYBE-CONSTANT :PARAMETERS :OUTPUT
 				  :SORTED-SLOTS :UPDATE-SLOTS)
 		       (setf output (merge-declarations declaration
 							(car declaration)
 							output)))
 		      (t
 		       (cerror
 			"Ignore the declaration"
 			"Unknown declaration (~S) in object creation:~%~S~%"
 			(car declaration)
 			declaration)))))))
 	      ((listp slot)
 	       ;; Process slot descriptors.
 	       (if (eq (car slot) :IS-A)
 		   (setf is-a (if (cddr slot)
 				  `(list ,@(cdr slot))
 				  (cadr slot)))
 		   (if (listp (cdr slot))
 		       (if (find-if #'cannot-be-quoted (cdr slot))
 			   (if (cddr slot)
 			       (push `(list ,(car slot) . ,(cdr slot)) output)
 			       (push `(cons ,(car slot) . ,(cdr slot)) output))
 			   (if (cddr slot)
 			       (push `'(,(car slot) . ,(cdr slot)) output)
 			       (push `'(,(car slot) . ,(cadr slot)) output)))
 		       (push (cdr slot) output))))
 	      (T
 	       (cerror
 		"Ignore the specification"
 		"A slot specification should be of the form ~
 		 (:name [values]*) ;~%found ~S instead.  Object ~S, slots ~S."
 		slot kr::*create-schema-schema* slots))))
       (cons is-a output))))
 
 
 (defun handle-is-a (schema is-a generate-instance override)
   (if (or (eq schema is-a)
 	  (memberq schema is-a))
       (format t "~A: cannot make ~S an instance of itself!  ~
     			Using NIL instead.~%"
 	      (if generate-instance
 		  "CREATE-INSTANCE" "CREATE-SCHEMA")
 	      schema)
       ;; Make sure :override does not duplicate is-a-inv contents.
       (let ((*schema-is-new* (not override)))
 	(set-is-a schema is-a))))
 
 
 (defun do-schema-body (schema is-a generate-instance do-constants override
 		       types &rest slot-specifiers)
   "Create-schema and friends expand into a call to this function."
   (when (equal is-a '(nil))
     (format
      t
      "*** (create-instance ~S) called with an illegal (unbound?) class name.~%"
      schema)
     (setf is-a NIL))
   (unless (listp is-a)
     (setf is-a (list is-a)))
   (when is-a
     (let ((*schema-is-new* T))	      ; Bind to prevent search on insertion
 				      ; of :is-a-inv in parent schemata.
       ;; Check for immediate is-a loop, and set the :IS-A slot.
       (handle-is-a schema is-a generate-instance override)))
 
   (do* ((slots slot-specifiers (cdr slots))
 	(slot (car slots) (car slots))
 	(initialize-method NIL)
 	(constants NIL)
 	(had-constants NIL)		; true if declared, including NIL
 	(cancel-constants (find '(:constant) slot-specifiers :test #'equal))
 	(parent (car is-a))
 	(slot-counter (if is-a 1 0)))
 
        ((null slots)
 	;; Process the type declarations.
 	(unless (eq types :NONE)
 	  (dolist (type types)
 	    (if (cdr type)
 		(let ((n (encode-type (car type))))
 		  (dolist (slot (cdr type))
 		    (set-slot-type schema slot n)))
 		(format t "*** ERROR - empty list of slots in type declaration ~
                           for object ~S:~%  ~S~%" schema (car type)))))
 	;; Process the constant declarations, and check the types.
 	(when do-constants
 	  (process-constant-slots
 	   schema is-a
 	   (if had-constants
 	       ;; There WAS a constant declaration, perhaps NIL.
 	       (if constants
 		   (if (formula-p constants)
 		       (g-value-formula-value schema :CONSTANT constants NIL)
 		       constants)
 		   :NONE)
 	       ;; There was no constant declaration.
 	       NIL)
 	   (not (eq types :NONE))))
 	;; Merge prototype and local declarations.
 	(dolist (slot slot-specifiers)
 	  (when (and (listp slot)
 		     (memberq (car slot)
 			      '(:IGNORED-SLOTS :LOCAL-ONLY-SLOTS
 				:MAYBE-CONSTANT :PARAMETERS :OUTPUT
 				:SORTED-SLOTS :UPDATE-SLOTS)))
 	    (merge-prototype-values schema (car slot) is-a (cdr slot))))
 	(when generate-instance
 	  ;; We are generating code for a CREATE-INSTANCE, really.
 	  (kr-init-method schema initialize-method))
 	schema)
 
     (cond ((eq slot :NAME-PREFIX)
 	   ;; Skip this and the following argument
 	   (pop slots))
 	  ((consp slot)
 	   (let ((slot-name (car slot))
 		 (slot-value (cdr slot)))
 	     (case slot-name		; handle a few special slots.
 	       (:INITIALIZE
 		(when slot-value
 		  ;; A local :INITIALIZE method was provided
 		  (setf initialize-method slot-value)))
 	       (:CONSTANT
 		(setf constants (cdr slot))
 		(setf had-constants T)))
 	     ;; Check that the slot is not declared constant in the parent.
 	     (when (and (not cancel-constants) (not *constants-disabled*)
 			(not *redefine-ok*))
 	       (when (and parent (slot-constant-p parent slot-name))
 		 (cerror
 		  "If continued, the value of the slot will change anyway"
 		  "Slot ~S in ~S was declared constant in prototype ~S!~%"
 		  slot-name schema (car is-a))))
 	     (if override
 		 ;; This is more costly - check whether the slot already exists,
 		 ;; dependencies, etc.
 		 (s-value schema slot-name slot-value)
 		 ;; No check needed in this case.
 		 (setf slot-counter
 		       (internal-s-value schema slot-name slot-value)))))
 	  (T
 	   (format t "Incorrect slot specification: object ~S ~S~%"
 		   schema slot)))))
 
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun creation-message (name)
     (when *print-new-instances*
       (when (and (listp name)
 		 (eq (car name) 'QUOTE))
 	(format *standard-output* "~&Object ~S~%" (eval name))))))
 
 
 (defun end-create-instance (schema)
   "Processes the second half of a create-instance.  Begin-create-instance must
 have been called on the <schema>."
   (process-constant-slots schema (get-local-value schema :IS-A)
 			  (get-local-value schema :CONSTANT)
 			  nil)
   (kr-init-method schema (get-local-value schema :INITIALIZE)))
 
 
 ;;; TYPE CHECKING
 
 (declaim (inline get-slot-type-code))
 (defun get-slot-type-code (object slot)
   (let ((entry (slot-accessor object slot)))
     (and entry
 	 (get-entry-type-code entry))))
 
 
 (declaim (inline g-type))
 (defun g-type (object slot)
   (let ((type (get-slot-type-code object slot)))
     (and type
 	 (code-to-type type))))
 
 
 (defun check-slot-type (object slot value &optional (error-p T) entry)
   "Check whether <value> has the right type for the <slot> in the <object>.
 
 RETURNS:
  if error-p is non-nil: multiple values:
  - a replacement value, if the user chose to continue and supply a
    replacement;  T if no error;  NIL otherwise;
  - T (if type error and the user did not supply a value), or
    NIL (if there was no type error), or
    :REPLACE, if a replacement value was supplied by the user.
  if error-p is nil:
  an error string describing what error condition was found.
 "
   (let ((type-code (if entry
 		       (get-entry-type-code entry)
 		       (get-slot-type-code object slot))))
     (or (null type-code)
         (zerop type-code)
 	(check-kr-type value type-code)
 	(let* ((type (code-to-type type-code))
 	       (readable-type (get-type-documentation type))
 	       (message
 		(format
 		 nil
 		 "bad KR type: value ~S~:[~*~;, a ~S,~] is not valid for slot ~S in~%  object ~S.  The slot is declared of type ~S~@[,~%  i.e., ~A~].~@[~%  The value was computed by a formula.~]~%"
 		 value value (type-of value)
 		 slot object type
 		 readable-type
 		 (formula-p (get-local-value object slot)))))
 	  (if error-p
 	      (progn
 		(cerror "Retain old value in the slot" message)
 		(values T T))
 	      message)))))
 
 
 ;; This version allows multiple restart actions.  However, it is extremely
 ;; slow and sets up a ton of garbage (Cons space and Other space).  It should
 ;; not be used for common operations, such as s-value.
 ;;
 ;;; (defun check-slot-type (object slot value &optional (error-p T))
 ;;;   (loop
 ;;;    (restart-case
 ;;;     (return
 ;;;       (let ((type (get-slot-type-code object slot)))
 ;;; 	(if type
 ;;; 	  (if (zerop type)
 ;;; 	    (values T NIL)		; no type specified
 ;;; 	    (if (check-kr-type value type)
 ;;; 	      (values T NIL)
 ;;; 	      (let* ((readable-type (get-type-documentation type))
 ;;; 		     (message
 ;;; 		      (format
 ;;; 		       nil
 ;;; 		       "bad KR type: value ~S~:[~*~;, a ~S,~] is not valid for slot ~S in~%  object ~S.  The slot is declared of type ~S~@[,~%  i.e., ~A~].~@[~%  The value was computed by a formula.~]~%"
 ;;; 		       value value (type-of value) slot object
 ;;; 		       (code-to-type type)
 ;;; 		       readable-type
 ;;; 		       (formula-p (get-value object slot)))))
 ;;; 		(if error-p
 ;;; 		  (error message)
 ;;; 		  message))))
 ;;; 	  (values T NIL))))
 ;;;     ;; Allow the user to specify different continuation strategies if we
 ;;;     ;; get an error and enter the debugger.
 ;;;     (nil (arg)
 ;;; 	 :report "Retain old value in the slot"
 ;;; 	 :interactive (lambda ()
 ;;; 			(list value))
 ;;; 	 arg
 ;;; 	 (return (values NIL T)))
 ;;;     (nil (arg)
 ;;; 	 :report "Enter replacement value for the slot"
 ;;; 	 :interactive (lambda ()
 ;;; 			(format t "New value for ~S slot ~S: " object slot)
 ;;; 			(force-output)
 ;;; 			(list (read)))
 ;;; 	 (multiple-value-bind (new-value result)
 ;;; 	     (check-slot-type object slot arg T)
 ;;; 	   (cond ((null result)
 ;;; 		  ;; no error in replacement value
 ;;; 		  (return (values arg :REPLACE)))
 ;;; 		 ((eq result :REPLACE)
 ;;; 		  (return (values new-value :REPLACE)))))))))
 
 
 (defun set-slot-type (object slot type)
   (let ((entry (or (slot-accessor object slot)
 		   (set-slot-accessor object slot *no-value* type NIL))))
     (setf (sl-bits entry)
 	  (logior (logand (sl-bits entry) *all-bits-mask*) type))))
 
 
 (defun s-type (object slot type &optional (check-p T))
   "Adds a type declaration to the given <slot>, eliminating any previous
 type declarations.  If <check-p> is true, checks whether the value
 already in the <slot> satisfies the new type declaration."
   (set-slot-type object slot (if type
 			       (encode-type type)
 			       ;; 0 means "no type declarations"
 			       0))
   (when check-p
     (let* ((entry (slot-accessor object slot))
 	   (value (if entry (sl-value entry))))
       (unless (or (eq value *no-value*)
 		  (formula-p value))
 	(multiple-value-bind (new-value result)
 	    (check-slot-type object slot value T entry)
 	  (cond ((eq result :REPLACE)
 		 (s-value object slot new-value)))))))
   type)
 
 
 
 ;;; DECLARATION ACCESSORS
 
 
 (defun get-declarations (schema declaration)
   "RETURNS: a list of all slots in the <schema> which are declared as
 <declaration>.
 
 Example: (get-declarations A :type)"
   (let ((slots nil))
     (case declaration
       (:CONSTANT nil)
       (:TYPE nil)
       ((:IGNORED-SLOTS :SORTED-SLOTS :MAYBE-CONSTANT
 		       :PARAMETERS :OUTPUT :UPDATE-SLOTS)
        (return-from get-declarations (g-value schema declaration)))
       (:LOCAL-ONLY-SLOTS
        (return-from get-declarations (g-local-value schema declaration)))
       (t
        (return-from get-declarations NIL)))
     ;; Visit all slots, construct information
     (iterate-slot-value (schema T T NIL)
       value ;; suppress warning
       (let ((bits (sl-bits iterate-slot-value-entry)))
 	(case declaration
 	  (:CONSTANT
 	   (when (is-constant bits)
 	     (push slot slots)))
 	  (:TYPE
 	   (let ((type (extract-type-code bits)))
 	     (unless (zerop type)
 	       (push (list slot (code-to-type type)) slots)))))))
     slots))
 
 
 (defun get-slot-declarations (schema slot)
   (let* ((entry (slot-accessor schema slot))
 	 (bits (if entry (sl-bits entry) 0))
 	 (declarations nil))
     (if (is-constant bits)
       (push :CONSTANT declarations))
     (if (memberq slot (g-value schema :update-slots))
       (push :UPDATE-SLOTS declarations))
     (if (memberq slot (g-value schema :local-only-slots))
       (push :LOCAL-ONLY-SLOTS declarations))
     (if (memberq slot (g-value schema :maybe-constant))
       (push :MAYBE-CONSTANT declarations))
     (let ((type (extract-type-code bits)))
       (unless (zerop type)
 	(push (list :type (code-to-type type)) declarations)))
     ;; Now process all declarations that are not stored in the slot bits.
     (dolist (s-slot '(:IGNORED-SLOTS :PARAMETERS :OUTPUT :SORTED-SLOTS))
       (let ((values (g-value schema s-slot)))
 	(if (memberq slot values)
 	  (push s-slot declarations))))
     declarations))
 
 
 
 ;;; Define support fns for basic builtin types.  These definitions must come
 ;;  before the file CONSTRAINTS.LISP is compiled.
 
 
 (defun T-P (value)
   (declare #.*special-kr-optimization*
 	   (ignore value))
   T)
 
 (defun no-type-error-p (value)
   (cerror "Return T"
           "KR typechecking called on value ~S with no type"
           value)
   T)
 
 
 (defun get-type-definition (type-descriptor)
   "Given the symbol which names a KR type (e.g., 'KR-BOOLEAN), this function
 returns the type expression that was used to define the type.
 
 Example:
  (base-type-for 'bitmap-or-nil) ==>
  (OR NULL (IS-A-P OPAL:BITMAP))
 "
   (let* ((name (if (symbolp type-descriptor) (symbol-name type-descriptor)))
 	 (code (gethash name kr::types-table)))
     (when name
       (maphash #'(lambda (key value)
 		   (when (and (eq value code)
 			    (not (stringp key)))
 		       (return-from get-type-definition key)))
 	       kr::types-table))))
 
 
 
 ;;; FORMULA META-SLOTS
 
 (defun find-meta (formula)
   "Returns, or inherits, the meta-schema associated with the <formula>, or
 NIL if none exists."
   (let ((meta (a-formula-meta formula)))
     (unless meta
       ;; Try to inherit the meta-information from the formula's parent(s).
       (do ((f (a-formula-is-a formula) (a-formula-is-a f)))
 	  ((null f))
 	(if (setf meta (a-formula-meta f))
 	    ;; Do not copy down the meta-schema, to reduce storage.
 	    (return))))
     meta))
 
 
 (defun g-formula-value (formula slot)
   "RETURNS:
 the value of the meta-slot <slot> in the <formula>, or NIL if none
 exists.  The value may be inherited from the <formula>'s parent(s), but
 no new meta-schema is created as a result of this operation."
   (when (formula-p formula)
     (case slot
       (:NUMBER (a-formula-number formula))
       (:VALID (cache-is-valid formula))
       (:DEPENDS-ON (a-formula-depends-on formula))
       (:SCHEMA (a-formula-schema formula))
       (:SLOT (a-formula-slot formula))
       (:CACHED-VALUE (a-formula-cached-value formula))
       (:PATH (a-formula-path formula))
       (:IS-A (a-formula-is-a formula))
       (:FUNCTION (a-formula-function formula))
       (:LAMBDA (a-formula-lambda formula))
       (:IS-A-INV (a-formula-is-a-inv formula))
       (:META (a-formula-meta formula))
       (T
        ;; Normal case: this is not a built-in formula slot.  Use meta-slots.
        (let ((meta (find-meta formula)))
 	 (if meta
 	     (g-value meta slot)))))))
 
 
 (defun s-formula-value (formula slot value)
   "Sets the value of the meta-slot <slot> in the <formula> to be <value>.
 If no meta-schema exists for the <formula>, creates one."
   (when (formula-p formula)
     (let ((meta (a-formula-meta formula)))
       (unless meta
 	(setf meta (find-meta formula))
 	(if meta
 	    ;; We do have a meta-schema that can be inherited.
 	    (let ((new (create-schema nil)))
 	      (s-value new :is-a (list meta))
 	      (setf meta new))
 	    ;; Create a brand new, non-inheriting meta-schema.
 	    (setf meta (create-schema nil)))
 	;; Install the new meta-schema.
 	(setf (a-formula-meta formula) meta))
       (s-value meta slot value))))
 
 
 ;;; READER MACROS
 
 
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (defun k-reader (stream subchar arg)
     "Modify the readtable so #k<NAME> is read as the KR object NAME, if
 defined.  This allows objects written with the *print-as-structure*
 notation to be read back in."
     (declare (ignore subchar arg))
     (let ((next-char (read-char stream)))
       (if (char= next-char #\<)
 	  ;; This is a KR #k<...> object name
 	  (let ((string ""))
 	    (do ((c (read-char stream) (read-char stream)))
 		((char= c #\>))
 	      (setf string (format nil "~A~C" string c)))
 	    (setf string (read-from-string string))
 	    (if (and (boundp string)
 		     (or (schema-p (symbol-value string))
 			 (formula-p (symbol-value string))))
 		(symbol-value string)
 		(cerror "Ignore the object"
 			"Non-existing KR object: ~S" string)))
 	  ;; This is something else
 	  (cerror
 	   "Ignore the token"
 	   "  Illegal character ~S after reader macro #k (expecting \"<\")"
 	   next-char)))))
 
 
 ;; Install this reader macro in the standard readtable.
 (eval-when (:execute :compile-toplevel :load-toplevel)
   (set-dispatch-macro-character #\# #\k (function k-reader)))
 
 (defun o-formula-reader (stream subchar arg)
   "Modify the readtable to #f(...) is read as (o-formula ...).  For example,
 this allows you to write
 (S-VALUE A :LEFT #F(GVL :TOP))"
   (declare (ignore subchar arg))
   `(o-formula ,(read stream t nil t)))
 
 (set-dispatch-macro-character #\# #\f #'o-formula-reader)