;;;-*-Mode:LISP; Package:(PCL (LISP WALKER)); Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987 Xerox Corporation.  All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted.  Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;; 
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;; 
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;;   CommonLoops Coordinator
;;;   Xerox Artifical Intelligence Systems
;;;   2400 Hanover St.
;;;   Palo Alto, CA 94303
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
;;; Non-Bootstrap stuff
;;;

(in-package 'pcl)

(defclass forward-referenced-class (standard-class) ())

(defmethod check-super-metaclass-compatibility ((c standard-class)
						(f forward-referenced-class))
  (declare (ignore c f))
  't)

(defmethod class-for-redefinition ((existing-class forward-referenced-class)
				   (proto-class standard-class)
				   name
				   supers
				   slots
				   options)
  (declare (ignore name supers slots options))
  (change-class existing-class (class-of proto-class))
  existing-class)

(defclass obsolete-class (standard-class) ())


(defmethod slot-value-using-class ((class obsolete-class)
				   object
				   slot-name
				   &optional dont-call-slot-missing-p
					     default)
  (change-class object
		(cadr (slot-value class 'class-precedence-list)))
  (slot-value-using-class
    (class-of object) object slot-name dont-call-slot-missing-p default))



;;;
;;;
;;;

(defclass built-in-class (standard-class) ())

(defmethod inform-type-system-about-class ((class built-in-class) name)
  (declare (ignore class name)))

(defmethod allocate-instance ((class built-in-class))
  (error
    "Attempt to make an instance of the built-in class ~S.~%~
     It is not possible to make instance of built-in classes with ~
     allocate-instance."
    class))

(defmethod check-super-metaclass-compatibility ((bic built-in-class)
						(new-super standard-class))
  (or (eq new-super (class-named 't))
      (error "~S cannot have ~S as a super.~%~
              The only meta-class STANDARD-CLASS class that a built-in~%~
              class can have as a super is the class T."
	     bic new-super)))


(defmethod check-super-metaclass-compatibility ((class built-in-class)
						(new-super built-in-class))
  (declare (ignore class new-super))
  't)

(defmethod check-super-metaclass-compatibility
	   ((built-in built-in-class)
	    (new-super forward-referenced-class))
  (declare (ignore built-in new-super))
  't)


;;;
;;;
;;;
(defvar *built-in-class-lattice* ())

(defmacro define-built-in-classes (classes-and-supers &optional reset-p)
  `(eval-when (eval load)
     (when ,reset-p (setq *built-in-class-lattice* ()))
     (dolist (bic ,classes-and-supers)
       (pushnew bic *built-in-class-lattice* :test #'equal))     
     (define-built-in-classes-1)
     (setf (symbol-function 'built-in-class-of)
	   #'(lambda (x)
	       (declare (notinline built-in-class-of))
	       (compile 'built-in-class-of (make-built-in-class-of))
	       (built-in-class-of x)))))

(defun define-built-in-classes-1 ()
  ;; First make sure that all the supers listed in *built-in-class-lattice*
  ;; are themselves defined by *built-in-class-lattice*.  This is just to
  ;; check for typos and other sorts of brainos.
  ;; 
  ;; At the same time make sure the subtype relationship specified here in
  ;; *built-in-class-lattice* agrees with the subtype relationship in this
  ;; Lisp.
  (dolist (e *built-in-class-lattice*)
    (dolist (super (cadr e))
;      (unless (and (eq (subtypep (car e) super) 't)	;*** Lame, should
;		   (eq (subtypep super (car e)) 'nil))	;*** check supers.
;	(warn "*built-in-class-lattice* and this Lisp may disagree about~%~
;               the type relationship between ~S and ~S."
;	       (car e) super))
      (unless (or (eq super 't)
		  (assq super *built-in-class-lattice*))
	(error "In *built-in-class-lattice*: ~S has ~S as a super,~%~
                but ~S is not itself a class in *built-in-class-lattice*."
	       (car e) super super))))

  ;; Now use add-named-class to define the built-in class as specified.
  ;; 
  (let ((proto (class-prototype (class-named 'built-in-class))))
    (dolist (e *built-in-class-lattice*)
      (add-named-class proto (car e) (cadr e) () ())))
  )

(defun make-built-in-class-tree ()
  (let ((tree (list 't)))
    (labels ((insert-subs (node type)
	       (dolist (e *built-in-class-lattice*)
		 (let ((e-type (car e))
		       (e-supers (cadr e)))
		   (when (and (memq type e-supers)
			      (not (member e-type (cdr node) :key #'car)))
		     (let ((new-node (list e-type)))
		       (push new-node (cdr node))
		       (insert-subs new-node e-type)))))))
      (insert-subs tree 't)
      tree)))


(defun make-built-in-class-of ()  
  `(lambda (x) ,(make-built-in-class-of-1 (make-built-in-class-tree) 'x)))

(defun make-built-in-class-of-1 (tree var)
  `(and (typep ,var ',(car tree))
	(or ,@(mapcar #'(lambda (x) (make-built-in-class-of-1 x var))
		      (cdr tree))
	    ,(class-constant-form (class-named (car tree)))
	    )))


(define-built-in-classes  '((array             (t))
			    (bit-vector        (vector))
			    (character         (t))
;     	                    (compiled-function (t))
			    (complex           (number))
			    (cons              (list))
			    (float             (number))
			    (integer           (rational))
			    (list              (sequence))
;	                    (nil               ())            ;*** fix this
			    (null              (list symbol))
			    (number            (t))
			    (ratio             (rational))
			    (rational          (number))
			    (sequence          (t))
			    (string            (vector))
			    (symbol            (t))
			    (vector            (array sequence))))


;;;
;;;
;;;
(defmethod describe-class (class-or-class-name
			  &optional (stream *standard-output*))
  (flet ((pretty-class (class) (or (class-name class) class)))
    (if (symbolp class-or-class-name)
	(describe-class (class-named class-or-class-name) stream)
	(let ((class class-or-class-name))
	  (format stream
		  "~&The class ~S is an instance of class ~S."
		  class
		  (class-of class))
	  (format stream "~&Name:~23T~S~%~
			    Class-Precedence-List:~23T~S~%~
                            Local-Supers:~23T~S~%~
                            Direct-Subclasses:~23T~S"
		  (class-name class)
		  (mapcar #'pretty-class (class-class-precedence-list class))
		  (mapcar #'pretty-class (class-local-supers class))
		  (mapcar #'pretty-class (class-direct-subclasses class)))
	  class))))

(defun describe-instance (object &optional (stream t))
  (let* ((class (class-of object))
         (instance-slots (class-instance-slots class))
         (non-instance-slots (class-non-instance-slots class))
         (dynamic-slots (iwmc-class-dynamic-slots object))
	 (max-slot-name-length 0))
    (macrolet ((adjust-slot-name-length (name)
		 `(setq max-slot-name-length
			(max max-slot-name-length
			     (length (the string (symbol-name ,name))))))
	       (describe-slot (name value &optional (allocation () alloc-p))
		 (if alloc-p
		     `(format stream
			      "~% ~A ~S ~VT  ~S"
			      ,name ,allocation (+ max-slot-name-length 7)
			      ,value)
		     `(format stream
			      "~% ~A~VT  ~S"
			      ,name max-slot-name-length ,value))))
      ;; Figure out a good width for the slot-name column.
      (iterate ((slotd in instance-slots))
	(adjust-slot-name-length (slotd-name slotd)))      
      (iterate ((slotd in non-instance-slots))
	(adjust-slot-name-length (slotd-name slotd)))
      (iterate ((name in dynamic-slots by cddr))
	(adjust-slot-name-length name))
      (setq max-slot-name-length  (min (+ max-slot-name-length 3) 30))
      (format stream "~%~S is an instance of class ~S:" object class)
      (format stream "~% The following slots are allocated in the instance ~
                         (:INSTANCE allocation):")
      (iterate ((slotd in instance-slots))
	(let ((name (slotd-name slotd)))
	  (describe-slot name (slot-value object name))))
      (when (or dynamic-slots
		(iterate ((slotd in non-instance-slots))
		  (when (neq (slotd-allocation slotd) :dynamic) (return t))))
	(format stream
		"~%The following slots have special allocations as shown:")
	(iterate ((slotd in non-instance-slots))
	  (unless (eq (slotd-allocation slotd) :dynamic)
	    (describe-slot (slotd-name slotd)
			   (slot-value object (slotd-name slotd))
			   (slotd-allocation slotd))))
	(iterate ((name in dynamic-slots by cddr)
		  (val in (cdr dynamic-slots) by cddr))
	  (describe-slot name val :dynamic)))))
  object)



;;;
;;; trace-method and untrace-method accept method specs as arguments.  A
;;; method-spec should be a list like:
;;;   (<generic-function-spec> qualifiers* (specializers*))
;;; where <generic-function-spec> should be either a symbol or a list
;;; of (SETF <symbol>).
;;;
;;;   For example, to trace the method defined by:
;;;
;;;     (defmethod foo ((x spaceship)) 'ss)
;;;
;;;   You should say:
;;;
;;;     (trace-method '(foo (spaceship)))
;;;
;;;   You can also provide a method object in the place of the method
;;;   spec, in which case that method object will be traced.
;;;
;;; For untrace-method, if an argument is given, that method is untraced.
;;; If no argument is given, all traced methods are untraced.
;;;

(defvar *traced-methods* ())

(defun trace-method (spec)

  (multiple-value-bind (gf method name)
      (parse-method-or-spec spec)
    (put-slot-always method 'untrace (list name (method-function standard-method)))
    (push method *traced-methods*)
    (trace-method-internal gf method name)
    method))

(defun untrace-method (&optional spec)  
  (flet ((untrace-it (m)
	   (let ((untrace (and (slot-exists-p m 'untrace)
			       (slot-value m 'untrace))))
	     (if untrace
		 (progn
		   (eval `(untrace ,(car untrace)))		   
		   (setf (method-function m) (cadr untrace))
		   (remove-dynamic-slot m 'untrace))
		 (error "~S is not a traced method?" m)))))
    (cond ((not (null spec))
	   (multiple-value-bind (gf method)
	       (parse-method-or-spec spec)
	     (when (method-generic-function method)
	       (untrace-it method)
	       (setq *traced-methods* (delete method *traced-methods*))
	       (list *traced-methods*))))
	  (t
	   (dolist (m *traced-methods*)
	     (untrace-it m))
	   (prog1 *traced-methods*
		  (setq *traced-methods* ()))))))

(defun trace-method-internal (gf method name)
  (let ((function (method-function method)))
    (eval `(untrace ,name))
    (setf (symbol-function name) function)
    (eval `(trace ,name))
    (setf (method-function method) (symbol-function name))))




(defun compile-method (spec)
  (multiple-value-bind (gf method name)
      (parse-method-or-spec spec)
    (declare (ignore gf))
    (compile name (method-function method))
    (setf (method-function method) (symbol-function name))))

(defmacro undefmethod (&rest args)
  (declare (arglist name {method-qualifier}* specializers))
  (multiple-value-bind (gf method)
      (parse-method-or-spec args)
    (remove-method gf method)
    method))

(defmacro undefmethod-setf (&rest args)
  (declare (arglist name {method-qualifier}* specializers setf-specializers))
  (multiple-value-bind (name qualifiers specls setf-specls)
      (parse-defmethod args t)
    (multiple-value-bind (gf method)
	(parse-method-or-spec `((setf ,name)
				,@qualifiers
				(,@specls ,@setf-specls)))
      (remove-method gf method)
      method)))