home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume11
/
comobj.pch
< prev
next >
Wrap
Lisp/Scheme
|
1987-09-14
|
56KB
|
1,516 lines
Subject: v11i044: Patch for Common objects sources
Newsgroups: comp.sources.unix
Sender: sources
Approved: rs@uunet.UU.NET
Submitted-by: hplabs!hplabsz!kempf@rutgers.edu
Posting-number: Volume 11, Issue 44
Archive-name: comobj.pch
[ New versions of two files that were mostly mangled last time. --r$ ]
-----CUT-----HERE---
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# This archive contains:
# fixup.l
# methods.l
# Error checking via wc(1) will be performed.
LANG=""; export LANG
echo x - fixup.l
cat >fixup.l <<'@EOF'
;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp; Patch-File: Yes -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985 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)
(setq *real-methods-exist-p* nil)
(setf (symbol-function 'expand-defmeth)
(symbol-function 'real-expand-defmeth)))
(eval-when (load)
(clrhash *discriminator-name-hash-table*)
(fix-early-defmeths)
;; This now happens at the end of loading HIGH to make it
;; possible to compile and load pcl in the same environment.
;(setq *error-when-defining-method-on-existing-function* t)
)
(eval-when (compile load eval)
(setq *real-methods-exist-p* t))
;;
;;;;;; Pending defmeths which I couldn't do before.
;;
(eval-when (load eval)
(setf (discriminator-named 'print-instance) ())
(make-specializable 'print-instance :arglist '(instance stream depth)))
(defmeth print-instance ((instance object) stream depth)
(let ((length (if (numberp *print-length*) (* *print-length* 2) nil)))
(format stream "#S(~S" (class-name (class-of instance)))
(iterate ((slot-or-value in (all-slots instance))
(slotp = t (not slotp)))
(when (numberp length)
(cond ((<= length 0) (format stream " ...") (return ()))
(t (decf length))))
(princ " " stream)
(let ((*print-level* (cond ((null *print-level*) ())
(slotp 1)
(t (- *print-level* depth)))))
(if (and *print-level* (<= *print-level* 0))
(princ "#" stream)
(prin1 slot-or-value stream))))
(princ ")" stream)))
(defmeth print-instance ((class essential-class) stream depth)
(named-object-print-function class stream depth))
(defmethod print-instance ((method essential-method) stream depth)
(ignore depth)
(printing-random-thing (method stream)
(let ((discriminator (method-discriminator method))
(class-name (capitalize-words (class-name (class-of method)))))
(format stream "~A ~S ~:S"
class-name
(and discriminator (discriminator-name discriminator))
(method-type-specifiers method)))))
(defmethod print-instance ((method basic-method) stream depth)
(ignore depth)
(printing-random-thing (method stream)
(let ((discriminator (method-discriminator method))
(class-name (capitalize-words (class-name (class-of method)))))
(format stream "~A ~S ~:S"
class-name
(and discriminator (discriminator-name discriminator))
(unparse-type-specifiers method)))))
(defmethod print-instance ((discriminator essential-discriminator) stream depth)
(named-object-print-function discriminator stream depth))
(defmethod print-instance ((discriminator basic-discriminator) stream depth)
(named-object-print-function
discriminator stream depth (list (method-combination-type discriminator))))
(eval-when (load)
(define-meta-class essential-class (lambda (x) (%instance-ref x 0)))
(defmeth class-slots ((class essential-class))
(ignore class)
())
(defmeth make-instance ((class essential-class))
(let ((primitive-instance
(%make-instance (class-named 'essential-class)
(1+ (length (class-slots class))))))
(setf (%instance-ref primitive-instance 0) class)
primitive-instance))
(defmeth get-slot-using-class ((class essential-class) object slot-name)
(let ((pos (position slot-name (class-slots class) :key #'slotd-name)))
(if pos
(%instance-ref object (1+ pos))
(slot-missing ;class
object slot-name))))
(defmeth put-slot-using-class ((class essential-class)
object
slot-name
new-value)
(let ((pos (position slot-name (class-slots class) :key #'slotd-name)))
(if pos
(setf (%instance-ref object (1+ pos)) new-value)
(slot-missing ;class
object slot-name))))
(defmeth optimize-get-slot (class form)
(declare (ignore class))
form)
(defmeth optimize-setf-of-get-slot (class form)
(declare (ignore class))
form)
(defmeth make-slotd ((class essential-class) &rest keywords-and-options)
(ignore class)
(apply #'make-slotd--essential-class keywords-and-options))
(defmeth add-named-class ((proto-class essential-class) name
local-supers
local-slot-slotds
extra)
;; First find out if there is already a class with this name.
;; If there is, call class-for-redefinition to get the class
;; object to use for the new definition. If there is no exisiting
;; class we just make a new instance.
(let* ((existing (class-named name t))
(class (if existing
(class-for-redefinition existing proto-class name
local-supers local-slot-slotds
extra)
(make (class-of proto-class)))))
(setq local-supers
(mapcar
#'(lambda (ls)
(or (class-named ls t)
(error "~S was specified as the name of a local-super~%~
for the class named ~S. But there is no class~%~
class named ~S." ls name ls)))
local-supers))
(setf (class-name class) name)
; (setf (class-ds-options class) extra) ;This is NOT part of the
; ;standard protocol.
(add-class class local-supers local-slot-slotds extra)
(setf (class-named name) class)
name))
(defmeth supers-changed ((class essential-class)
old-local-supers
old-local-slots
extra
top-p)
(ignore old-local-supers old-local-slots top-p)
(let ((cpl (compute-class-precedence-list class)))
(setf (class-class-precedence-list class) cpl)
; (update-slots--class class cpl) ;This is NOT part of
; ;the essential-class
; ;protocol.
(dolist (sub-class (class-direct-subclasses class))
(supers-changed sub-class
(class-local-supers sub-class)
(class-local-slots sub-class)
extra
nil))
; (when top-p ;This is NOT part of
; (update-method-inheritance class old-local-supers));the essential-class
; ;protocol.
))
(defmeth slots-changed ((class essential-class)
old-local-slots
extra
top-p)
(ignore top-p old-local-slots)
;; When this is called, class should have its local-supers and
;; local-slots slots filled in properly.
; (update-slots--class class (class-class-precedence-list class))
(dolist (sub-class (class-direct-subclasses class))
(slots-changed sub-class (class-local-slots sub-class) extra nil)))
(defmeth method-equal (method argument-specifiers options)
(ignore options)
(equal argument-specifiers (method-type-specifiers method)))
(defmeth methods-combine-p ((d essential-discriminator))
(ignore d)
nil)
)
;;
;;;;;;
;;
(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 method) args mti)
(ignore args)
(let* ((arglist (and mex-method (method-arglist mex-method)))
(uid (macroexpand-time-method-uid mti))
(load-method-1-args (macroexpand-time-load-method-1-args mti))
(load-time-eval-form `(load-time-eval
(if (boundp ',uid)
,uid
(setq ,uid
(apply #'load-method-1
',load-method-1-args)))))
(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 ,load-time-eval-form . ,arglist))
(t
`(call-next-method-internal ,load-time-eval-form . ,arglist)))))
(defun call-next-method-internal (current-method &rest args)
(let* ((discriminator (method-discriminator current-method))
(type-specifiers (method-type-specifiers current-method))
(most-specific nil)
(most-specific-type-specifiers ())
(dispatch-order (get-slot--class discriminator 'dispatch-order)))
(iterate ((method in (discriminator-methods discriminator)))
(let ((method-type-specifiers (method-type-specifiers method))
(temp ()))
(and (every #'(lambda (arg type-spec)
(or (eq type-spec 't)
(memq type-spec
(get-slot--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 (discriminator-default-method
discriminator)))
(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))
@EOF
if test "`wc -lwc <fixup.l`" != ' 355 1302 12760'
then
echo ERROR: wc results of fixup.l are `wc -lwc <fixup.l` should be 355 1302 12760
fi
chmod 666 fixup.l
LANG=""; export LANG
echo x - methods.l
cat >methods.l <<'@EOF'
;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985 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)
;;
;;;;;; Methods
;;
(ndefstruct (essential-method
(:class class)
(:conc-name method-))
(discriminator nil)
(arglist ())
(type-specifiers ())
(function nil))
(ndefstruct (combinable-method-mixin (:class class)))
(ndefstruct (basic-method
(:class class)
(:include (essential-method))
(:constructor make-method-1)
(:conc-name method-))
(function nil)
(discriminator nil)
(type-specifiers ())
(arglist ())
(options () :allocation :dynamic))
(ndefstruct (method (:class class)
(:include (combinable-method-mixin
basic-method))))
(ndefstruct (essential-discriminator
(:class class)
(:conc-name discriminator-))
(name nil)
(methods ())
(discriminating-function ())
(classical-method-table nil :allocation :dynamic)
(cache ()))
(ndefstruct (method-combination-mixin (:class class)
(:conc-name nil))
(method-combination-type :daemon)
(method-combination-parameters ())
(methods-combine-p ())
)
(ndefstruct (basic-discriminator
(:class class)
(:include (essential-discriminator))
(:constructor make-discriminator-1)
(:conc-name discriminator-))
(dispatch-order :default)
(inactive-methods () :allocation :dynamic))
(ndefstruct (discriminator (:class class)
(:include (method-combination-mixin
basic-discriminator)))
)
;;;
;;; This is really just for bootstrapping, of course this isn't all
;;; worked out yet. But this SHOULD really just be for bootstrapping.
;;;
(defmeth method-causes-combination-p ((method basic-method))
(ignore method)
())
;;
;;;;;;
;;
(defun real-expand-defmeth (name&options arglist body)
(unless (listp name&options) (setq name&options (list name&options)))
(keyword-parse ((discriminator-class 'discriminator)
(method-class 'method))
(cdr name&options)
(dolist (x '(:discriminator-class :method-class))
(delete x name&options :test #'(lambda (x y)
(and (listp y) (eq (car y) x)))))
(let ((discriminator-class-object (class-named discriminator-class t))
(method-class-object (class-named method-class t)))
(or discriminator-class-object ;
(error
"The :DISCRIMINATOR-CLASS option to defmeth was used to specify~
that the class~%of the discriminator should be ~S;~%~
but there is no class named ~S."
discriminator-class
discriminator-class))
(or method-class-object
(error "The :METHOD-CLASS option to defmeth was used to specify~%~
that the class of the method should be ~S;~%~
but there is no class named ~S."
method-class
method-class))
(expand-defmeth-internal (class-prototype discriminator-class-object)
(class-prototype method-class-object)
name&options
arglist
body))))
(defvar *method-being-defined*)
(defmeth expand-defmeth-internal ((proto-discriminator basic-discriminator)
(proto-method basic-method)
name&options arglist body)
(keyword-parse ((setf () setf-specified-p))
(cdr name&options)
(let* ((discriminator-class-name (class-name
(class-of proto-discriminator)))
(method-class-name (class-name (class-of proto-method)))
(name (car name&options))
(merged-arglist (cons (car arglist) (append setf (cdr arglist))))
(merged-args (arglist-without-type-specifiers proto-discriminator
proto-method
merged-arglist))
(merged-type-specifiers
(defmethod-argument-specializers arglist))
discriminator-name
method-name
(defmethod-uid (gensym))
(load-method-1 ())
(documentation ())
(declarations ()))
(if setf-specified-p
(setq discriminator-name (make-setf-discriminator-name name)
method-name (make-setf-method-name name
(arglist-type-specifiers
proto-discriminator
proto-method
setf)
merged-type-specifiers))
(setq discriminator-name name
method-name (make-method-name name
merged-type-specifiers)))
(multiple-value-setq (documentation declarations body)
(extract-declarations body))
(setq load-method-1 `(,discriminator-class-name
,method-class-name
,discriminator-name
,merged-type-specifiers
,merged-args
,(cdr name&options)))
;;
;; There are 4 cases:
;; - evaluated
;; - compiled to core
;; - compiled to file
;; - loading the compiled file
;;
;; When loading a method which has a run-super in it, there is no way
;; to know which of two events will happen first:
;; 1. the load-time-eval form in the run super will be
;; evaluated first, or
;; 2. the function to install the loaded method (defmethod-uid)
;; will be evaluated first.
;; consequently, both the special function (defmethod-uid) and the
;; expansion of run-super must check to see if the other has already
;; run and set the value of defmethod-uid to the method involved.
;; This is what causes the boundp checks of defmethod-uid each time
;; before it is set.
;;
`(progn
(eval-when (eval load)
(defun ,defmethod-uid ()
(declare (special ,defmethod-uid))
(unless (boundp ',defmethod-uid)
(setq ,defmethod-uid (apply #'load-method-1
',load-method-1)))
,@(and *real-methods-exist-p*
`((record-definition
',discriminator-name 'method
',merged-type-specifiers ',(cdr name&options))
(setf (symbol-function ',method-name)
#'(lambda ,merged-args
,@documentation
,@declarations
(declare (method-function-name ,method-name))
,(wrap-method-body
proto-discriminator
(apply 'compile-method-1 load-method-1)
discriminator-name
defmethod-uid
load-method-1
body)
))))
(setf (method-function ,defmethod-uid)
(symbol-function ',method-name))
(add-method (discriminator-named ',discriminator-name)
,defmethod-uid
()))
(,defmethod-uid))
(eval-when (compile load eval)
,@(and setf-specified-p
`((record-definition
',name 'defsetf ',discriminator-name 'defmeth)
(defsetf ,name
,(arglist-without-type-specifiers
proto-discriminator proto-method arglist)
,(arglist-without-type-specifiers
proto-discriminator proto-method setf)
(list ',discriminator-name ,@(arglist-args
proto-discriminator
proto-method
merged-args)))))
',discriminator-name)))))
(defmethod wrap-method-body ((mex-generic-function discriminator)
(mex-method method)
generic-function-name
method-uid
load-method-1-args
body)
(let ((macroexpand-time-information (list mex-generic-function
mex-method
generic-function-name
method-uid
load-method-1-args)))
`(macrolet ,(iterate (((name arglist params fn) in *method-body-macros*))
(collect `(,name ,arglist
(funcall (function ,fn)
',macroexpand-time-information
,@params))))
(block ,generic-function-name
. ,body))))
(defun macroexpand-time-generic-function (mti) (nth 0 mti))
(defun macroexpand-time-method (mti) (nth 1 mti))
(defun macroexpand-time-generic-function-name (mti) (nth 2 mti))
(defun macroexpand-time-method-uid (mti) (nth 3 mti))
(defun macroexpand-time-load-method-1-args (mti) (nth 4 mti))
(defun load-method-1 (discriminator-class-name
method-class-name
discriminator-name
method-type-specifiers
method-arglist
options)
(let* ((discriminator
(ensure-selector-specializable
(class-prototype (class-named discriminator-class-name))
discriminator-name
method-arglist))
(method
(or (find-method discriminator method-type-specifiers options t)
(make method-class-name))))
(setf (method-arglist method) method-arglist)
(setf (method-type-specifiers method)
(parse-type-specifiers
discriminator method method-type-specifiers))
(setf (method-options method) options)
method))
(defun compile-method-1 (discriminator-class-name
method-class-name
discriminator-name
method-type-specifiers
method-arglist
options)
(ignore discriminator-name)
(let ((method (make method-class-name)))
(setf (method-arglist method) method-arglist)
(setf (method-type-specifiers method)
(parse-type-specifiers
(class-prototype (class-named discriminator-class-name))
method
method-type-specifiers))
(setf (method-options method) options)
method))
(defmeth add-named-method ((proto-discriminator essential-discriminator)
(proto-method essential-method)
discriminator-name
arglist
type-specs
extra
function)
;; What about changing the class of the discriminator if there is
;; one. Whose job is that anyways. Do we need something kind of
;; like class-for-redefinition?
(let* ((discriminator
;; Modulo bootstrapping hair, this is just:
;; (or (discriminator-named ..)
;; (make-specializable))
(ensure-selector-specializable proto-discriminator
discriminator-name
arglist))
(existing (find-method discriminator type-specs extra t))
(method (or existing
(make (class-of proto-method)))))
(when existing (change-class method (class-of proto-method)))
(setf (method-arglist method) arglist)
(setf (method-function method) function)
(setf (method-type-specifiers method) type-specs)
(add-method discriminator method extra)))
(defmeth add-method ((discriminator essential-discriminator)
(method essential-method)
extra)
(ignore extra)
(let ((type-specs (method-type-specifiers method))
;(options (method-options method))
;(methods (discriminator-methods discriminator))
)
(setf (method-discriminator method) discriminator)
; ;; Put the new method where it belongs, either:
; ;; - The same (EQ) method object is already on discriminator-methods
; ;; of the discriminator so we don't need to do anything to put the
; ;; new methods where it belongs.
; ;; - There is an method on discriminator-methods which is equal to
; ;; the new method (according to METHOD-EQUAL). In this case, we
; ;; replace the existing method with the new one.
; ;; - We just add the new method to discriminator-methods by pushing
; ;; it onto that list.
; (unless (memq method methods)
; (do* ((tail (discriminator-methods discriminator) (cdr tail))
; (existing-method (car tail) (car tail)))
; ((cond ((null existing-method)
; (push method (discriminator-methods discriminator)))
; ((method-equal existing-method type-specs options)
; (remove-method discriminator existing-method)
; (return (add-method discriminator method))))
;
; (when (method-causes-combination-p method) ;NOT part of
; (pushnew method (methods-combine-p discriminator)));standard
; ;protocol.
; (dolist (argument-specifier type-specs)
; (add-method-on-argument-specifier discriminator
; method
; argument-specifier)))
; ()))
(pushnew method (discriminator-methods discriminator))
(dolist (argument-specifier type-specs)
(add-method-on-argument-specifier discriminator
method
argument-specifier)))
(discriminator-changed discriminator method t)
(update-pretty-arglist discriminator method) ;NOT part of
;standard protocol.
())
(defmeth remove-named-method (discriminator-name
argument-specifiers
&optional extra)
(let ((discriminator ())
(method ()))
(cond ((null (setq discriminator (discriminator-named
discriminator-name)))
(error "There is no discriminator named ~S." discriminator-name))
((null (setq method (find-method discriminator
argument-specifiers
extra
t)))
(error "There is no method for the discriminator ~S~%~
which matches the argument-specifiers ~S."
discriminator
argument-specifiers))
(t
(remove-method discriminator method)))))
(defmeth remove-method ((discriminator basic-discriminator) method)
(setf (method-discriminator method) nil)
(setf (discriminator-methods discriminator)
(delq method (discriminator-methods discriminator)))
(dolist (type-spec (method-type-specifiers method))
(remove-method-on-argument-specifier discriminator method type-spec))
(discriminator-changed discriminator method nil)
discriminator)
(defmeth add-method-on-argument-specifier
((discriminator essential-discriminator)
(method essential-method)
argument-specifier)
(ignore method)
(when (classp argument-specifier)
(pushnew method
(class-direct-methods argument-specifier))
;; This is a bug. This needs to be split up into a method on
;; essential class and a method on class or something.
(when (methods-combine-p discriminator)
(pushnew discriminator
(class-discriminators-which-combine-methods
argument-specifier)))))
(defmeth remove-method-on-argument-specifier
((discriminator essential-discriminator)
(method essential-method)
argument-specifier)
(ignore method)
(when (classp argument-specifier)
(setf (class-direct-methods argument-specifier)
(delq method
(class-direct-methods argument-specifier)))
(when (methods-combine-p discriminator)
(setf (class-discriminators-which-combine-methods
argument-specifier)
(delq discriminator
(class-discriminators-which-combine-methods
argument-specifier))))))
(defun make-specializable (function-name &rest options)
(when options (setq options (list* ':allow-other-keys t options)))
(keyword-bind ((arglist nil arglist-specified-p)
(discriminator-class 'discriminator)
(dispatch nil dispatch-p))
options
(cond ((not (null arglist-specified-p)))
((fboundp 'function-arglist)
;; function-arglist exists, get the arglist from it.
;; Note: the funcall of 'function-arglist prevents
;; compiler warnings at least in some lisps.
(setq arglist (funcall 'function-arglist function-name)))
((fboundp function-name)
(error
"The :arglist argument to make-specializable was not supplied~%~
and there is no version of FUNCTION-ARGLIST defined for this~%~
port of Portable CommonLoops.~%~
You must either define a version of FUNCTION-ARGLIST (which~%~
should be easy), and send it off to the Portable CommonLoops~%~
people or you should call make-specializable again with the~%~
function's arglist as its second argument.")))
(setq dispatch
(if dispatch-p
(iterate ((disp in dispatch))
(unless (memq disp arglist)
(error "There is a symbol in the :dispatch argument (~S)~%~
which isn't in the arglist."))
(collect (position disp arglist)))
:default))
(let ((discriminator-class-object
(if (classp discriminator-class)
discriminator-class
(class-named discriminator-class t)))
(discriminator nil))
(if (null discriminator-class-object)
(error
"The :DISCRIMINATOR-CLASS argument to make-specializable is ~S~%~
but there is no class by that name."
discriminator-class)
(setq discriminator
(apply #'make discriminator-class-object
:name function-name
:dispatch-order dispatch
options)))
; (setf (function-pretty-arglist function-name) arglist)
(if arglist-specified-p
(put-slot-always discriminator 'pretty-arglist arglist)
(remove-dynamic-slot discriminator 'pretty-arglist))
(setf (discriminator-named function-name) discriminator)
(when (fboundp function-name)
(add-named-method (class-prototype (class-named 'discriminator))
(class-prototype (class-named 'method))
function-name
arglist
()
()
(symbol-function function-name)))
discriminator)))
(defun update-pretty-arglist (discriminator method)
(setf (function-pretty-arglist
(or (discriminator-name discriminator)
(discriminator-discriminating-function discriminator)))
(or (get-slot-using-class (class-of discriminator) discriminator
'pretty-arglist t ())
(method-arglist method))))
(defmeth discriminator-pretty-arglist ((discriminator basic-discriminator))
(or (get-slot-using-class (class-of discriminator) discriminator
'pretty-arglist t ())
(let ((method (or (discriminator-default-method discriminator)
(car (discriminator-methods discriminator)))))
(and method (method-arglist method)))))
(defmeth ensure-selector-specializable ((proto-discriminator
essential-discriminator)
selector arglist)
(let ((discriminator (discriminator-named selector)))
(cond ((not (null discriminator)) discriminator)
((or (not (fboundp selector))
(eq *error-when-defining-method-on-existing-function*
'bootstrapping))
(setf (discriminator-named selector)
(make (class-of proto-discriminator) :name selector)))
((null *error-when-defining-method-on-existing-function*)
(make-specializable selector
:arglist arglist
:discriminator-class (class-of
proto-discriminator))
(discriminator-named selector))
(t
(error "Attempt to add a method to the lisp function ~S without~%~
first calling make-specializable. Before attempting to~
define a method on ~S~% you should evaluate the form:~%~
(~S '~S)"
selector selector 'make-specializable selector)))))
(defmeth find-method (discriminator type-specifiers options &optional parse)
(iterate ((method in (discriminator-methods discriminator)))
(when (method-equal method
(if parse
(parse-type-specifiers discriminator
method
type-specifiers)
type-specifiers)
options)
(return method))))
(defmeth method-equal ((method basic-method) argument-specifiers options)
(and (equal options (method-options method))
(equal argument-specifiers (method-type-specifiers method))))
(defmeth discriminator-default-method ((discriminator essential-discriminator))
(find-method discriminator () ()))
(defmeth install-discriminating-function ((discriminator
essential-discriminator)
where
function
&optional inhibit-compile-p)
(ignore discriminator)
(check-type where symbol "a symbol other than NIL")
(check-type function function "a funcallable object")
(when (and (listp function)
(eq (car function) 'lambda)
(null inhibit-compile-p))
(setq function (compile nil function)))
(if where
(setf (symbol-function where) function)
(setf (discriminator-discriminating-function discriminator) function)))
;;
;;;;;; Discriminator-Based caching.
;;
;;; Methods are cached in a discriminator-based cache. The cache is an N-key
;;; cache based on the number of specialized arguments the discriminator has.
;;; As yet the size of the cache does not change statically or dynamically.
;;; Because of this I allow myself the freedom of computing the mask at
;;; compile time and not even storing it in the discriminator.
(defvar *default-discriminator-cache-size* 8)
(defun make-discriminator-cache (&optional
(size *default-discriminator-cache-size*))
(make-memory-block size))
(defun make-discriminator-cache-mask (discriminator-cache
no-of-specialized-args)
(make-memory-block-mask (memory-block-size discriminator-cache)
(+ no-of-specialized-args 1)))
(defmeth flush-discriminator-caches ((discriminator essential-discriminator))
(let ((cache (discriminator-cache discriminator)))
(when cache (clear-memory-block (discriminator-cache discriminator) 0))))
(defmeth initialize-discriminator-cache ((self essential-discriminator)
no-of-specialized-args)
(ignore no-of-specialized-args)
(unless (discriminator-cache self)
(setf (discriminator-cache self) (make-discriminator-cache))))
(defmacro discriminator-cache-offset (mask &rest classes)
`(logand ,mask
,@(iterate ((class in classes))
(collect `(object-cache-no ,class ,mask)))))
(defmacro discriminator-cache-entry (cache offset offset-from-offset)
`(memory-block-ref ,cache (+ ,offset ,offset-from-offset)))
(defmacro cache-method (cache mask method-function &rest classes)
`(let* ((.offset. (discriminator-cache-offset ,mask ,@classes)))
;; Once again, we have to endure a little brain damage because we can't
;; count on having without-interrupts. I suppose the speed loss isn't
;; too significant since this is only when we get a cache miss.
(setf (discriminator-cache-entry ,cache .offset. 0) nil)
,@(iterate ((class in (cdr classes)) (key-no from 1))
(collect `(setf (discriminator-cache-entry ,cache .offset. ,key-no)
,class)))
(prog1
(setf (discriminator-cache-entry ,cache .offset. ,(length classes))
,method-function)
(setf (discriminator-cache-entry ,cache .offset. 0) ,(car classes)))))
(defmacro cached-method (var cache mask &rest classes)
`(let ((.offset. (discriminator-cache-offset ,mask . ,classes)))
(and ,@(iterate ((class in classes) (key-no from 0))
(collect
`(eq (discriminator-cache-entry ,cache .offset. ,key-no)
,class)))
(setq ,var (discriminator-cache-entry ,cache
.offset.
,(length classes)))
t)))
(defmeth make-caching-discriminating-function (discriminator lookup-function
cache
mask)
(multiple-value-bind (required restp specialized-positions)
(compute-discriminating-function-arglist-info discriminator)
(funcall (get-templated-function-constructor
'caching-discriminating-function
required
restp
specialized-positions
lookup-function)
discriminator cache mask)))
(defun make-checking-discriminating-function (discriminator method-function
type-specs
default-function)
(multiple-value-bind (required restp)
(compute-discriminating-function-arglist-info discriminator)
(let ((check-positions
(iterate ((type-spec in type-specs)
(pos from 0))
(collect (and (neq type-spec 't) pos)))))
(apply (get-templated-function-constructor
'checking-discriminating-function
required
restp
(if default-function t nil)
check-positions)
discriminator method-function default-function type-specs))))
;;
;;;;;;
;;
(defvar *always-remake-discriminating-function* nil)
(defmeth make-discriminating-function ((discriminator
essential-discriminator))
(let ((default (discriminator-default-method discriminator))
(methods (discriminator-methods discriminator)))
(cond ((null methods)
(make-no-methods-discriminating-function discriminator))
((and default (null (cdr methods)))
(make-default-method-only-discriminating-function discriminator))
((or (and default (null (cddr methods)))
(and (null default) (null (cdr methods))))
(make-single-method-only-discriminating-function discriminator))
((every #'(lambda (m)
(classical-type-specifiers-p
(method-type-specifiers m)))
methods)
(make-classical-methods-only-discriminating-function
discriminator))
(t
(make-multi-method-discriminating-function discriminator)))))
(defmeth make-no-methods-discriminating-function (discriminator)
(install-discriminating-function
discriminator
(discriminator-name discriminator)
#'(lambda (&rest ignore)
(error "There are no methods on the discriminator ~S,~%~
so it is an error to call it."
discriminator))))
(defmeth make-default-method-only-discriminating-function
((self essential-discriminator))
(install-discriminating-function
self
(discriminator-name self)
(method-function (discriminator-default-method self))))
(defmeth make-single-method-only-discriminating-function
((self essential-discriminator))
(let* ((methods (discriminator-methods self))
(default (discriminator-default-method self))
(method (if (eq (car methods) default)
(cadr methods)
(car methods)))
(method-type-specifiers (method-type-specifiers method))
(method-function (method-function method)))
(install-discriminating-function
self
(discriminator-name self)
(make-checking-discriminating-function
self
method-function
method-type-specifiers
(and default (method-function default))))))
(defmeth make-classical-methods-only-discriminating-function
((self essential-discriminator))
(initialize-discriminator-cache self 1)
(let ((default-method (discriminator-default-method self))
(methods (discriminator-methods self)))
(setf (discriminator-classical-method-table self)
(cons (and default-method (method-function default-method))
(iterate ((method in methods))
(unless (eq method default-method)
(collect (cons (car (method-type-specifiers method))
(method-function method))))))))
(let* ((cache (discriminator-cache self))
(mask (make-discriminator-cache-mask cache 1)))
(install-discriminating-function
self
(discriminator-name self)
(make-caching-discriminating-function
self 'lookup-classical-method cache mask))))
(defun lookup-classical-method (discriminator class)
;; There really should be some sort of more sophisticated protocol going
;; on here. Compare type-specifiers and all that.
(let* ((classical-method-table
(get-slot--class discriminator 'classical-method-table)))
(or (iterate ((super in (get-slot--class class 'class-precedence-list)))
(let ((hit (assq super (cdr classical-method-table))))
(when hit (return (cdr hit)))))
(car classical-method-table))))
(defmeth make-multi-method-discriminating-function
((self essential-discriminator))
(multiple-value-bind (required restp specialized)
(compute-discriminating-function-arglist-info self)
(ignore required restp)
(initialize-discriminator-cache self (length specialized))
(let* ((cache (discriminator-cache self))
(mask (make-discriminator-cache-mask cache (length specialized))))
(install-discriminating-function
self
(discriminator-name self)
(make-caching-discriminating-function
self 'lookup-multi-method cache mask)))))
(defvar *lookup-multi-method-internal*
(make-array (min 256. call-arguments-limit)))
(defun lookup-multi-method-internal (discriminator classes)
(let* ((methods (discriminator-methods discriminator))
(cpls *lookup-multi-method-internal*)
(order (get-slot--class discriminator 'dispatch-order))
(most-specific-method nil)
(most-specific-type-specs ())
(type-specs ()))
;; Put all the class-precedence-lists in a place where we can save
;; them as we look through all the methods.
(without-interrupts
(iterate ((class in classes)
(i from 0))
(setf (svref cpls i) (get-slot--class class 'class-precedence-list)))
(dolist (method methods)
(setq type-specs (get-slot--class method 'type-specifiers))
(when (iterate ((type-spec in type-specs)
(i from 0))
(or (eq type-spec 't)
(memq type-spec (svref cpls i))
(return nil))
(finally (return t)))
(if (null most-specific-method)
(setq most-specific-method method
most-specific-type-specs type-specs)
(case (compare-type-specifier-lists
most-specific-type-specs type-specs nil
() classes order)
(2 (setq most-specific-method method
most-specific-type-specs type-specs))
(1))))))
(or most-specific-method
(discriminator-default-method discriminator))))
(defun lookup-multi-method (discriminator &rest classes)
(declare (inline lookup-multi-method-internal))
(let ((method (lookup-multi-method-internal discriminator classes)))
(and method (method-function method))))
(defun lookup-method (discriminator &rest classes)
(declare (inline lookup-multi-method-internal))
(lookup-multi-method-internal discriminator classes))
;;
;;;;;; Code for parsing arglists (in the usual case).
;; (when discriminator is class DISCRIMINATOR and method is class METHOD)
;;;
;;; arglist-type-specifiers
;;; Given an arglist this returns its type-specifiers. Trailing T's (both
;;; implicit and explicit) are dropped. The type specifiers are returned as
;;; they are found in the arglist, they are not parsed into internal
;;; type-specs.
;;;
(defmeth arglist-type-specifiers ((proto-disc basic-discriminator)
(proto-meth basic-method)
arglist)
(let ((arg (car arglist)))
(and arglist
(not (memq arg '(&optional &rest &key &aux))) ;Don't allow any
;type-specifiers
;after one of these.
(let ((tail (arglist-type-specifiers proto-disc
proto-meth
(cdr arglist)))
(type-spec (and (listp arg) (cadr arg))))
(or (and tail (cons (or type-spec 't) tail))
(and type-spec (cons type-spec ())))))))
;;; arglist-without-type-specifiers
;;; Given an arglist remove the type specifiers.
;;;
(defmeth arglist-without-type-specifiers ((proto-disc basic-discriminator)
(proto-meth basic-method)
arglist)
(let ((arg (car arglist)))
(and arglist
(if (memq arg '(&optional &rest &key &aux)) ;don't allow any
;type-specifiers
;after one of these.
arglist
(cons (if (listp arg) (car arg) arg)
(arglist-without-type-specifiers proto-disc
proto-meth
(cdr arglist)))))))
(defmeth arglist-args ((discriminator-class basic-discriminator)
(method-class basic-method)
arglist)
(and arglist
(cond ((eq (car arglist) '&aux) ())
((memq (car arglist) '(&optional &rest &key))
(arglist-args discriminator-class method-class (cdr arglist)))
(t
;; This plays on the fact that no type specifiers are allowed
;; on arguments that can have default values.
(cons (if (listp (car arglist)) (caar arglist) (car arglist))
(arglist-args discriminator-class
method-class
(cdr arglist)))))))
(defmeth parse-type-specifiers ((proto-discriminator basic-discriminator)
(proto-method basic-method)
type-specifiers)
(iterate ((type-specifier in type-specifiers))
(collect (parse-type-specifier proto-discriminator
proto-method
type-specifier))))
(defmeth parse-type-specifier ((proto-discriminator basic-discriminator)
(proto-method basic-method)
type-specifier)
(ignore proto-discriminator proto-method)
(cond ((eq type-specifier 't) 't)
((symbolp type-specifier)
(or (class-named type-specifier nil)
(error
"~S used as a type-specifier, but is not the name of a class."
type-specifier)))
((classp type-specifier) type-specifier)
(t (error "~S is not a legal type-specifier." type-specifier))))
(defmeth unparse-type-specifiers ((method essential-method))
(iterate ((parsed-type-spec in (method-type-specifiers method)))
(collect (unparse-type-specifier method parsed-type-spec))))
(defmeth unparse-type-specifier ((method essential-method) type-spec)
(ignore method)
(if (classp type-spec)
(class-name type-spec)
type-spec))
(defun classical-type-specifiers-p (typespecs)
(or (null typespecs)
(and (classp (car typespecs))
(null (cdr typespecs)))))
;;;
;;; Compute various information about a discriminator's arglist by looking at
;;; the argument lists of the methods. The hair for trying not to use &rest
;;; arguments lives here.
;;; The values returned are:
;;; number-of-required-arguments
;;; the number of required arguments to this discrimator's
;;; discriminating function
;;; &rest-argument-p
;;; whether or not this discriminator's discriminating
;;; function takes an &rest argument.
;;; specialized-argument-positions
;;; a list of the positions of the arguments this discriminator
;;; specializes (e.g. for a classical discrimator this is the
;;; list: (1)).
;;;
;;; As usual, it is legitimate to specialize the -internal function that is
;;; why I put it there, since I certainly could have written this more
;;; efficiently if I didn't want to provide that extensibility.
;;;
(defmeth compute-discriminating-function-arglist-info
((discriminator essential-discriminator)
&optional (methods () methods-p))
(declare (values number-of-required-arguments
&rest-argument-p
specialized-argument-postions))
(unless methods-p
(setq methods (discriminator-methods discriminator)))
(let ((number-required nil)
(restp nil)
(specialized-positions ()))
(iterate ((method in methods))
(multiple-value-setq (number-required restp specialized-positions)
(compute-discriminating-function-arglist-info-internal
discriminator method number-required restp specialized-positions)))
(values number-required restp (sort specialized-positions #'<))))
(defmeth compute-discriminating-function-arglist-info-internal
((discriminator essential-discriminator)
(method essential-method)
number-of-requireds restp specialized-argument-positions)
(ignore discriminator)
(let ((requireds 0))
;; Go through this methods arguments seeing how many are required,
;; and whether there is an &rest argument.
(iterate ((arg in (method-arglist method)))
(cond ((eq arg '&aux) (return))
((memq arg '(&optional &rest &key))
(return (setq restp t)))
((memq arg lambda-list-keywords))
(t (incf requireds))))
;; Now go through this method's type specifiers to see which
;; argument positions are type specified. Treat T specially
;; in the usual sort of way. For efficiency don't bother to
;; keep specialized-argument-positions sorted, rather depend
;; on our caller to do that.
(iterate ((type-spec in (method-type-specifiers method))
(pos from 0))
(unless (eq type-spec 't)
(pushnew pos specialized-argument-positions)))
;; Finally merge the values for this method into the values
;; for the exisiting methods and return them. Note that if
;; num-of-requireds is NIL it means this is the first method
;; and we depend on that.
(values (min (or number-of-requireds requireds) requireds)
(or restp
(and number-of-requireds (/= number-of-requireds requireds)))
specialized-argument-positions)))
(defun make-discriminating-function-arglist (number-required-arguments restp)
(iterate ((i from 0 below number-required-arguments))
(collect (intern (format nil "Discriminating Function Arg ~D" i)))
(finally (when restp
(collect '&rest)
(collect (intern "Discriminating Function &rest Arg"))))))
(defmeth compare-methods (discriminator method-1 method-2)
(ignore discriminator)
(let ((compare ()))
(iterate ((ts-1 in (method-type-specifiers method-1))
(ts-2 in (method-type-specifiers method-2)))
(cond ((eq ts-1 ts-2) (setq compare '=))
((eq ts-1 't) (setq compare method-2))
((eq ts-2 't) (setq compare method-1))
((memq ts-1 (class-class-precedence-list ts-2))
(setq compare method-2))
((memq ts-2 (class-class-precedence-list ts-1))
(setq compare method-1))
(t (return nil)))
(finally (return compare)))))
;;
;;;;;; Comparing type-specifiers, statically or wrt an object.
;;
;;; compare-type-specifier-lists compares two lists of type specifiers
;;; compare-type-specifiers compare two type specifiers
;;; If static-p it t the comparison is done statically, otherwise it is
;;; done with respect to object(s). The value returned is:
;;; 1 if type-spec-1 is more specific
;;; 2 if type-spec-2 is more specific
;;; = if they are equal
;;; NIL if they cannot be disambiguated
;;;
(defun compare-type-specifier-lists (type-spec-list-1
type-spec-list-2
staticp
args
classes
order)
(when (or type-spec-list-1 type-spec-list-2)
(ecase (compare-type-specifiers (or (car type-spec-list-1) t)
(or (car type-spec-list-2) t)
staticp
(car args)
(car classes))
(1 '1)
(2 '2)
(= (if (eq order :default)
(compare-type-specifier-lists (cdr type-spec-list-1)
(cdr type-spec-list-2)
staticp
(cdr args)
(cdr classes)
order)
(compare-type-specifier-lists (nth (car order) type-spec-list-1)
(nth (car order) type-spec-list-2)
staticp
(cdr args)
(cdr classes)
(cdr order))))
(nil
(unless staticp
(error "The type specifiers ~S and ~S can not be disambiguated~
with respect to the argument: ~S"
(or (car type-spec-list-1) t)
(or (car type-spec-list-2) t)
(car args)
(car classes)))))))
(defun compare-type-specifiers (type-spec-1 type-spec-2 staticp arg class)
(cond ((equal type-spec-1 type-spec-2) '=)
((eq type-spec-2 t) '1)
((eq type-spec-1 t) '2)
((and (classp type-spec-1) (classp type-spec-2))
; (if staticp
; (if (common-subs type-spec-1 type-spec-2)
; nil
; (let ((supers (common-supers type-spec-1 type-spec-2)))
; (cond ((cdr supers) nil)
; ((eq (car supers) type-spec-1) '2)
; ((eq (car supers) type-spec-2) '1)
; (t 'disjoint))))
(iterate ((super in (class-class-precedence-list (or class (class-of arg)))))
(cond ((eq super type-spec-1)
(return '1))
((eq super type-spec-2)
(return '2)))))
;)
(t
(compare-complex-type-specifiers type-spec-1 type-spec-2 staticp arg class))))
(defun compare-complex-type-specifiers (type-spec-1 type-spec-2 static-p arg class)
(ignore type-spec-1 type-spec-2 static-p arg class)
(error "Complex type specifiers are not yet supported."))
(defmeth no-matching-method (discriminator)
(let ((class-of-discriminator (class-of discriminator)))
(if (eq (class-of class-of-discriminator) (class-named 'class))
;; The meta-class of the discriminator is class, we can get at
;; it's name slot without doing any method lookup.
(let ((name (get-slot--class discriminator 'name)))
(if (and name (symbolp name))
(error "No matching method for: ~S." name)
(error "No matching method for the anonymous discriminator: ~S."
discriminator)))
(error "No matching method for the discriminator: ~S." discriminator))))
;;
;;;;;; Optimizing GET-SLOT
;;
(defmeth method-argument-class ((method basic-method) argument)
(let* ((arglist (method-arglist method))
(position (position argument arglist)))
(and position (nth position (method-type-specifiers method)))))
(defmeth optimize-get-slot ((class basic-class)
form)
(declare (ignore class))
(cons 'get-slot--class (cdr form)))
(defmeth optimize-setf-of-get-slot ((class basic-class)
form)
(declare (ignore class))
(cons 'put-slot--class (cdr form)))
@EOF
if test "`wc -lwc <methods.l`" != ' 1118 3675 42045'
then
echo ERROR: wc results of methods.l are `wc -lwc <methods.l` should be 1118 3675 42045
fi
chmod 755 methods.l
exit 0