aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorStefan Monnier2015-10-29 10:33:36 -0400
committerStefan Monnier2015-10-29 10:33:36 -0400
commitaa1c4ae271733cf7dc64918b570bab4034488fa1 (patch)
treeae25c2ee8a08e885354de4a8793f871c7723168a /lisp
parentc0d866dd690ffef08894dbce573c636ab0b42665 (diff)
downloademacs-aa1c4ae271733cf7dc64918b570bab4034488fa1.tar.gz
emacs-aa1c4ae271733cf7dc64918b570bab4034488fa1.zip
* lisp/emacs-lisp/cl-generic.el: Accomodate future changes
(cl--generic-generalizer): Add `name' field. (cl-generic-make-generalizer): Add corresponding `name' argument. (cl-generic-define-generalizer): New macro. (cl--generic-head-generalizer, cl--generic-eql-generalizer) (cl--generic-struct-generalizer, cl--generic-typeof-generalizer) (cl--generic-t-generalizer): Use it. (cl-generic-ensure-function): Add `noerror' argument. (cl-generic-define): Use it so we don't follow aliases. (cl-generic-define-method): Preserve pre-existing ordering of methods. (cl--generic-arg-specializer): New function. (cl--generic-cache-miss): Use it. (cl-generic-generalizers): Only fset a temporary definition during bootstrap. (cl--generic-struct-tag, cl--generic-struct-specializers): Allow extra arguments. * lisp/emacs-lisp/eieio-compat.el (eieio--generic-static-symbol-generalizer) (eieio--generic-static-object-generalizer): Use cl-generic-define-generalizer. (eieio--generic-static-symbol-specializers): Allow extra arguments. * lisp/emacs-lisp/eieio-core.el (eieio--generic-generalizer) (eieio--generic-subclass-generalizer): Use cl-generic-define-generalizer. (eieio--generic-subclass-specializers): Allow extra arguments.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/emacs-lisp/cl-generic.el105
-rw-r--r--lisp/emacs-lisp/eieio-compat.el42
-rw-r--r--lisp/emacs-lisp/eieio-core.el30
3 files changed, 100 insertions, 77 deletions
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index dd01ebe9dd8..0d7ef5b2e61 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -80,7 +80,7 @@
80 80
81;; TODO: 81;; TODO:
82;; 82;;
83;; - A generic "filter" generalizer (e.g. could be used to cleanly adds methods 83;; - A generic "filter" generalizer (e.g. could be used to cleanly add methods
84;; to cl-generic-combine-methods with a specializer that says it applies only 84;; to cl-generic-combine-methods with a specializer that says it applies only
85;; when some particular qualifier is used). 85;; when some particular qualifier is used).
86;; - A way to dispatch on the context (e.g. the major-mode, some global 86;; - A way to dispatch on the context (e.g. the major-mode, some global
@@ -101,14 +101,33 @@
101(cl-defstruct (cl--generic-generalizer 101(cl-defstruct (cl--generic-generalizer
102 (:constructor nil) 102 (:constructor nil)
103 (:constructor cl-generic-make-generalizer 103 (:constructor cl-generic-make-generalizer
104 (priority tagcode-function specializers-function))) 104 (name priority tagcode-function specializers-function)))
105 (name nil :type string)
105 (priority nil :type integer) 106 (priority nil :type integer)
106 tagcode-function 107 tagcode-function
107 specializers-function) 108 specializers-function)
108 109
109(defconst cl--generic-t-generalizer 110
110 (cl-generic-make-generalizer 111(defmacro cl-generic-define-generalizer
111 0 (lambda (_name) nil) (lambda (_tag) '(t)))) 112 (name priority tagcode-function specializers-function)
113 "Define a new kind of generalizer.
114NAME is the name of the variable that will hold it.
115PRIORITY defines which generalizer takes precedence.
116 The catch-all generalizer has priority 0.
117 Then `eql' generalizer has priority 100.
118TAGCODE-FUNCTION takes as first argument a varname and should return
119 a chunk of code that computes the tag of the value held in that variable.
120 Further arguments are reserved for future use.
121SPECIALIZERS-FUNCTION takes as first argument a tag value TAG
122 and should return a list of specializers that match TAG.
123 Further arguments are reserved for future use."
124 (declare (indent 1) (debug (symbolp body)))
125 `(defconst ,name
126 (cl-generic-make-generalizer
127 ',name ,priority ,tagcode-function ,specializers-function)))
128
129(cl-generic-define-generalizer cl--generic-t-generalizer
130 0 (lambda (_name &rest _) nil) (lambda (_tag &rest _) '(t)))
112 131
113(cl-defstruct (cl--generic-method 132(cl-defstruct (cl--generic-method
114 (:constructor nil) 133 (:constructor nil)
@@ -144,16 +163,18 @@
144(defmacro cl--generic (name) 163(defmacro cl--generic (name)
145 `(get ,name 'cl--generic)) 164 `(get ,name 'cl--generic))
146 165
147(defun cl-generic-ensure-function (name) 166(defun cl-generic-ensure-function (name &optional noerror)
148 (let (generic 167 (let (generic
149 (origname name)) 168 (origname name))
150 (while (and (null (setq generic (cl--generic name))) 169 (while (and (null (setq generic (cl--generic name)))
151 (fboundp name) 170 (fboundp name)
171 (null noerror)
152 (symbolp (symbol-function name))) 172 (symbolp (symbol-function name)))
153 (setq name (symbol-function name))) 173 (setq name (symbol-function name)))
154 (unless (or (not (fboundp name)) 174 (unless (or (not (fboundp name))
155 (autoloadp (symbol-function name)) 175 (autoloadp (symbol-function name))
156 (and (functionp name) generic)) 176 (and (functionp name) generic)
177 noerror)
157 (error "%s is already defined as something else than a generic function" 178 (error "%s is already defined as something else than a generic function"
158 origname)) 179 origname))
159 (if generic 180 (if generic
@@ -220,7 +241,7 @@ BODY, if present, is used as the body of a default method.
220 241
221;;;###autoload 242;;;###autoload
222(defun cl-generic-define (name args options) 243(defun cl-generic-define (name args options)
223 (pcase-let* ((generic (cl-generic-ensure-function name)) 244 (pcase-let* ((generic (cl-generic-ensure-function name 'noerror))
224 (`(,spec-args . ,_) (cl--generic-split-args args)) 245 (`(,spec-args . ,_) (cl--generic-split-args args))
225 (mandatory (mapcar #'car spec-args)) 246 (mandatory (mapcar #'car spec-args))
226 (apo (assq :argument-precedence-order options))) 247 (apo (assq :argument-precedence-order options)))
@@ -418,8 +439,12 @@ which case this method will be invoked when the argument is `eql' to VAL.
418 (setq i (1+ i)))) 439 (setq i (1+ i))))
419 ;; We used to (setcar me method), but that can cause false positives in 440 ;; We used to (setcar me method), but that can cause false positives in
420 ;; the hash-consing table of the method-builder (bug#20644). 441 ;; the hash-consing table of the method-builder (bug#20644).
421 ;; See the related FIXME in cl--generic-build-combined-method. 442 ;; See also the related FIXME in cl--generic-build-combined-method.
422 (setf (cl--generic-method-table generic) (cons method (delq (car me) mt))) 443 (setf (cl--generic-method-table generic)
444 (if (null me)
445 (cons method mt)
446 ;; Keep the ordering; important for methods with :extra qualifiers.
447 (mapcar (lambda (x) (if (eq x (car me)) method x)) mt)))
423 (cl-pushnew `(cl-defmethod . (,(cl--generic-name generic) . ,specializers)) 448 (cl-pushnew `(cl-defmethod . (,(cl--generic-name generic) . ,specializers))
424 current-load-list :test #'equal) 449 current-load-list :test #'equal)
425 ;; FIXME: Try to avoid re-constructing a new function if the old one 450 ;; FIXME: Try to avoid re-constructing a new function if the old one
@@ -623,16 +648,19 @@ FUN is the function that should be called when METHOD calls
623 (setq fun (cl-generic-call-method generic method fun))) 648 (setq fun (cl-generic-call-method generic method fun)))
624 fun))))) 649 fun)))))
625 650
651(defun cl--generic-arg-specializer (method dispatch-arg)
652 (or (if (integerp dispatch-arg)
653 (nth dispatch-arg
654 (cl--generic-method-specializers method))
655 (cdr (assoc dispatch-arg
656 (cl--generic-method-specializers method))))
657 t))
658
626(defun cl--generic-cache-miss (generic 659(defun cl--generic-cache-miss (generic
627 dispatch-arg dispatches-left methods-left types) 660 dispatch-arg dispatches-left methods-left types)
628 (let ((methods '())) 661 (let ((methods '()))
629 (dolist (method methods-left) 662 (dolist (method methods-left)
630 (let* ((specializer (or (if (integerp dispatch-arg) 663 (let* ((specializer (cl--generic-arg-specializer method dispatch-arg))
631 (nth dispatch-arg
632 (cl--generic-method-specializers method))
633 (cdr (assoc dispatch-arg
634 (cl--generic-method-specializers method))))
635 t))
636 (m (member specializer types))) 664 (m (member specializer types)))
637 (when m 665 (when m
638 (push (cons (length m) method) methods)))) 666 (push (cons (length m) method) methods))))
@@ -682,10 +710,12 @@ The METHODS list is sorted from most specific first to most generic last.
682The function can use `cl-generic-call-method' to create functions that call those 710The function can use `cl-generic-call-method' to create functions that call those
683methods.") 711methods.")
684 712
685;; Temporary definition to let the next defmethod succeed. 713(unless (ignore-errors (cl-generic-generalizers t))
686(fset 'cl-generic-generalizers 714 ;; Temporary definition to let the next defmethod succeed.
687 (lambda (_specializer) (list cl--generic-t-generalizer))) 715 (fset 'cl-generic-generalizers
688(fset 'cl-generic-combine-methods #'cl--generic-standard-method-combination) 716 (lambda (specializer)
717 (if (eq t specializer) (list cl--generic-t-generalizer))))
718 (fset 'cl-generic-combine-methods #'cl--generic-standard-method-combination))
689 719
690(cl-defmethod cl-generic-generalizers (specializer) 720(cl-defmethod cl-generic-generalizers (specializer)
691 "Support for the catch-all t specializer." 721 "Support for the catch-all t specializer."
@@ -940,10 +970,9 @@ The value returned is a list of elements of the form
940 970
941(defvar cl--generic-head-used (make-hash-table :test #'eql)) 971(defvar cl--generic-head-used (make-hash-table :test #'eql))
942 972
943(defconst cl--generic-head-generalizer 973(cl-generic-define-generalizer cl--generic-head-generalizer
944 (cl-generic-make-generalizer 974 80 (lambda (name &rest _) `(gethash (car-safe ,name) cl--generic-head-used))
945 80 (lambda (name) `(gethash (car-safe ,name) cl--generic-head-used)) 975 (lambda (tag &rest _) (if (eq (car-safe tag) 'head) (list tag))))
946 (lambda (tag) (if (eq (car-safe tag) 'head) (list tag)))))
947 976
948(cl-defmethod cl-generic-generalizers :extra "head" (specializer) 977(cl-defmethod cl-generic-generalizers :extra "head" (specializer)
949 "Support for the `(head VAL)' specializers." 978 "Support for the `(head VAL)' specializers."
@@ -961,10 +990,9 @@ The value returned is a list of elements of the form
961 990
962(defvar cl--generic-eql-used (make-hash-table :test #'eql)) 991(defvar cl--generic-eql-used (make-hash-table :test #'eql))
963 992
964(defconst cl--generic-eql-generalizer 993(cl-generic-define-generalizer cl--generic-eql-generalizer
965 (cl-generic-make-generalizer 994 100 (lambda (name &rest _) `(gethash ,name cl--generic-eql-used))
966 100 (lambda (name) `(gethash ,name cl--generic-eql-used)) 995 (lambda (tag &rest _) (if (eq (car-safe tag) 'eql) (list tag))))
967 (lambda (tag) (if (eq (car-safe tag) 'eql) (list tag)))))
968 996
969(cl-defmethod cl-generic-generalizers ((specializer (head eql))) 997(cl-defmethod cl-generic-generalizers ((specializer (head eql)))
970 "Support for the `(eql VAL)' specializers." 998 "Support for the `(eql VAL)' specializers."
@@ -976,7 +1004,7 @@ The value returned is a list of elements of the form
976 1004
977;;; Support for cl-defstructs specializers. 1005;;; Support for cl-defstructs specializers.
978 1006
979(defun cl--generic-struct-tag (name) 1007(defun cl--generic-struct-tag (name &rest _)
980 ;; It's tempting to use (and (vectorp ,name) (aref ,name 0)) 1008 ;; It's tempting to use (and (vectorp ,name) (aref ,name 0))
981 ;; but that would suffer from some problems: 1009 ;; but that would suffer from some problems:
982 ;; - the vector may have size 0. 1010 ;; - the vector may have size 0.
@@ -1007,16 +1035,15 @@ The value returned is a list of elements of the form
1007 (cl--class-parents class))))) 1035 (cl--class-parents class)))))
1008 (nreverse parents))) 1036 (nreverse parents)))
1009 1037
1010(defun cl--generic-struct-specializers (tag) 1038(defun cl--generic-struct-specializers (tag &rest _)
1011 (and (symbolp tag) (boundp tag) 1039 (and (symbolp tag) (boundp tag)
1012 (let ((class (symbol-value tag))) 1040 (let ((class (symbol-value tag)))
1013 (when (cl-typep class 'cl-structure-class) 1041 (when (cl-typep class 'cl-structure-class)
1014 (cl--generic-class-parents class))))) 1042 (cl--generic-class-parents class)))))
1015 1043
1016(defconst cl--generic-struct-generalizer 1044(cl-generic-define-generalizer cl--generic-struct-generalizer
1017 (cl-generic-make-generalizer 1045 50 #'cl--generic-struct-tag
1018 50 #'cl--generic-struct-tag 1046 #'cl--generic-struct-specializers)
1019 #'cl--generic-struct-specializers))
1020 1047
1021(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type) 1048(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type)
1022 "Support for dispatch on cl-struct types." 1049 "Support for dispatch on cl-struct types."
@@ -1056,11 +1083,11 @@ The value returned is a list of elements of the form
1056 (sequence) 1083 (sequence)
1057 (number))) 1084 (number)))
1058 1085
1059(defconst cl--generic-typeof-generalizer 1086(cl-generic-define-generalizer cl--generic-typeof-generalizer
1060 (cl-generic-make-generalizer 1087 ;; FIXME: We could also change `type-of' to return `null' for nil.
1061 ;; FIXME: We could also change `type-of' to return `null' for nil. 1088 10 (lambda (name &rest _) `(if ,name (type-of ,name) 'null))
1062 10 (lambda (name) `(if ,name (type-of ,name) 'null)) 1089 (lambda (tag &rest _)
1063 (lambda (tag) (and (symbolp tag) (assq tag cl--generic-typeof-types))))) 1090 (and (symbolp tag) (assq tag cl--generic-typeof-types))))
1064 1091
1065(cl-defmethod cl-generic-generalizers :extra "typeof" (type) 1092(cl-defmethod cl-generic-generalizers :extra "typeof" (type)
1066 "Support for dispatch on builtin types." 1093 "Support for dispatch on builtin types."
diff --git a/lisp/emacs-lisp/eieio-compat.el b/lisp/emacs-lisp/eieio-compat.el
index 386ff2f7449..638c475ef2b 100644
--- a/lisp/emacs-lisp/eieio-compat.el
+++ b/lisp/emacs-lisp/eieio-compat.el
@@ -124,7 +124,7 @@ Summary:
124 (defgeneric ,method ,args) 124 (defgeneric ,method ,args)
125 (eieio--defmethod ',method ',key ',class #',code)))) 125 (eieio--defmethod ',method ',key ',class #',code))))
126 126
127(defun eieio--generic-static-symbol-specializers (tag) 127(defun eieio--generic-static-symbol-specializers (tag &rest _)
128 (cl-assert (or (null tag) (eieio--class-p tag))) 128 (cl-assert (or (null tag) (eieio--class-p tag)))
129 (when (eieio--class-p tag) 129 (when (eieio--class-p tag)
130 (let ((superclasses (eieio--generic-subclass-specializers tag)) 130 (let ((superclasses (eieio--generic-subclass-specializers tag))
@@ -134,27 +134,25 @@ Summary:
134 (push `(eieio--static ,(cadr superclass)) specializers)) 134 (push `(eieio--static ,(cadr superclass)) specializers))
135 (nreverse specializers)))) 135 (nreverse specializers))))
136 136
137(defconst eieio--generic-static-symbol-generalizer 137(cl-generic-define-generalizer eieio--generic-static-symbol-generalizer
138 (cl-generic-make-generalizer 138 ;; Give it a slightly higher priority than `subclass' so that the
139 ;; Give it a slightly higher priority than `subclass' so that the 139 ;; interleaved list comes before subclass's non-interleaved list.
140 ;; interleaved list comes before subclass's non-interleaved list. 140 61 (lambda (name &rest _) `(and (symbolp ,name) (cl--find-class ,name)))
141 61 (lambda (name) `(and (symbolp ,name) (cl--find-class ,name))) 141 #'eieio--generic-static-symbol-specializers)
142 #'eieio--generic-static-symbol-specializers)) 142(cl-generic-define-generalizer eieio--generic-static-object-generalizer
143(defconst eieio--generic-static-object-generalizer 143 ;; Give it a slightly higher priority than `class' so that the
144 (cl-generic-make-generalizer 144 ;; interleaved list comes before the class's non-interleaved list.
145 ;; Give it a slightly higher priority than `class' so that the 145 51 #'cl--generic-struct-tag
146 ;; interleaved list comes before the class's non-interleaved list. 146 (lambda (tag _targets)
147 51 #'cl--generic-struct-tag 147 (and (symbolp tag) (boundp tag) (setq tag (symbol-value tag))
148 (lambda (tag) 148 (eieio--class-p tag)
149 (and (symbolp tag) (boundp tag) (setq tag (symbol-value tag)) 149 (let ((superclasses (eieio--class-precedence-list tag))
150 (eieio--class-p tag) 150 (specializers ()))
151 (let ((superclasses (eieio--class-precedence-list tag)) 151 (dolist (superclass superclasses)
152 (specializers ())) 152 (setq superclass (eieio--class-name superclass))
153 (dolist (superclass superclasses) 153 (push superclass specializers)
154 (setq superclass (eieio--class-name superclass)) 154 (push `(eieio--static ,superclass) specializers))
155 (push superclass specializers) 155 (nreverse specializers)))))
156 (push `(eieio--static ,superclass) specializers))
157 (nreverse specializers))))))
158 156
159(cl-defmethod cl-generic-generalizers ((_specializer (head eieio--static))) 157(cl-defmethod cl-generic-generalizers ((_specializer (head eieio--static)))
160 (list eieio--generic-static-symbol-generalizer 158 (list eieio--generic-static-symbol-generalizer
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index e3f7b11bb64..7011a30656b 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -1059,16 +1059,15 @@ method invocation orders of the involved classes."
1059 1059
1060;;;; General support to dispatch based on the type of the argument. 1060;;;; General support to dispatch based on the type of the argument.
1061 1061
1062(defconst eieio--generic-generalizer 1062(cl-generic-define-generalizer eieio--generic-generalizer
1063 (cl-generic-make-generalizer 1063 ;; Use the exact same tagcode as for cl-struct, so that methods
1064 ;; Use the exact same tagcode as for cl-struct, so that methods 1064 ;; that dispatch on both kinds of objects get to share this
1065 ;; that dispatch on both kinds of objects get to share this 1065 ;; part of the dispatch code.
1066 ;; part of the dispatch code. 1066 50 #'cl--generic-struct-tag
1067 50 #'cl--generic-struct-tag 1067 (lambda (tag &rest _)
1068 (lambda (tag) 1068 (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag))
1069 (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag)) 1069 (mapcar #'eieio--class-name
1070 (mapcar #'eieio--class-name 1070 (eieio--class-precedence-list (symbol-value tag))))))
1071 (eieio--class-precedence-list (symbol-value tag)))))))
1072 1071
1073(cl-defmethod cl-generic-generalizers :extra "class" (specializer) 1072(cl-defmethod cl-generic-generalizers :extra "class" (specializer)
1074 ;; CLHS says: 1073 ;; CLHS says:
@@ -1088,22 +1087,21 @@ method invocation orders of the involved classes."
1088;; would not make much sense (e.g. to which argument should it apply?). 1087;; would not make much sense (e.g. to which argument should it apply?).
1089;; Instead, we add a new "subclass" specializer. 1088;; Instead, we add a new "subclass" specializer.
1090 1089
1091(defun eieio--generic-subclass-specializers (tag) 1090(defun eieio--generic-subclass-specializers (tag &rest _)
1092 (when (eieio--class-p tag) 1091 (when (eieio--class-p tag)
1093 (mapcar (lambda (class) 1092 (mapcar (lambda (class)
1094 `(subclass ,(eieio--class-name class))) 1093 `(subclass ,(eieio--class-name class)))
1095 (eieio--class-precedence-list tag)))) 1094 (eieio--class-precedence-list tag))))
1096 1095
1097(defconst eieio--generic-subclass-generalizer 1096(cl-generic-define-generalizer eieio--generic-subclass-generalizer
1098 (cl-generic-make-generalizer 1097 60 (lambda (name &rest _) `(and (symbolp ,name) (cl--find-class ,name)))
1099 60 (lambda (name) `(and (symbolp ,name) (cl--find-class ,name))) 1098 #'eieio--generic-subclass-specializers)
1100 #'eieio--generic-subclass-specializers))
1101 1099
1102(cl-defmethod cl-generic-generalizers ((_specializer (head subclass))) 1100(cl-defmethod cl-generic-generalizers ((_specializer (head subclass)))
1103 (list eieio--generic-subclass-generalizer)) 1101 (list eieio--generic-subclass-generalizer))
1104 1102
1105 1103
1106;;;### (autoloads nil "eieio-compat" "eieio-compat.el" "ea8c7f24ed47c6b71ac37cbdae1c9931") 1104;;;### (autoloads nil "eieio-compat" "eieio-compat.el" "bd51800d7de6429a2c9a6a600ba2dc52")
1107;;; Generated autoloads from eieio-compat.el 1105;;; Generated autoloads from eieio-compat.el
1108 1106
1109(autoload 'eieio--defalias "eieio-compat" "\ 1107(autoload 'eieio--defalias "eieio-compat" "\