diff options
| author | Stefan Monnier | 2023-11-07 18:57:03 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2023-11-09 00:37:19 -0500 |
| commit | 97a2305482c24574c2429d50f3573810a0d12091 (patch) | |
| tree | 3ece9eb1085238c96c078587c2d34eb3b413de78 | |
| parent | 8323394bc801e01dedd95e0ff8d573dd1f5e34ba (diff) | |
| download | emacs-97a2305482c24574c2429d50f3573810a0d12091.tar.gz emacs-97a2305482c24574c2429d50f3573810a0d12091.zip | |
Move EIEIO's C3 linearization code to `subr.el`scratch/derived-mode-add-parents-2
The code was used to linearize the EIEIO class hierarchy, since
it results in saner results than things like BFS or DFS.
By moving it to `subr.el` we get to benefit from that same
advantage both in `cl--class-allparents` and
in `derived-mode-all-parents`.
* lisp/subr.el (merge-ordered-lists): New function.
(derived-mode-all-parents): Use it to improve parent ordering.
* lisp/emacs-lisp/eieio-core.el (eieio--c3-candidate)
(eieio--c3-merge-lists): Delete functions, replaced by
`merge-ordered-lists`.
(eieio--class-precedence-c3): Use `merge-ordered-lists`.
* lisp/emacs-lisp/cl-preloaded.el (cl--class-allparents):
Use `merge-ordered-lists` to improve parent ordering.
* lisp/emacs-lisp/cl-macs.el (cl--struct-all-parents): Delete function.
(cl--pcase-mutually-exclusive-p): Use `cl--class-allparents` instead.
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 17 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-preloaded.el | 12 | ||||
| -rw-r--r-- | lisp/emacs-lisp/eieio-core.el | 58 | ||||
| -rw-r--r-- | lisp/simple.el | 2 | ||||
| -rw-r--r-- | lisp/subr.el | 57 |
5 files changed, 61 insertions, 85 deletions
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index e2c13534054..2431e658368 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -3337,19 +3337,6 @@ To see the documentation for a defined struct type, use | |||
| 3337 | 3337 | ||
| 3338 | ;;; Add cl-struct support to pcase | 3338 | ;;; Add cl-struct support to pcase |
| 3339 | 3339 | ||
| 3340 | ;;In use by comp.el | ||
| 3341 | (defun cl--struct-all-parents (class) ;FIXME: Merge with `cl--class-allparents' | ||
| 3342 | (when (cl--struct-class-p class) | ||
| 3343 | (let ((res ()) | ||
| 3344 | (classes (list class))) | ||
| 3345 | ;; BFS precedence. | ||
| 3346 | (while (let ((class (pop classes))) | ||
| 3347 | (push class res) | ||
| 3348 | (setq classes | ||
| 3349 | (append classes | ||
| 3350 | (cl--class-parents class))))) | ||
| 3351 | (nreverse res)))) | ||
| 3352 | |||
| 3353 | ;;;###autoload | 3340 | ;;;###autoload |
| 3354 | (pcase-defmacro cl-struct (type &rest fields) | 3341 | (pcase-defmacro cl-struct (type &rest fields) |
| 3355 | "Pcase patterns that match cl-struct EXPVAL of type TYPE. | 3342 | "Pcase patterns that match cl-struct EXPVAL of type TYPE. |
| @@ -3395,8 +3382,8 @@ the form NAME which is a shorthand for (NAME NAME)." | |||
| 3395 | (let ((c1 (cl--find-class t1)) | 3382 | (let ((c1 (cl--find-class t1)) |
| 3396 | (c2 (cl--find-class t2))) | 3383 | (c2 (cl--find-class t2))) |
| 3397 | (and c1 c2 | 3384 | (and c1 c2 |
| 3398 | (not (or (memq c1 (cl--struct-all-parents c2)) | 3385 | (not (or (memq t1 (cl--class-allparents c2)) |
| 3399 | (memq c2 (cl--struct-all-parents c1))))))) | 3386 | (memq t2 (cl--class-allparents c1))))))) |
| 3400 | (let ((c1 (and (symbolp t1) (cl--find-class t1)))) | 3387 | (let ((c1 (and (symbolp t1) (cl--find-class t1)))) |
| 3401 | (and c1 (cl--struct-class-p c1) | 3388 | (and c1 (cl--struct-class-p c1) |
| 3402 | (funcall orig (cl--defstruct-predicate t1) | 3389 | (funcall orig (cl--defstruct-predicate t1) |
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 03068639575..3d0c2b54785 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el | |||
| @@ -323,15 +323,9 @@ supertypes from the most specific to least specific.") | |||
| 323 | (cl-assert (cl--class-p (cl--find-class 'cl-structure-object))) | 323 | (cl-assert (cl--class-p (cl--find-class 'cl-structure-object))) |
| 324 | 324 | ||
| 325 | (defun cl--class-allparents (class) | 325 | (defun cl--class-allparents (class) |
| 326 | (let ((parents ()) | 326 | (cons (cl--class-name class) |
| 327 | (classes (list class))) | 327 | (merge-ordered-lists (mapcar #'cl--class-allparents |
| 328 | ;; BFS precedence. FIXME: Use a topological sort. | 328 | (cl--class-parents class))))) |
| 329 | (while (let ((class (pop classes))) | ||
| 330 | (cl-pushnew (cl--class-name class) parents) | ||
| 331 | (setq classes | ||
| 332 | (append classes | ||
| 333 | (cl--class-parents class))))) | ||
| 334 | (nreverse parents))) | ||
| 335 | 329 | ||
| 336 | (eval-and-compile | 330 | (eval-and-compile |
| 337 | (cl-assert (null (cl--class-parents (cl--find-class 'cl-structure-object))))) | 331 | (cl-assert (null (cl--class-parents (cl--find-class 'cl-structure-object))))) |
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el index f5ff04ff372..8e8fa2b168e 100644 --- a/lisp/emacs-lisp/eieio-core.el +++ b/lisp/emacs-lisp/eieio-core.el | |||
| @@ -964,49 +964,6 @@ need be... May remove that later...)" | |||
| 964 | (cdr tuple) | 964 | (cdr tuple) |
| 965 | nil))) | 965 | nil))) |
| 966 | 966 | ||
| 967 | ;;; | ||
| 968 | ;; Method Invocation order: C3 | ||
| 969 | (defun eieio--c3-candidate (class remaining-inputs) | ||
| 970 | "Return CLASS if it can go in the result now, otherwise nil." | ||
| 971 | ;; Ensure CLASS is not in any position but the first in any of the | ||
| 972 | ;; element lists of REMAINING-INPUTS. | ||
| 973 | (and (not (let ((found nil)) | ||
| 974 | (while (and remaining-inputs (not found)) | ||
| 975 | (setq found (member class (cdr (car remaining-inputs))) | ||
| 976 | remaining-inputs (cdr remaining-inputs))) | ||
| 977 | found)) | ||
| 978 | class)) | ||
| 979 | |||
| 980 | (defun eieio--c3-merge-lists (reversed-partial-result remaining-inputs) | ||
| 981 | "Try to merge REVERSED-PARTIAL-RESULT REMAINING-INPUTS in a consistent order. | ||
| 982 | If a consistent order does not exist, signal an error." | ||
| 983 | (setq remaining-inputs (delq nil remaining-inputs)) | ||
| 984 | (if (null remaining-inputs) | ||
| 985 | ;; If all remaining inputs are empty lists, we are done. | ||
| 986 | (nreverse reversed-partial-result) | ||
| 987 | ;; Otherwise, we try to find the next element of the result. This | ||
| 988 | ;; is achieved by considering the first element of each | ||
| 989 | ;; (non-empty) input list and accepting a candidate if it is | ||
| 990 | ;; consistent with the rests of the input lists. | ||
| 991 | (let* ((found nil) | ||
| 992 | (tail remaining-inputs) | ||
| 993 | (next (progn | ||
| 994 | (while (and tail (not found)) | ||
| 995 | (setq found (eieio--c3-candidate (caar tail) | ||
| 996 | remaining-inputs) | ||
| 997 | tail (cdr tail))) | ||
| 998 | found))) | ||
| 999 | (if next | ||
| 1000 | ;; The graph is consistent so far, add NEXT to result and | ||
| 1001 | ;; merge input lists, dropping NEXT from their heads where | ||
| 1002 | ;; applicable. | ||
| 1003 | (eieio--c3-merge-lists | ||
| 1004 | (cons next reversed-partial-result) | ||
| 1005 | (mapcar (lambda (l) (if (eq (cl-first l) next) (cl-rest l) l)) | ||
| 1006 | remaining-inputs)) | ||
| 1007 | ;; The graph is inconsistent, give up | ||
| 1008 | (signal 'inconsistent-class-hierarchy (list remaining-inputs)))))) | ||
| 1009 | |||
| 1010 | (defsubst eieio--class/struct-parents (class) | 967 | (defsubst eieio--class/struct-parents (class) |
| 1011 | (or (eieio--class-parents class) | 968 | (or (eieio--class-parents class) |
| 1012 | `(,eieio-default-superclass))) | 969 | `(,eieio-default-superclass))) |
| @@ -1014,14 +971,13 @@ If a consistent order does not exist, signal an error." | |||
| 1014 | (defun eieio--class-precedence-c3 (class) | 971 | (defun eieio--class-precedence-c3 (class) |
| 1015 | "Return all parents of CLASS in c3 order." | 972 | "Return all parents of CLASS in c3 order." |
| 1016 | (let ((parents (eieio--class-parents class))) | 973 | (let ((parents (eieio--class-parents class))) |
| 1017 | (eieio--c3-merge-lists | 974 | (cons class |
| 1018 | (list class) | 975 | (merge-ordered-lists |
| 1019 | (append | 976 | (append |
| 1020 | (or | 977 | (or |
| 1021 | (mapcar #'eieio--class-precedence-c3 parents) | 978 | (mapcar #'eieio--class-precedence-c3 parents) |
| 1022 | `((,eieio-default-superclass))) | 979 | `((,eieio-default-superclass))) |
| 1023 | (list parents)))) | 980 | (list parents)))))) |
| 1024 | ) | ||
| 1025 | ;;; | 981 | ;;; |
| 1026 | ;; Method Invocation Order: Depth First | 982 | ;; Method Invocation Order: Depth First |
| 1027 | 983 | ||
diff --git a/lisp/simple.el b/lisp/simple.el index 266a66500cb..f79f1013669 100644 --- a/lisp/simple.el +++ b/lisp/simple.el | |||
| @@ -1029,7 +1029,7 @@ that if you use overwrite mode as your normal editing mode, you can use | |||
| 1029 | this function to insert characters when necessary. | 1029 | this function to insert characters when necessary. |
| 1030 | 1030 | ||
| 1031 | In binary overwrite mode, this function does overwrite, and octal | 1031 | In binary overwrite mode, this function does overwrite, and octal |
| 1032 | (or decimal or hex) digits are interpreted as a character code. This | 1032 | \(or decimal or hex) digits are interpreted as a character code. This |
| 1033 | is intended to be useful for editing binary files." | 1033 | is intended to be useful for editing binary files." |
| 1034 | (interactive "*p") | 1034 | (interactive "*p") |
| 1035 | (let* ((char | 1035 | (let* ((char |
diff --git a/lisp/subr.el b/lisp/subr.el index b000787a5d6..a209c76ad85 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -2678,6 +2678,50 @@ The variable list SPEC is the same as in `if-let*'." | |||
| 2678 | 2678 | ||
| 2679 | ;; PUBLIC: find if the current mode derives from another. | 2679 | ;; PUBLIC: find if the current mode derives from another. |
| 2680 | 2680 | ||
| 2681 | (defun merge-ordered-lists (lists &optional error-function) | ||
| 2682 | "Merge LISTS in a consistent order. | ||
| 2683 | LISTS is a list of lists of elements. | ||
| 2684 | Merge them into a single list containing the same elements (removing | ||
| 2685 | duplicates) and obeying their relative positions in each list. | ||
| 2686 | If a consistent order does not exist, call ERROR-FUNCTION with | ||
| 2687 | the remaining lists. | ||
| 2688 | It should return the candidate to use to continue the merge | ||
| 2689 | By default we choose the first element of the first list." | ||
| 2690 | ;; Use [C3](https://en.wikipedia.org/wiki/C3_linearization) | ||
| 2691 | (let ((result '())) | ||
| 2692 | (while (cdr (setq lists (delq nil lists))) | ||
| 2693 | ;; Try to find the next element of the result. This | ||
| 2694 | ;; is achieved by considering the first element of each | ||
| 2695 | ;; (non-empty) input list and accepting a candidate if it is | ||
| 2696 | ;; consistent with the rests of the input lists. | ||
| 2697 | (let* ((next nil) | ||
| 2698 | (tail lists)) | ||
| 2699 | (while tail | ||
| 2700 | (let ((candidate (caar tail)) | ||
| 2701 | (other-lists lists)) | ||
| 2702 | ;; Ensure CANDIDATE is not in any position but the first | ||
| 2703 | ;; in any of the element lists of LISTS. | ||
| 2704 | (while other-lists | ||
| 2705 | (if (not (memq candidate (cdr (car other-lists)))) | ||
| 2706 | (setq other-lists (cdr other-lists)) | ||
| 2707 | (setq candidate nil) | ||
| 2708 | (setq other-lists nil))) | ||
| 2709 | (if (not candidate) | ||
| 2710 | (setq tail (cdr tail)) | ||
| 2711 | (setq next candidate) | ||
| 2712 | (setq tail nil)))) | ||
| 2713 | (unless next ;; The graph is inconsistent. | ||
| 2714 | (setq next (funcall (or error-function #'caar) lists))) | ||
| 2715 | ;; The graph is consistent so far, add NEXT to result and | ||
| 2716 | ;; merge input lists, dropping NEXT from their heads where | ||
| 2717 | ;; applicable. | ||
| 2718 | (push next result) | ||
| 2719 | (setq lists | ||
| 2720 | (mapcar (lambda (l) (if (eq (car l) next) (cdr l) l)) | ||
| 2721 | lists)))) | ||
| 2722 | (if (null result) (car lists) ;; Common case. | ||
| 2723 | (append (nreverse result) (car lists))))) | ||
| 2724 | |||
| 2681 | (defun derived-mode-all-parents (mode &optional known-children) | 2725 | (defun derived-mode-all-parents (mode &optional known-children) |
| 2682 | "Return all the parents of MODE, starting with MODE. | 2726 | "Return all the parents of MODE, starting with MODE. |
| 2683 | The returned list is not fresh, don't modify it. | 2727 | The returned list is not fresh, don't modify it. |
| @@ -2708,17 +2752,12 @@ The returned list is not fresh, don't modify it. | |||
| 2708 | ;; If MODE is an alias, then follow the alias. | 2752 | ;; If MODE is an alias, then follow the alias. |
| 2709 | (let ((alias (symbol-function mode))) | 2753 | (let ((alias (symbol-function mode))) |
| 2710 | (and (symbolp alias) alias)))) | 2754 | (and (symbolp alias) alias)))) |
| 2711 | (parents (cons mode (if parent (funcall all-parents parent)))) | 2755 | (parents (if parent (funcall all-parents parent))) |
| 2712 | (extras (get mode 'derived-mode-extra-parents))) | 2756 | (extras (get mode 'derived-mode-extra-parents))) |
| 2713 | (put mode 'derived-mode--all-parents | 2757 | (put mode 'derived-mode--all-parents |
| 2714 | (if (null extras) ;; Common case. | 2758 | (cons mode |
| 2715 | parents | 2759 | (merge-ordered-lists |
| 2716 | (delete-dups | 2760 | (cons parents (mapcar all-parents extras))))))))) |
| 2717 | (apply #'append | ||
| 2718 | parents (mapcar (lambda (extra) | ||
| 2719 | (copy-sequence | ||
| 2720 | (funcall all-parents extra))) | ||
| 2721 | extras))))))))) | ||
| 2722 | 2761 | ||
| 2723 | (defun provided-mode-derived-p (mode &rest modes) | 2762 | (defun provided-mode-derived-p (mode &rest modes) |
| 2724 | "Non-nil if MODE is derived from one of MODES. | 2763 | "Non-nil if MODE is derived from one of MODES. |