summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2024-06-03 13:26:10 -0400
committerStefan Monnier <monnier@iro.umontreal.ca>2024-06-03 13:40:39 -0400
commit16fc5b6c0c72464a75d9a84b754375662b3acec6 (patch)
tree88bc6831578e836df2d68389d2b2f3c3cb64a674 /lisp/emacs-lisp
parenteb9afd558ec506f1d349dbb61668d6231fda136f (diff)
downloademacs-16fc5b6c0c72464a75d9a84b754375662b3acec6.tar.gz
emacs-16fc5b6c0c72464a75d9a84b754375662b3acec6.tar.bz2
emacs-16fc5b6c0c72464a75d9a84b754375662b3acec6.zip
pcase.el (\`): Try and handle large patterns better
Large backquote patterns tend to lead to very large and deeply nested expansions, but they also tend to contain a lot of "constant" subpatterns that can be compiled to quote patterns. This patch does just that. See discussion at https://lists.gnu.org/archive/html/emacs-devel/2024-05/msg01140.html * lisp/emacs-lisp/pcase.el (pcase--split-pred): Improve the handling of pred-vs-quote so it also works with quoted objects like cons cells, vectors, and strings. Simplify the `pcase--mutually-exclusive-p` branch accordingly. (pcase--expand-\`): New function, extracted from the \` pcase macro. Make it recurse internally, and optimize backquote patterns to `quote` patterns where possible. (\`): Use it. * test/lisp/emacs-lisp/pcase-tests.el (pcase-tests-vectors): Add tests that were broken by a more naïve version of the optimization. (pcase-tests-quote-optimization): New test.
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/pcase.el52
1 files changed, 30 insertions, 22 deletions
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 1a58c60734a..69353daf7d0 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -829,16 +829,8 @@ A and B can be one of:
(let* ((test (cadr (cadr upat)))
(res (pcase--split-pred vars `(pred ,test) pat)))
(cons (cdr res) (car res))))
- ((let ((otherpred
- (cond ((eq 'pred (car-safe pat)) (cadr pat))
- ((not (eq 'quote (car-safe pat))) nil)
- ((consp (cadr pat)) #'consp)
- ((stringp (cadr pat)) #'stringp)
- ((vectorp (cadr pat)) #'vectorp)
- ((compiled-function-p (cadr pat))
- #'compiled-function-p))))
- (and otherpred
- (pcase--mutually-exclusive-p (cadr upat) otherpred)))
+ ((and (eq 'pred (car-safe pat))
+ (pcase--mutually-exclusive-p (cadr upat) (cadr pat)))
'(:pcase--fail . nil))
;; Since we turn (or 'a 'b 'c) into (pred (memq _ '(a b c)))
;; try and preserve the info we get from that memq test.
@@ -852,7 +844,8 @@ A and B can be one of:
'(:pcase--fail . nil))))
((and (eq 'quote (car-safe pat))
(symbolp (cadr upat))
- (or (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat)))
+ (or (get (cadr upat) 'pure) ;FIXME: Drop this `or'?
+ (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat)))
(get (cadr upat) 'side-effect-free)
(ignore-errors
(setq test (list (funcall (cadr upat) (cadr pat))))))
@@ -1124,21 +1117,36 @@ The predicate is the logical-AND of:
- True! (The second element can be anything, and for the sake
of the body forms, its value is bound to the symbol `forum'.)"
(declare (debug (pcase-QPAT)))
+ (pcase--expand-\` qpat))
+
+(defun pcase--expand-\` (qpat)
(cond
((eq (car-safe qpat) '\,) (cadr qpat))
- ((eq (car-safe qpat) '\,@) (error "Unsupported QPAT: %S" qpat))
+ ((or (eq (car-safe qpat) '\,@) (eq qpat '...))
+ (error "Unsupported QPAT: %S" qpat))
((vectorp qpat)
- `(and (pred vectorp)
- (app length ,(length qpat))
- ,@(let ((upats nil))
- (dotimes (i (length qpat))
- (push `(app (aref _ ,i) ,(list '\` (aref qpat i)))
- upats))
- (nreverse upats))))
+ (let* ((trivial t)
+ (contents nil)
+ (upats nil))
+ (dotimes (i (length qpat))
+ (let* ((upat (pcase--expand-\` (aref qpat i))))
+ (if (eq (car-safe upat) 'quote)
+ (push (cadr upat) contents)
+ (setq trivial nil))
+ (push `(app (aref _ ,i) ,upat) upats)))
+ (if trivial
+ `',(apply #'vector (nreverse contents))
+ `(and (pred vectorp)
+ (app length ,(length qpat))
+ ,@(nreverse upats)))))
((consp qpat)
- `(and (pred consp)
- (app car-safe ,(list '\` (car qpat)))
- (app cdr-safe ,(list '\` (cdr qpat)))))
+ (let ((upata (pcase--expand-\` (car qpat)))
+ (upatd (pcase--expand-\` (cdr qpat))))
+ (if (and (eq (car-safe upata) 'quote) (eq (car-safe upatd) 'quote))
+ `'(,(cadr upata) . ,(cadr upatd))
+ `(and (pred consp)
+ (app car-safe ,upata)
+ (app cdr-safe ,upatd)))))
((or (stringp qpat) (numberp qpat) (symbolp qpat)) `',qpat)
;; In all other cases just raise an error so we can't break
;; backward compatibility when adding \` support for other