2015-01-20 15:40:29 -05:00
|
|
|
;;; eieio-compat.el --- Compatibility with Older EIEIO versions -*- lexical-binding:t -*-
|
|
|
|
|
2016-01-01 01:16:19 -08:00
|
|
|
;; Copyright (C) 1995-1996, 1998-2016 Free Software Foundation, Inc.
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;; Author: Eric M. Ludlam <zappo@gnu.org>
|
|
|
|
;; Keywords: OO, lisp
|
2015-12-03 15:27:21 +00:00
|
|
|
;; Package: eieio
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
;; Backward compatibility definition of old EIEIO functions in
|
|
|
|
;; terms of newer equivalent.
|
|
|
|
|
|
|
|
;; The main elements are the old EIEIO `defmethod' and `defgeneric' which are
|
|
|
|
;; now implemented on top of cl-generic. The differences we have to
|
|
|
|
;; accommodate are:
|
|
|
|
;; - EIEIO's :static methods (turned into a new `eieio--static' specializer).
|
|
|
|
;; - EIEIO's support for `call-next-method' and `next-method-p' instead of
|
|
|
|
;; `cl-next-method-p' and `cl-call-next-method' (simple matter of renaming).
|
|
|
|
;; - Different errors are signaled.
|
|
|
|
;; - EIEIO's defgeneric does not reset the function.
|
|
|
|
;; - EIEIO's no-next-method and no-applicable-method can't be aliases of
|
|
|
|
;; cl-generic's namesakes since they have different calling conventions,
|
|
|
|
;; which means that packages that (defmethod no-next-method ..) don't work.
|
|
|
|
;; - EIEIO's `call-next-method' and `next-method-p' had dynamic scope whereas
|
|
|
|
;; cl-generic's `cl-next-method-p' and `cl-call-next-method' are lexically
|
|
|
|
;; scoped.
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(require 'eieio-core)
|
|
|
|
(require 'cl-generic)
|
|
|
|
|
|
|
|
(put 'eieio--defalias 'byte-hunk-handler
|
|
|
|
#'byte-compile-file-form-defalias) ;;(get 'defalias 'byte-hunk-handler)
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio--defalias (name body)
|
|
|
|
"Like `defalias', but with less side-effects.
|
|
|
|
More specifically, it has no side-effects at all when the new function
|
|
|
|
definition is the same (`eq') as the old one."
|
|
|
|
(cl-assert (not (symbolp body)))
|
|
|
|
(while (and (fboundp name) (symbolp (symbol-function name)))
|
|
|
|
;; Follow aliases, so methods applied to obsolete aliases still work.
|
|
|
|
(setq name (symbol-function name)))
|
|
|
|
(unless (and (fboundp name)
|
|
|
|
(eq (symbol-function name) body))
|
|
|
|
(defalias name body)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defmacro defgeneric (method args &optional doc-string)
|
|
|
|
"Create a generic function METHOD.
|
|
|
|
DOC-STRING is the base documentation for this class. A generic
|
|
|
|
function has no body, as its purpose is to decide which method body
|
|
|
|
is appropriate to use. Uses `defmethod' to create methods, and calls
|
|
|
|
`defgeneric' for you. With this implementation the ARGS are
|
|
|
|
currently ignored. You can use `defgeneric' to apply specialized
|
|
|
|
top level documentation to a method."
|
|
|
|
(declare (doc-string 3) (obsolete cl-defgeneric "25.1"))
|
|
|
|
`(eieio--defalias ',method
|
|
|
|
(eieio--defgeneric-init-form
|
|
|
|
',method
|
|
|
|
,(if doc-string (help-add-fundoc-usage doc-string args)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defmacro defmethod (method &rest args)
|
|
|
|
"Create a new METHOD through `defgeneric' with ARGS.
|
|
|
|
|
|
|
|
The optional second argument KEY is a specifier that
|
|
|
|
modifies how the method is called, including:
|
|
|
|
:before - Method will be called before the :primary
|
|
|
|
:primary - The default if not specified
|
|
|
|
:after - Method will be called after the :primary
|
|
|
|
:static - First arg could be an object or class
|
|
|
|
The next argument is the ARGLIST. The ARGLIST specifies the arguments
|
|
|
|
to the method as with `defun'. The first argument can have a type
|
|
|
|
specifier, such as:
|
|
|
|
((VARNAME CLASS) ARG2 ...)
|
|
|
|
where VARNAME is the name of the local variable for the method being
|
|
|
|
created. The CLASS is a class symbol for a class made with `defclass'.
|
|
|
|
A DOCSTRING comes after the ARGLIST, and is optional.
|
|
|
|
All the rest of the args are the BODY of the method. A method will
|
|
|
|
return the value of the last form in the BODY.
|
|
|
|
|
|
|
|
Summary:
|
|
|
|
|
|
|
|
(defmethod mymethod [:before | :primary | :after | :static]
|
|
|
|
((typearg class-name) arg2 &optional opt &rest rest)
|
|
|
|
\"doc-string\"
|
|
|
|
body)"
|
|
|
|
(declare (doc-string 3) (obsolete cl-defmethod "25.1")
|
|
|
|
(debug
|
|
|
|
(&define ; this means we are defining something
|
|
|
|
[&or name ("setf" :name setf name)]
|
|
|
|
;; ^^ This is the methods symbol
|
|
|
|
[ &optional symbolp ] ; this is key :before etc
|
|
|
|
list ; arguments
|
|
|
|
[ &optional stringp ] ; documentation string
|
|
|
|
def-body ; part to be debugged
|
|
|
|
)))
|
|
|
|
(let* ((key (if (keywordp (car args)) (pop args)))
|
|
|
|
(params (car args))
|
|
|
|
(arg1 (car params))
|
|
|
|
(fargs (if (consp arg1)
|
|
|
|
(cons (car arg1) (cdr params))
|
|
|
|
params))
|
|
|
|
(class (if (consp arg1) (nth 1 arg1)))
|
|
|
|
(code `(lambda ,fargs ,@(cdr args))))
|
|
|
|
`(progn
|
|
|
|
;; Make sure there is a generic and the byte-compiler sees it.
|
|
|
|
(defgeneric ,method ,args)
|
|
|
|
(eieio--defmethod ',method ',key ',class #',code))))
|
|
|
|
|
2015-10-29 10:33:36 -04:00
|
|
|
(defun eieio--generic-static-symbol-specializers (tag &rest _)
|
2015-03-18 23:02:26 -04:00
|
|
|
(cl-assert (or (null tag) (eieio--class-p tag)))
|
|
|
|
(when (eieio--class-p tag)
|
|
|
|
(let ((superclasses (eieio--generic-subclass-specializers tag))
|
|
|
|
(specializers ()))
|
|
|
|
(dolist (superclass superclasses)
|
|
|
|
(push superclass specializers)
|
|
|
|
(push `(eieio--static ,(cadr superclass)) specializers))
|
|
|
|
(nreverse specializers))))
|
|
|
|
|
2015-10-29 10:33:36 -04:00
|
|
|
(cl-generic-define-generalizer eieio--generic-static-symbol-generalizer
|
|
|
|
;; Give it a slightly higher priority than `subclass' so that the
|
|
|
|
;; interleaved list comes before subclass's non-interleaved list.
|
|
|
|
61 (lambda (name &rest _) `(and (symbolp ,name) (cl--find-class ,name)))
|
|
|
|
#'eieio--generic-static-symbol-specializers)
|
|
|
|
(cl-generic-define-generalizer eieio--generic-static-object-generalizer
|
|
|
|
;; Give it a slightly higher priority than `class' so that the
|
|
|
|
;; interleaved list comes before the class's non-interleaved list.
|
|
|
|
51 #'cl--generic-struct-tag
|
2015-11-04 09:42:20 -05:00
|
|
|
(lambda (tag &rest _)
|
2015-10-29 10:33:36 -04:00
|
|
|
(and (symbolp tag) (boundp tag) (setq tag (symbol-value tag))
|
|
|
|
(eieio--class-p tag)
|
|
|
|
(let ((superclasses (eieio--class-precedence-list tag))
|
|
|
|
(specializers ()))
|
|
|
|
(dolist (superclass superclasses)
|
|
|
|
(setq superclass (eieio--class-name superclass))
|
|
|
|
(push superclass specializers)
|
|
|
|
(push `(eieio--static ,superclass) specializers))
|
|
|
|
(nreverse specializers)))))
|
Replace *-function vars with generic functions in cl-generic.
* lisp/emacs-lisp/cl-generic.el (cl--generic-generalizer): New struct.
(cl-generic-tagcode-function, cl-generic-tag-types-function): Remove.
(cl--generic-t-generalizer): New const.
(cl--generic-make-method): Rename from `cl--generic-method-make'.
(cl--generic-make): Change calling convention.
(cl--generic): Add `options' field.
(cl-generic-function-options): New function.
(cl-defgeneric): Rewrite handling of options. Add support for :method
options and allow the use of a default body.
(cl-generic-define): Save options in the corresponding new field.
(cl-defmethod): Fix ordering of qualifiers.
(cl-generic-define-method): Use cl-generic-generalizers.
(cl--generic-get-dispatcher): Change calling convention, and change
calling convention of the returned function as well so as to take the
list of methods separately from the generic function object, so that it
can receive the original generic function object.
(cl--generic-make-next-function): New function, extracted from
cl--generic-make-function.
(cl--generic-make-function): Use it.
(cl-generic-method-combination-function): Remove.
(cl--generic-cyclic-definition): New error.
(cl-generic-call-method): Take a generic function object rather than
its name.
(cl-method-qualifiers): New alias.
(cl--generic-build-combined-method): Use cl-generic-combine-methods,
don't segregate by qualifiers here any more.
(cl--generic-standard-method-combination): Segregate by qualifiers
here instead. Add support for the `:extra' qualifier.
(cl--generic-cache-miss): Move earlier, adjust to new calling convention.
(cl-generic-generalizers, cl-generic-combine-methods):
New generic functions.
(cl-no-next-method, cl-no-applicable-method, cl-no-primary-method):
Use the new "default method in defgeneric" functionality, change
calling convention to receive a generic function object.
(cl--generic-head-used): New var.
(cl--generic-head-generalizer, cl--generic-eql-generalizer)
(cl--generic-struct-generalizer, cl--generic-typeof-generalizer):
New consts.
* lisp/emacs-lisp/eieio-core.el (eieio--generic-generalizer)
(eieio--generic-subclass-generalizer): New consts.
(cl-generic-generalizers): New methods.
* lisp/emacs-lisp/eieio-compat.el (eieio--generic-static-symbol-generalizer)
(eieio--generic-static-object-generalizer): New consts.
(cl-generic-generalizers) <(head eieio--static)>: New method.
* lisp/emacs-lisp/byte-opt.el (byte-optimize-form-code-walker):
Unfold closures like lambdas.
2015-03-04 20:04:57 -05:00
|
|
|
|
|
|
|
(cl-defmethod cl-generic-generalizers ((_specializer (head eieio--static)))
|
|
|
|
(list eieio--generic-static-symbol-generalizer
|
|
|
|
eieio--generic-static-object-generalizer))
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio--defgeneric-init-form (method doc-string)
|
|
|
|
(if doc-string (put method 'function-documentation doc-string))
|
|
|
|
(if (memq method '(no-next-method no-applicable-method))
|
|
|
|
(symbol-function method)
|
|
|
|
(let ((generic (cl-generic-ensure-function method)))
|
|
|
|
(symbol-function (cl--generic-name generic)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio--defmethod (method kind argclass code)
|
|
|
|
(setq kind (intern (downcase (symbol-name kind))))
|
|
|
|
(let* ((specializer (if (not (eq kind :static))
|
|
|
|
(or argclass t)
|
|
|
|
(setq kind nil)
|
|
|
|
`(eieio--static ,argclass)))
|
|
|
|
(uses-cnm (not (memq kind '(:before :after))))
|
|
|
|
(specializers `((arg ,specializer)))
|
|
|
|
(code
|
|
|
|
;; Backward compatibility for `no-next-method' and
|
|
|
|
;; `no-applicable-method', which have slightly different calling
|
|
|
|
;; convention than their cl-generic counterpart.
|
|
|
|
(pcase method
|
|
|
|
(`no-next-method
|
|
|
|
(setq method 'cl-no-next-method)
|
|
|
|
(setq specializers `(generic method ,@specializers))
|
|
|
|
(lambda (_generic _method &rest args) (apply code args)))
|
|
|
|
(`no-applicable-method
|
|
|
|
(setq method 'cl-no-applicable-method)
|
|
|
|
(setq specializers `(generic ,@specializers))
|
2016-07-14 15:05:49 -04:00
|
|
|
(lambda (generic arg &rest args)
|
|
|
|
(apply code arg (cl--generic-name generic) (cons arg args))))
|
2015-01-20 15:40:29 -05:00
|
|
|
(_ code))))
|
|
|
|
(cl-generic-define-method
|
2015-01-26 09:04:55 -05:00
|
|
|
method (unless (memq kind '(nil :primary)) (list kind))
|
|
|
|
specializers uses-cnm
|
2015-01-20 15:40:29 -05:00
|
|
|
(if uses-cnm
|
|
|
|
(let* ((docstring (documentation code 'raw))
|
|
|
|
(args (help-function-arglist code 'preserve-names))
|
|
|
|
(doc-only (if docstring
|
|
|
|
(let ((split (help-split-fundoc docstring nil)))
|
2015-02-05 14:43:38 -05:00
|
|
|
(if split (cdr split) docstring)))))
|
2015-01-20 15:40:29 -05:00
|
|
|
(lambda (cnm &rest args)
|
2015-02-05 14:43:38 -05:00
|
|
|
(:documentation
|
|
|
|
(help-add-fundoc-usage doc-only (cons 'cl-cnm args)))
|
2015-01-20 15:40:29 -05:00
|
|
|
(cl-letf (((symbol-function 'call-next-method) cnm)
|
|
|
|
((symbol-function 'next-method-p)
|
|
|
|
(lambda () (cl--generic-isnot-nnm-p cnm))))
|
|
|
|
(apply code args))))
|
2015-01-21 14:39:06 -05:00
|
|
|
code))
|
|
|
|
;; The old EIEIO code did not signal an error when there are methods
|
|
|
|
;; applicable but only of the before/after kind. So if we add a :before
|
|
|
|
;; or :after, make sure there's a matching dummy primary.
|
|
|
|
(when (and (memq kind '(:before :after))
|
2015-01-26 09:04:55 -05:00
|
|
|
;; FIXME: Use `cl-find-method'?
|
2015-01-26 11:43:06 -05:00
|
|
|
(not (cl-find-method method ()
|
|
|
|
(mapcar (lambda (arg)
|
|
|
|
(if (consp arg) (nth 1 arg) t))
|
|
|
|
specializers))))
|
2015-01-21 14:39:06 -05:00
|
|
|
(cl-generic-define-method method () specializers t
|
|
|
|
(lambda (cnm &rest args)
|
|
|
|
(if (cl--generic-isnot-nnm-p cnm)
|
|
|
|
(apply cnm args)))))
|
|
|
|
method))
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;; Compatibility with code which tries to catch `no-method-definition' errors.
|
|
|
|
(push 'no-method-definition (get 'cl-no-applicable-method 'error-conditions))
|
|
|
|
|
|
|
|
(defun generic-p (fname) (not (null (cl--generic fname))))
|
|
|
|
|
|
|
|
(defun no-next-method (&rest args)
|
|
|
|
(declare (obsolete cl-no-next-method "25.1"))
|
|
|
|
(apply #'cl-no-next-method 'unknown nil args))
|
|
|
|
|
|
|
|
(defun no-applicable-method (object method &rest args)
|
|
|
|
(declare (obsolete cl-no-applicable-method "25.1"))
|
|
|
|
(apply #'cl-no-applicable-method method object args))
|
|
|
|
|
|
|
|
(define-obsolete-function-alias 'call-next-method 'cl-call-next-method "25.1")
|
2015-01-21 14:39:06 -05:00
|
|
|
(defun next-method-p ()
|
|
|
|
(declare (obsolete cl-next-method-p "25.1"))
|
|
|
|
;; EIEIO's `next-method-p' just returned nil when called in an
|
|
|
|
;; invalid context.
|
|
|
|
(message "next-method-p called outside of a primary or around method")
|
|
|
|
nil)
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio-defmethod (method args)
|
|
|
|
"Obsolete work part of an old version of the `defmethod' macro."
|
|
|
|
(declare (obsolete cl-defmethod "24.1"))
|
|
|
|
(eval `(defmethod ,method ,@args))
|
|
|
|
method)
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio-defgeneric (method doc-string)
|
|
|
|
"Obsolete work part of an old version of the `defgeneric' macro."
|
|
|
|
(declare (obsolete cl-defgeneric "24.1"))
|
2015-01-21 14:39:06 -05:00
|
|
|
(eval `(defgeneric ,method (x) ,@(if doc-string `(,doc-string))))
|
|
|
|
;; Return the method
|
|
|
|
'method)
|
2015-01-20 15:40:29 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun eieio-defclass (cname superclasses slots options)
|
|
|
|
(declare (obsolete eieio-defclass-internal "25.1"))
|
|
|
|
(eval `(defclass ,cname ,superclasses ,slots ,@options)))
|
|
|
|
|
|
|
|
|
|
|
|
;; Local Variables:
|
2015-12-17 20:01:16 +00:00
|
|
|
;; generated-autoload-file: "eieio-loaddefs.el"
|
2015-01-20 15:40:29 -05:00
|
|
|
;; End:
|
|
|
|
|
|
|
|
(provide 'eieio-compat)
|
|
|
|
|
|
|
|
;;; eieio-compat.el ends here
|