;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp; Patch-File: Yes -*- ;;; ;;; ************************************************************************* ;;; 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. ;;; ************************************************************************* ;;; (in-package 'pcl) (eval-when (compile load eval) (fix-early-generic-functions)) ;; ;;;;;; Pending defmeths which I couldn't do before. ;; (eval-when (load eval) (setf (symbol-function 'print-instance) (symbol-function 'print-object))) ;; ;;;;;; ;; (define-method-body-macro call-next-method () :global :error :method (expand-call-next-method (macroexpand-time-method macroexpand-time-environment) nil macroexpand-time-environment)) (defmethod expand-call-next-method ((mex-method standard-method) args mti) (declare (ignore args)) (let* ((arglist (method-arglist mex-method)) (uid (macroexpand-time-method-uid mti)) (applyp nil)) (multiple-value-setq (arglist applyp) (make-call-arguments arglist)) (cond ((null (method-type-specifiers mex-method)) (warn "Using call-next-method in a default method.~%~ At run time this will generate an error.") `(error "Using call-next-method in a default method.")) (applyp `(apply #'call-next-method-internal ,uid . ,arglist)) (t `(call-next-method-internal ,uid . ,arglist))))) (defun call-next-method-internal (current-method &rest args) (let* ((generic-function (method-generic-function current-method)) (type-specifiers (method-type-specifiers current-method)) (most-specific nil) (most-specific-type-specifiers ()) (dispatch-order (slot-value--funcallable-standard-class (class-of generic-function) generic-function 'dispatch-order nil nil))) (iterate ((method in (generic-function-methods generic-function))) (let ((method-type-specifiers (method-type-specifiers method)) (temp ())) (and (every #'(lambda (arg type-spec) (or (eq type-spec 't) (memq type-spec (slot-value--class (class-of arg) 'class-precedence-list)))) args method-type-specifiers) (eql 1 (setq temp (compare-type-specifier-lists type-specifiers method-type-specifiers () args () dispatch-order))) (or (null most-specific) (eql 1 (setq temp (compare-type-specifier-lists method-type-specifiers most-specific-type-specifiers () args () dispatch-order)))) (setq most-specific method most-specific-type-specifiers method-type-specifiers)))) (if (or most-specific (setq most-specific (generic-function-default-method generic-function))) (apply (method-function most-specific) args) (error "no super method found")))) ;;; ;;; This is kind of bozoid because it always copies the lambda-list even ;;; when it doesn't need to. It also doesn't remember things it could ;;; remember, causing it to call memq more than it should. Fix this one ;;; day when there is nothing else to do. ;;; (defun make-call-arguments (lambda-list &aux applyp) (setq lambda-list (reverse lambda-list)) (when (memq '&aux lambda-list) (setq lambda-list (cdr (memq '&aux lambda-list)))) (setq lambda-list (nreverse lambda-list)) (let ((optional (memq '&optional lambda-list))) (when optional ;; The &optional keyword appears in the lambda list. ;; Get rid of it, by moving the rest of the lambda list ;; up, then go through the optional arguments, replacing ;; them with the real symbol. (setf (car optional) (cadr optional) (cdr optional) (cddr optional)) (iterate ((loc on optional)) (when (memq (car loc) lambda-list-keywords) (unless (memq (car loc) '(&rest &key &allow-other-keys)) (error "The non-standard lambda list keyword ~S appeared in the~%~ lambda list of a method in which CALL-NEXT-METHOD is used.~%~ PCL can only deal with standard lambda list keywords.")) (when (listp (car loc)) (setf (car loc) (caar loc))))))) (let ((rest (memq '&rest lambda-list))) (cond ((not (null rest)) ;; &rest appears in the lambda list. This means we ;; have to do an apply. We ignore the rest of the ;; lambda list, just grab the &rest var and set applyp. (setf (car rest) (if (listp (cadr rest)) (caadr rest) (cadr rest)) (cdr rest) ()) (setq applyp t)) (t (let ((key (memq '&key lambda-list))) (when key ;; &key appears in the lambda list. Remove &key from the ;; lambda list then replace all the keywords with pairs of ;; the actual keyword followed by the value variable. ;; Have to parse the hairy triple case of &key. (let ((key-args (iterate ((arg in (cdr key))) (until (eq arg '&allow-other-keys)) (cond ((symbolp arg) (collect (make-keyword arg)) (collect arg)) ((cddr arg) (collect (caddr arg)) (collect (car arg))) (t (collect (make-keyword (car arg))) (collect (car arg))))))) (if key-args (setf (car key) (car key-args) (cdr key) (cdr key-args)) (setf (cdr key) nil lambda-list (remove '&key lambda-list))))))))) (values lambda-list applyp))