[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/cl-generic 1849d2c 1/5: * packages/cl-generic: Add new
From: |
Stefan Monnier |
Subject: |
[elpa] externals/cl-generic 1849d2c 1/5: * packages/cl-generic: Add new package |
Date: |
Tue, 1 Dec 2020 15:25:37 -0500 (EST) |
branch: externals/cl-generic
commit 1849d2c033b962f531e3b33b2b4d6ae82d00b3a5
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
* packages/cl-generic: Add new package
---
cl-generic.el | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/cl-generic.el b/cl-generic.el
new file mode 100644
index 0000000..060cffc
--- /dev/null
+++ b/cl-generic.el
@@ -0,0 +1,115 @@
+;;; cl-generic.el --- Forward cl-generic compatibility for Emacs<25
+
+;; Copyright (C) 2015 Free Software Foundation, Inc
+
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; vcomment: Emacs-25's version is 1.0 so this has to stay below.
+;; Version: 0.1
+
+;; This program 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.
+
+;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is a forward compatibility package, which provides (a subset of) the
+;; features of the cl-generic package introduced in Emacs-25, for use on
+;; previous emacsen.
+
+;; Make sure this is installed *late* in your `load-path`, i.e. after Emacs's
+;; built-in .../lisp/emacs-lisp directory, so that if/when you upgrade to
+;; Emacs≥25, the built-in version of the file will take precedence, otherwise
+;; you could get into trouble (although we try to hack our way around the
+;; problem in case it happens).
+
+;; AFAIK, the main incompatibilities between cl-generic and EIEIO's defmethod
+;; are:
+;; - EIEIO does not support multiple dispatch. We ignore this difference here
+;; and rely on EIEIO to detect and signal the problem.
+;; - EIEIO only supports primary, :before, and :after qualifiers. We ignore
+;; this difference here and rely on EIEIO to detect and signal the problem.
+;; - EIEIO does not support specializers other than classes. We ignore this
+;; difference here and rely on EIEIO to detect and signal the problem.
+;; - EIEIO uses :static instead of (subclass <foo>) and :static methods match
+;; both class arguments as well as object argument of that class. Here we
+;; turn (subclass <foo>) into a :static qualifier and ignore the semantic
+;; difference, hoping noone will notice.
+;; - EIEIO's defgeneric does not reset the function. We ignore this difference
+;; and hope for the best.
+;; - EIEIO uses `call-next-method' and `next-method-p' while cl-defmethod uses
+;; `cl-next-method-p' and `cl-call-next-method' (simple matter of renaming).
+;; We handle that by renaming the calls in the `cl-defmethod' macro.
+;; - The errors signaled are slightly different. We make
+;; cl-no-applicable-method into a "parent" error of no-method-definition,
+;; which should cover the usual cases.
+;; - EIEIO's no-next-method and no-applicable-method have different calling
+;; conventions from cl-generic's. We don't try to handle this, so just
+;; refrain from trying to call (or add methods to) `cl-no-next-method' or
+;; `cl-no-applicable-method'.
+;; - EIEIO's `call-next-method' and `next-method-p' have dynamic scope whereas
+;; cl-generic's `cl-next-method-p' and `cl-call-next-method' are lexically
+;; scoped. The cl-defmethod here handles the common subset between the two.
+
+;;; Code:
+
+;; We need to handle the situation where this package is used with an Emacs
+;; that comes with a real cl-generic (i.e. ≥25.1).
+
+;; First line of defense: try to make sure the built-in cl-lib comes earlier in
+;; load-path so we never get loaded:
+;;;###autoload (let ((d (file-name-directory #$)))
+;;;###autoload (when (member d load-path)
+;;;###autoload (setq load-path (append (remove d load-path) (list d)))))
+
+(require 'cl-lib nil 'noerror)
+
+;; In Emacs≥25, cl-lib autoloads cl-defmethod and friends.
+
+(unless (fboundp 'cl-defmethod)
+ (require 'eieio)
+ (require 'cl) ;For `labels'.
+
+ (defalias 'cl-defgeneric 'defgeneric)
+
+ ;; Compatibility with code which tries to catch
+ ;; `cl-no-applicable-method' errors.
+ (push 'cl-no-applicable-method (get 'no-method-definition 'error-conditions))
+
+ (defmacro cl-defmethod (name args &rest body)
+ (let ((qualifiers nil))
+ (while (not (listp args))
+ (push args qualifiers)
+ (setq args (pop body)))
+ (when (eq (car-safe (car args)) 'subclass)
+ ;; There's no exact equivalent to `subclass', but :static
+ ;; provides a superset which should work just as well in practice.
+ (push :static qualifiers)
+ (setcar args (cadr (car args))))
+ (let ((docstring (if (and (stringp (car body)) (cdr body)) (pop body))))
+ `(defmethod ,name ,@qualifiers ,args
+ ,docstring
+ ;; We could just alias `cl-call-next-method' to `call-next-method',
+ ;; and that would work, but then files compiled with this cl-generic
+ ;; wouldn't work in Emacs-25 any more.
+ ;; Also we use `labels' rather than one of cl-lib's macros, so as to
+ ;; be compatible with older emacsen (and ELPA's cl-lib emulation
+ ;; doesn't provide cl-flet and provides an incomplete cl-labels).
+ ,@(if qualifiers
+ ;; Must be :before or :after, so can't call next-method.
+ body
+ `((labels ((cl-call-next-method (&rest args)
+ (apply #'call-next-method args))
+ (cl-next-method-p () (next-method-p)))
+ ,@body))))))))
+
+(provide 'cl-generic)
+;;; cl-generic.el ends here