aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2014-09-22 14:22:02 -0400
committerStefan Monnier2014-09-22 14:22:02 -0400
commit6b33c17c85473d49a01f66f5ce2382d183ba079a (patch)
treef048782a2cf4917698bc56c6c4d021a694537b53
parentf11af8a48cfef05314e6e5d86e18861cffbde9f1 (diff)
parentf8b25a5169905206935ebf49a9e99a7536106e46 (diff)
downloademacs-6b33c17c85473d49a01f66f5ce2382d183ba079a.tar.gz
emacs-6b33c17c85473d49a01f66f5ce2382d183ba079a.zip
Add pcase-defmacro, as well as `quote' and `app' patterns.
* loadup.el: Increase max-lisp-eval-depth when macroexpanding macroexp. * emacs-lisp/pcase.el: Allow (F . ARGS) in `app' patterns. (pcase--funcall, pcase--eval): New functions. (pcase--u1): Use them for guard, pred, let, and app. (\`): Use the new feature to generate better code for vector patterns. * emacs-lisp/pcase.el: Use pcase-defmacro to handle backquote. (pcase--upat): Remove. (pcase--macroexpand): Don't hardcode handling of `. (pcase--split-consp, pcase--split-vector): Remove. (pcase--split-equal): Disregard ` since it's expanded away. (pcase--split-member): Optimize for quote rather than for `. (pcase--split-pred): Optimize for quote rather than for `. (pcase--u1): Remove handling of ` (and of `or' and `and'). Quote non-selfquoting values when passing them to `eq'. Drop `app's let-binding if the variable is not used. (pcase--q1): Remove. (`): Define as a pattern macro. * emacs-lisp/pcase.el (pcase--match): New smart-constructor function. (pcase--expand pcase--q1, pcase--app-subst-match): Use it. (pcase--macroexpand): Handle self-quoting patterns here, expand them to quote patterns. (pcase--split-match): Don't hoist or/and here any more. (pcase--split-equal): Optimize quote patterns as well as ` patterns. (pcase--flip): New helper macro. (pcase--u1): Optimize the memq case directly. Don't handle neither self-quoting nor and/or patterns any more. * emacs-lisp/pcase.el (pcase-defmacro): New macro. (pcase--macroexpand): New function. (pcase--expand): Use it. * emacs-lisp/pcase.el (pcase--app-subst-match, pcase--app-subst-rest): New optimization functions. (pcase--u1): Add support for `quote' and `app'. (pcase): Document them in the docstring.
-rw-r--r--etc/NEWS4
-rw-r--r--lisp/ChangeLog37
-rw-r--r--lisp/emacs-lisp/pcase.el397
-rw-r--r--lisp/loadup.el3
-rw-r--r--test/automated/pcase-tests.el68
5 files changed, 294 insertions, 215 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 398a39ea9f8..397b8866f6b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -102,6 +102,10 @@ performance improvements when pasting large amounts of text.
102 102
103* Changes in Specialized Modes and Packages in Emacs 24.5 103* Changes in Specialized Modes and Packages in Emacs 24.5
104 104
105** pcase
106*** New UPatterns `quote' and `app'.
107*** New UPatterns can be defined with `pcase-defmacro'.
108
105** Lisp mode 109** Lisp mode
106*** Strings after `:documentation' are highlighted as docstrings. 110*** Strings after `:documentation' are highlighted as docstrings.
107 111
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 3fa8ca5a749..256ec813511 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,42 @@
12014-09-22 Stefan Monnier <monnier@iro.umontreal.ca> 12014-09-22 Stefan Monnier <monnier@iro.umontreal.ca>
2 2
3 Add pcase-defmacro, as well as `quote' and `app' patterns.
4 * loadup.el: Increase max-lisp-eval-depth when macroexpanding macroexp.
5 * emacs-lisp/pcase.el: Allow (F . ARGS) in `app' patterns.
6 (pcase--funcall, pcase--eval): New functions.
7 (pcase--u1): Use them for guard, pred, let, and app.
8 (\`): Use the new feature to generate better code for vector patterns.
9 * emacs-lisp/pcase.el: Use pcase-defmacro to handle backquote.
10 (pcase--upat): Remove.
11 (pcase--macroexpand): Don't hardcode handling of `.
12 (pcase--split-consp, pcase--split-vector): Remove.
13 (pcase--split-equal): Disregard ` since it's expanded away.
14 (pcase--split-member): Optimize for quote rather than for `.
15 (pcase--split-pred): Optimize for quote rather than for `.
16 (pcase--u1): Remove handling of ` (and of `or' and `and').
17 Quote non-selfquoting values when passing them to `eq'.
18 Drop `app's let-binding if the variable is not used.
19 (pcase--q1): Remove.
20 (`): Define as a pattern macro.
21 * emacs-lisp/pcase.el (pcase--match): New smart-constructor function.
22 (pcase--expand pcase--q1, pcase--app-subst-match): Use it.
23 (pcase--macroexpand): Handle self-quoting patterns here, expand them to
24 quote patterns.
25 (pcase--split-match): Don't hoist or/and here any more.
26 (pcase--split-equal): Optimize quote patterns as well as ` patterns.
27 (pcase--flip): New helper macro.
28 (pcase--u1): Optimize the memq case directly.
29 Don't handle neither self-quoting nor and/or patterns any more.
30 * emacs-lisp/pcase.el (pcase-defmacro): New macro.
31 (pcase--macroexpand): New function.
32 (pcase--expand): Use it.
33 * emacs-lisp/pcase.el (pcase--app-subst-match, pcase--app-subst-rest):
34 New optimization functions.
35 (pcase--u1): Add support for `quote' and `app'.
36 (pcase): Document them in the docstring.
37
382014-09-22 Stefan Monnier <monnier@iro.umontreal.ca>
39
3 Use lexical-bindin in Ibuffer. 40 Use lexical-bindin in Ibuffer.
4 * ibuffer.el (ibuffer-do-toggle-read-only): `arg' is unused. 41 * ibuffer.el (ibuffer-do-toggle-read-only): `arg' is unused.
5 (ibuffer-compile-format): Simplify. 42 (ibuffer-compile-format): Simplify.
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 94aedd4339a..ddcd4040f2b 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -102,10 +102,12 @@ UPatterns can take the following forms:
102 SYMBOL matches anything and binds it to SYMBOL. 102 SYMBOL matches anything and binds it to SYMBOL.
103 (or UPAT...) matches if any of the patterns matches. 103 (or UPAT...) matches if any of the patterns matches.
104 (and UPAT...) matches if all the patterns match. 104 (and UPAT...) matches if all the patterns match.
105 'VAL matches if the object is `equal' to VAL
105 `QPAT matches if the QPattern QPAT matches. 106 `QPAT matches if the QPattern QPAT matches.
106 (pred PRED) matches if PRED applied to the object returns non-nil. 107 (pred FUN) matches if FUN applied to the object returns non-nil.
107 (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil. 108 (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil.
108 (let UPAT EXP) matches if EXP matches UPAT. 109 (let UPAT EXP) matches if EXP matches UPAT.
110 (app FUN UPAT) matches if FUN applied to the object matches UPAT.
109If a SYMBOL is used twice in the same pattern (i.e. the pattern is 111If a SYMBOL is used twice in the same pattern (i.e. the pattern is
110\"non-linear\"), then the second occurrence is turned into an `eq'uality test. 112\"non-linear\"), then the second occurrence is turned into an `eq'uality test.
111 113
@@ -117,12 +119,14 @@ QPatterns can take the following forms:
117 STRING matches if the object is `equal' to STRING. 119 STRING matches if the object is `equal' to STRING.
118 ATOM matches if the object is `eq' to ATOM. 120 ATOM matches if the object is `eq' to ATOM.
119 121
120PRED can take the form 122FUN can take the form
121 FUNCTION in which case it gets called with one argument. 123 SYMBOL or (lambda ARGS BODY) in which case it's called with one argument.
122 (FUN ARG1 .. ARGN) in which case it gets called with an N+1'th argument 124 (F ARG1 .. ARGn) in which case F gets called with an n+1'th argument
123 which is the value being matched. 125 which is the value being matched.
124A PRED of the form FUNCTION is equivalent to one of the form (FUNCTION). 126So a FUN of the form SYMBOL is equivalent to one of the form (FUN).
125PRED patterns can refer to variables bound earlier in the pattern. 127FUN can refer to variables bound earlier in the pattern.
128FUN is assumed to be pure, i.e. it can be dropped if its result is not used,
129and two identical calls can be merged into one.
126E.g. you can match pairs where the cdr is larger than the car with a pattern 130E.g. you can match pairs where the cdr is larger than the car with a pattern
127like `(,a . ,(pred (< a))) or, with more checks: 131like `(,a . ,(pred (< a))) or, with more checks:
128`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))" 132`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))"
@@ -157,6 +161,7 @@ like `(,a . ,(pred (< a))) or, with more checks:
157 (let* ((x (make-symbol "x")) 161 (let* ((x (make-symbol "x"))
158 (pcase--dontwarn-upats (cons x pcase--dontwarn-upats))) 162 (pcase--dontwarn-upats (cons x pcase--dontwarn-upats)))
159 (pcase--expand 163 (pcase--expand
164 ;; FIXME: Could we add the FILE:LINE data in the error message?
160 exp (append cases `((,x (error "No clause matching `%S'" ,x))))))) 165 exp (append cases `((,x (error "No clause matching `%S'" ,x)))))))
161 166
162(defun pcase--let* (bindings body) 167(defun pcase--let* (bindings body)
@@ -277,7 +282,7 @@ of the form (UPAT EXP)."
277 (main 282 (main
278 (pcase--u 283 (pcase--u
279 (mapcar (lambda (case) 284 (mapcar (lambda (case)
280 `((match ,val . ,(car case)) 285 `(,(pcase--match val (pcase--macroexpand (car case)))
281 ,(lambda (vars) 286 ,(lambda (vars)
282 (unless (memq case used-cases) 287 (unless (memq case used-cases)
283 ;; Keep track of the cases that are used. 288 ;; Keep track of the cases that are used.
@@ -296,6 +301,45 @@ of the form (UPAT EXP)."
296 (message "Redundant pcase pattern: %S" (car case)))) 301 (message "Redundant pcase pattern: %S" (car case))))
297 (macroexp-let* defs main)))) 302 (macroexp-let* defs main))))
298 303
304(defun pcase--macroexpand (pat)
305 "Expands all macro-patterns in PAT."
306 (let ((head (car-safe pat)))
307 (cond
308 ((null head)
309 (if (pcase--self-quoting-p pat) `',pat pat))
310 ((memq head '(pred guard quote)) pat)
311 ((memq head '(or and)) `(,head ,@(mapcar #'pcase--macroexpand (cdr pat))))
312 ((eq head 'let) `(let ,(pcase--macroexpand (cadr pat)) ,@(cddr pat)))
313 ((eq head 'app) `(app ,(nth 1 pat) ,(pcase--macroexpand (nth 2 pat))))
314 (t
315 (let* ((expander (get head 'pcase-macroexpander))
316 (npat (if expander (apply expander (cdr pat)))))
317 (if (null npat)
318 (error (if expander
319 "Unexpandable %s pattern: %S"
320 "Unknown %s pattern: %S")
321 head pat)
322 (pcase--macroexpand npat)))))))
323
324;;;###autoload
325(defmacro pcase-defmacro (name args &rest body)
326 "Define a pcase UPattern macro."
327 (declare (indent 2) (debug (def-name sexp def-body)) (doc-string 3))
328 `(put ',name 'pcase-macroexpander
329 (lambda ,args ,@body)))
330
331(defun pcase--match (val upat)
332 "Build a MATCH structure, hoisting all `or's and `and's outside."
333 (cond
334 ;; Hoist or/and patterns into or/and matches.
335 ((memq (car-safe upat) '(or and))
336 `(,(car upat)
337 ,@(mapcar (lambda (upat)
338 (pcase--match val upat))
339 (cdr upat))))
340 (t
341 `(match ,val . ,upat))))
342
299(defun pcase-codegen (code vars) 343(defun pcase-codegen (code vars)
300 ;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding 344 ;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding
301 ;; let* which might prevent the setcar/setcdr in pcase--expand's fancy 345 ;; let* which might prevent the setcar/setcdr in pcase--expand's fancy
@@ -319,11 +363,6 @@ of the form (UPAT EXP)."
319 ((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen? 363 ((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen?
320 (t (macroexp-if test then else)))) 364 (t (macroexp-if test then else))))
321 365
322(defun pcase--upat (qpattern)
323 (cond
324 ((eq (car-safe qpattern) '\,) (cadr qpattern))
325 (t (list '\` qpattern))))
326
327;; Note about MATCH: 366;; Note about MATCH:
328;; When we have patterns like `(PAT1 . PAT2), after performing the `consp' 367;; When we have patterns like `(PAT1 . PAT2), after performing the `consp'
329;; check, we want to turn all the similar patterns into ones of the form 368;; check, we want to turn all the similar patterns into ones of the form
@@ -399,17 +438,8 @@ MATCH is the pattern that needs to be matched, of the form:
399 ((eq (car match) 'match) 438 ((eq (car match) 'match)
400 (if (not (eq sym (cadr match))) 439 (if (not (eq sym (cadr match)))
401 (cons match match) 440 (cons match match)
402 (let ((pat (cddr match))) 441 (let ((res (funcall splitter (cddr match))))
403 (cond 442 (cons (or (car res) match) (or (cdr res) match)))))
404 ;; Hoist `or' and `and' patterns to `or' and `and' matches.
405 ((memq (car-safe pat) '(or and))
406 (pcase--split-match sym splitter
407 (cons (car pat)
408 (mapcar (lambda (alt)
409 `(match ,sym . ,alt))
410 (cdr pat)))))
411 (t (let ((res (funcall splitter (cddr match))))
412 (cons (or (car res) match) (or (cdr res) match))))))))
413 ((memq (car match) '(or and)) 443 ((memq (car match) '(or and))
414 (let ((then-alts '()) 444 (let ((then-alts '())
415 (else-alts '()) 445 (else-alts '())
@@ -446,45 +476,13 @@ MATCH is the pattern that needs to be matched, of the form:
446 (push (cons (cdr split) code&vars) else-rest)))) 476 (push (cons (cdr split) code&vars) else-rest))))
447 (cons (nreverse then-rest) (nreverse else-rest)))) 477 (cons (nreverse then-rest) (nreverse else-rest))))
448 478
449(defun pcase--split-consp (syma symd pat)
450 (cond
451 ;; A QPattern for a cons, can only go the `then' side.
452 ((and (eq (car-safe pat) '\`) (consp (cadr pat)))
453 (let ((qpat (cadr pat)))
454 (cons `(and (match ,syma . ,(pcase--upat (car qpat)))
455 (match ,symd . ,(pcase--upat (cdr qpat))))
456 :pcase--fail)))
457 ;; A QPattern but not for a cons, can only go to the `else' side.
458 ((eq (car-safe pat) '\`) '(:pcase--fail . nil))
459 ((and (eq (car-safe pat) 'pred)
460 (pcase--mutually-exclusive-p #'consp (cadr pat)))
461 '(:pcase--fail . nil))))
462
463(defun pcase--split-vector (syms pat)
464 (cond
465 ;; A QPattern for a vector of same length.
466 ((and (eq (car-safe pat) '\`)
467 (vectorp (cadr pat))
468 (= (length syms) (length (cadr pat))))
469 (let ((qpat (cadr pat)))
470 (cons `(and ,@(mapcar (lambda (s)
471 `(match ,(car s) .
472 ,(pcase--upat (aref qpat (cdr s)))))
473 syms))
474 :pcase--fail)))
475 ;; Other QPatterns go to the `else' side.
476 ((eq (car-safe pat) '\`) '(:pcase--fail . nil))
477 ((and (eq (car-safe pat) 'pred)
478 (pcase--mutually-exclusive-p #'vectorp (cadr pat)))
479 '(:pcase--fail . nil))))
480
481(defun pcase--split-equal (elem pat) 479(defun pcase--split-equal (elem pat)
482 (cond 480 (cond
483 ;; The same match will give the same result. 481 ;; The same match will give the same result.
484 ((and (eq (car-safe pat) '\`) (equal (cadr pat) elem)) 482 ((and (eq (car-safe pat) 'quote) (equal (cadr pat) elem))
485 '(:pcase--succeed . :pcase--fail)) 483 '(:pcase--succeed . :pcase--fail))
486 ;; A different match will fail if this one succeeds. 484 ;; A different match will fail if this one succeeds.
487 ((and (eq (car-safe pat) '\`) 485 ((and (eq (car-safe pat) 'quote)
488 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) 486 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
489 ;; (consp (cadr pat))) 487 ;; (consp (cadr pat)))
490 ) 488 )
@@ -498,6 +496,7 @@ MATCH is the pattern that needs to be matched, of the form:
498 '(:pcase--fail . nil)))))) 496 '(:pcase--fail . nil))))))
499 497
500(defun pcase--split-member (elems pat) 498(defun pcase--split-member (elems pat)
499 ;; FIXME: The new pred-based member code doesn't do these optimizations!
501 ;; Based on pcase--split-equal. 500 ;; Based on pcase--split-equal.
502 (cond 501 (cond
503 ;; The same match (or a match of membership in a superset) will 502 ;; The same match (or a match of membership in a superset) will
@@ -505,10 +504,10 @@ MATCH is the pattern that needs to be matched, of the form:
505 ;; (??? 504 ;; (???
506 ;; '(:pcase--succeed . nil)) 505 ;; '(:pcase--succeed . nil))
507 ;; A match for one of the elements may succeed or fail. 506 ;; A match for one of the elements may succeed or fail.
508 ((and (eq (car-safe pat) '\`) (member (cadr pat) elems)) 507 ((and (eq (car-safe pat) 'quote) (member (cadr pat) elems))
509 nil) 508 nil)
510 ;; A different match will fail if this one succeeds. 509 ;; A different match will fail if this one succeeds.
511 ((and (eq (car-safe pat) '\`) 510 ((and (eq (car-safe pat) 'quote)
512 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) 511 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
513 ;; (consp (cadr pat))) 512 ;; (consp (cadr pat)))
514 ) 513 )
@@ -539,7 +538,7 @@ MATCH is the pattern that needs to be matched, of the form:
539 ((and (eq 'pred (car upat)) 538 ((and (eq 'pred (car upat))
540 (let ((otherpred 539 (let ((otherpred
541 (cond ((eq 'pred (car-safe pat)) (cadr pat)) 540 (cond ((eq 'pred (car-safe pat)) (cadr pat))
542 ((not (eq '\` (car-safe pat))) nil) 541 ((not (eq 'quote (car-safe pat))) nil)
543 ((consp (cadr pat)) #'consp) 542 ((consp (cadr pat)) #'consp)
544 ((vectorp (cadr pat)) #'vectorp) 543 ((vectorp (cadr pat)) #'vectorp)
545 ((byte-code-function-p (cadr pat)) 544 ((byte-code-function-p (cadr pat))
@@ -547,7 +546,7 @@ MATCH is the pattern that needs to be matched, of the form:
547 (pcase--mutually-exclusive-p (cadr upat) otherpred))) 546 (pcase--mutually-exclusive-p (cadr upat) otherpred)))
548 '(:pcase--fail . nil)) 547 '(:pcase--fail . nil))
549 ((and (eq 'pred (car upat)) 548 ((and (eq 'pred (car upat))
550 (eq '\` (car-safe pat)) 549 (eq 'quote (car-safe pat))
551 (symbolp (cadr upat)) 550 (symbolp (cadr upat))
552 (or (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat))) 551 (or (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat)))
553 (get (cadr upat) 'side-effect-free) 552 (get (cadr upat) 'side-effect-free)
@@ -569,10 +568,70 @@ MATCH is the pattern that needs to be matched, of the form:
569(defun pcase--self-quoting-p (upat) 568(defun pcase--self-quoting-p (upat)
570 (or (keywordp upat) (numberp upat) (stringp upat))) 569 (or (keywordp upat) (numberp upat) (stringp upat)))
571 570
571(defun pcase--app-subst-match (match sym fun nsym)
572 (cond
573 ((eq (car match) 'match)
574 (if (and (eq sym (cadr match))
575 (eq 'app (car-safe (cddr match)))
576 (equal fun (nth 1 (cddr match))))
577 (pcase--match nsym (nth 2 (cddr match)))
578 match))
579 ((memq (car match) '(or and))
580 `(,(car match)
581 ,@(mapcar (lambda (match)
582 (pcase--app-subst-match match sym fun nsym))
583 (cdr match))))
584 (t (error "Uknown MATCH %s" match))))
585
586(defun pcase--app-subst-rest (rest sym fun nsym)
587 (mapcar (lambda (branch)
588 `(,(pcase--app-subst-match (car branch) sym fun nsym)
589 ,@(cdr branch)))
590 rest))
591
572(defsubst pcase--mark-used (sym) 592(defsubst pcase--mark-used (sym)
573 ;; Exceptionally, `sym' may be a constant expression rather than a symbol. 593 ;; Exceptionally, `sym' may be a constant expression rather than a symbol.
574 (if (symbolp sym) (put sym 'pcase-used t))) 594 (if (symbolp sym) (put sym 'pcase-used t)))
575 595
596(defmacro pcase--flip (fun arg1 arg2)
597 "Helper function, used internally to avoid (funcall (lambda ...) ...)."
598 (declare (debug (sexp body)))
599 `(,fun ,arg2 ,arg1))
600
601(defun pcase--funcall (fun arg vars)
602 "Build a function call to FUN with arg ARG."
603 (if (symbolp fun)
604 `(,fun ,arg)
605 (let* (;; `vs' is an upper bound on the vars we need.
606 (vs (pcase--fgrep (mapcar #'car vars) fun))
607 (env (mapcar (lambda (var)
608 (list var (cdr (assq var vars))))
609 vs))
610 (call (progn
611 (when (memq arg vs)
612 ;; `arg' is shadowed by `env'.
613 (let ((newsym (make-symbol "x")))
614 (push (list newsym arg) env)
615 (setq arg newsym)))
616 (if (functionp fun)
617 `(funcall #',fun ,arg)
618 `(,@fun ,arg)))))
619 (if (null vs)
620 call
621 ;; Let's not replace `vars' in `fun' since it's
622 ;; too difficult to do it right, instead just
623 ;; let-bind `vars' around `fun'.
624 `(let* ,env ,call)))))
625
626(defun pcase--eval (exp vars)
627 "Build an expression that will evaluate EXP."
628 (let* ((found (assq exp vars)))
629 (if found (cdr found)
630 (let* ((vs (pcase--fgrep (mapcar #'car vars) exp))
631 (env (mapcar (lambda (v) (list v (cdr (assq v vars))))
632 vs)))
633 (if env (macroexp-let* env exp) exp)))))
634
576;; It's very tempting to use `pcase' below, tho obviously, it'd create 635;; It's very tempting to use `pcase' below, tho obviously, it'd create
577;; bootstrapping problems. 636;; bootstrapping problems.
578(defun pcase--u1 (matches code vars rest) 637(defun pcase--u1 (matches code vars rest)
@@ -594,22 +653,26 @@ Otherwise, it defers to REST which is a list of branches of the form
594 ((eq 'or (caar matches)) 653 ((eq 'or (caar matches))
595 (let* ((alts (cdar matches)) 654 (let* ((alts (cdar matches))
596 (var (if (eq (caar alts) 'match) (cadr (car alts)))) 655 (var (if (eq (caar alts) 'match) (cadr (car alts))))
597 (simples '()) (others '())) 656 (simples '()) (others '()) (memq-ok t))
598 (when var 657 (when var
599 (dolist (alt alts) 658 (dolist (alt alts)
600 (if (and (eq (car alt) 'match) (eq var (cadr alt)) 659 (if (and (eq (car alt) 'match) (eq var (cadr alt))
601 (let ((upat (cddr alt))) 660 (let ((upat (cddr alt)))
602 (and (eq (car-safe upat) '\`) 661 (eq (car-safe upat) 'quote)))
603 (or (integerp (cadr upat)) (symbolp (cadr upat)) 662 (let ((val (cadr (cddr alt))))
604 (stringp (cadr upat)))))) 663 (unless (or (integerp val) (symbolp val))
605 (push (cddr alt) simples) 664 (setq memq-ok nil))
665 (push (cadr (cddr alt)) simples))
606 (push alt others)))) 666 (push alt others))))
607 (cond 667 (cond
608 ((null alts) (error "Please avoid it") (pcase--u rest)) 668 ((null alts) (error "Please avoid it") (pcase--u rest))
669 ;; Yes, we can use `memq' (or `member')!
609 ((> (length simples) 1) 670 ((> (length simples) 1)
610 ;; De-hoist the `or' MATCH into an `or' pattern that will be 671 (pcase--u1 (cons `(match ,var
611 ;; turned into a `memq' below. 672 . (pred (pcase--flip
612 (pcase--u1 (cons `(match ,var or . ,(nreverse simples)) (cdr matches)) 673 ,(if memq-ok #'memq #'member)
674 ',simples)))
675 (cdr matches))
613 code vars 676 code vars
614 (if (null others) rest 677 (if (null others) rest
615 (cons (cons 678 (cons (cons
@@ -643,35 +706,11 @@ Otherwise, it defers to REST which is a list of branches of the form
643 sym (lambda (pat) (pcase--split-pred vars upat pat)) rest)) 706 sym (lambda (pat) (pcase--split-pred vars upat pat)) rest))
644 (then-rest (car splitrest)) 707 (then-rest (car splitrest))
645 (else-rest (cdr splitrest))) 708 (else-rest (cdr splitrest)))
646 (pcase--if (if (and (eq (car upat) 'pred) (symbolp (cadr upat))) 709 (pcase--if (if (eq (car upat) 'pred)
647 `(,(cadr upat) ,sym) 710 (pcase--funcall (cadr upat) sym vars)
648 (let* ((exp (cadr upat)) 711 (pcase--eval (cadr upat) vars))
649 ;; `vs' is an upper bound on the vars we need.
650 (vs (pcase--fgrep (mapcar #'car vars) exp))
651 (env (mapcar (lambda (var)
652 (list var (cdr (assq var vars))))
653 vs))
654 (call (if (eq 'guard (car upat))
655 exp
656 (when (memq sym vs)
657 ;; `sym' is shadowed by `env'.
658 (let ((newsym (make-symbol "x")))
659 (push (list newsym sym) env)
660 (setq sym newsym)))
661 (if (functionp exp)
662 `(funcall #',exp ,sym)
663 `(,@exp ,sym)))))
664 (if (null vs)
665 call
666 ;; Let's not replace `vars' in `exp' since it's
667 ;; too difficult to do it right, instead just
668 ;; let-bind `vars' around `exp'.
669 `(let* ,env ,call))))
670 (pcase--u1 matches code vars then-rest) 712 (pcase--u1 matches code vars then-rest)
671 (pcase--u else-rest)))) 713 (pcase--u else-rest))))
672 ((pcase--self-quoting-p upat)
673 (pcase--mark-used sym)
674 (pcase--q1 sym upat matches code vars rest))
675 ((symbolp upat) 714 ((symbolp upat)
676 (pcase--mark-used sym) 715 (pcase--mark-used sym)
677 (if (not (assq upat vars)) 716 (if (not (assq upat vars))
@@ -686,57 +725,41 @@ Otherwise, it defers to REST which is a list of branches of the form
686 ;; (cons (cons (nth 1 upat) (nth 2 upat)) vars) rest) 725 ;; (cons (cons (nth 1 upat) (nth 2 upat)) vars) rest)
687 (macroexp-let2 726 (macroexp-let2
688 macroexp-copyable-p sym 727 macroexp-copyable-p sym
689 (let* ((exp (nth 2 upat)) 728 (pcase--eval (nth 2 upat) vars)
690 (found (assq exp vars))) 729 (pcase--u1 (cons (pcase--match sym (nth 1 upat)) matches)
691 (if found (cdr found)
692 (let* ((vs (pcase--fgrep (mapcar #'car vars) exp))
693 (env (mapcar (lambda (v) (list v (cdr (assq v vars))))
694 vs)))
695 (if env (macroexp-let* env exp) exp))))
696 (pcase--u1 (cons `(match ,sym . ,(nth 1 upat)) matches)
697 code vars rest))) 730 code vars rest)))
698 ((eq (car-safe upat) '\`) 731 ((eq (car-safe upat) 'app)
732 ;; A upat of the form (app FUN UPAT)
699 (pcase--mark-used sym) 733 (pcase--mark-used sym)
700 (pcase--q1 sym (cadr upat) matches code vars rest)) 734 (let* ((fun (nth 1 upat))
701 ((eq (car-safe upat) 'or) 735 (nsym (make-symbol "x"))
702 (let ((all (> (length (cdr upat)) 1)) 736 (body
703 (memq-fine t)) 737 ;; We don't change `matches' to reuse the newly computed value,
704 (when all 738 ;; because we assume there shouldn't be such redundancy in there.
705 (dolist (alt (cdr upat)) 739 (pcase--u1 (cons (pcase--match nsym (nth 2 upat)) matches)
706 (unless (if (pcase--self-quoting-p alt) 740 code vars
707 (progn 741 (pcase--app-subst-rest rest sym fun nsym))))
708 (unless (or (symbolp alt) (integerp alt)) 742 (if (not (get nsym 'pcase-used))
709 (setq memq-fine nil)) 743 body
710 t) 744 (macroexp-let*
711 (and (eq (car-safe alt) '\`) 745 `((,nsym ,(pcase--funcall fun sym vars)))
712 (or (symbolp (cadr alt)) (integerp (cadr alt)) 746 body))))
713 (setq memq-fine nil) 747 ((eq (car-safe upat) 'quote)
714 (stringp (cadr alt))))) 748 (pcase--mark-used sym)
715 (setq all nil)))) 749 (let* ((val (cadr upat))
716 (if all 750 (splitrest (pcase--split-rest
717 ;; Use memq for (or `a `b `c `d) rather than a big tree. 751 sym (lambda (pat) (pcase--split-equal val pat)) rest))
718 (let* ((elems (mapcar (lambda (x) (if (consp x) (cadr x) x)) 752 (then-rest (car splitrest))
719 (cdr upat))) 753 (else-rest (cdr splitrest)))
720 (splitrest 754 (pcase--if (cond
721 (pcase--split-rest 755 ((null val) `(null ,sym))
722 sym (lambda (pat) (pcase--split-member elems pat)) rest)) 756 ((or (integerp val) (symbolp val))
723 (then-rest (car splitrest)) 757 (if (pcase--self-quoting-p val)
724 (else-rest (cdr splitrest))) 758 `(eq ,sym ,val)
725 (pcase--mark-used sym) 759 `(eq ,sym ',val)))
726 (pcase--if `(,(if memq-fine #'memq #'member) ,sym ',elems) 760 (t `(equal ,sym ',val)))
727 (pcase--u1 matches code vars then-rest) 761 (pcase--u1 matches code vars then-rest)
728 (pcase--u else-rest))) 762 (pcase--u else-rest))))
729 (pcase--u1 (cons `(match ,sym ,@(cadr upat)) matches) code vars
730 (append (mapcar (lambda (upat)
731 `((and (match ,sym . ,upat) ,@matches)
732 ,code ,@vars))
733 (cddr upat))
734 rest)))))
735 ((eq (car-safe upat) 'and)
736 (pcase--u1 (append (mapcar (lambda (upat) `(match ,sym ,@upat))
737 (cdr upat))
738 matches)
739 code vars rest))
740 ((eq (car-safe upat) 'not) 763 ((eq (car-safe upat) 'not)
741 ;; FIXME: The implementation below is naive and results in 764 ;; FIXME: The implementation below is naive and results in
742 ;; inefficient code. 765 ;; inefficient code.
@@ -758,79 +781,25 @@ Otherwise, it defers to REST which is a list of branches of the form
758 (pcase--u rest)) 781 (pcase--u rest))
759 vars 782 vars
760 (list `((and . ,matches) ,code . ,vars)))) 783 (list `((and . ,matches) ,code . ,vars))))
761 (t (error "Unknown upattern `%s'" upat))))) 784 (t (error "Unknown internal pattern `%S'" upat)))))
762 (t (error "Incorrect MATCH %s" (car matches))))) 785 (t (error "Incorrect MATCH %S" (car matches)))))
763 786
764(defun pcase--q1 (sym qpat matches code vars rest) 787(pcase-defmacro \` (qpat)
765 "Return code that runs CODE if SYM matches QPAT and if MATCHES match.
766Otherwise, it defers to REST which is a list of branches of the form
767\(OTHER_MATCH OTHER-CODE . OTHER-VARS)."
768 (cond 788 (cond
769 ((eq (car-safe qpat) '\,) (error "Can't use `,UPATTERN")) 789 ((eq (car-safe qpat) '\,) (cadr qpat))
770 ((floatp qpat) (error "Floating point patterns not supported"))
771 ((vectorp qpat) 790 ((vectorp qpat)
772 (let* ((len (length qpat)) 791 `(and (pred vectorp)
773 (syms (mapcar (lambda (i) (cons (make-symbol (format "xaref%s" i)) i)) 792 (app length ,(length qpat))
774 (number-sequence 0 (1- len)))) 793 ,@(let ((upats nil))
775 (splitrest (pcase--split-rest 794 (dotimes (i (length qpat))
776 sym 795 (push `(app (pcase--flip aref ,i) ,(list '\` (aref qpat i)))
777 (lambda (pat) (pcase--split-vector syms pat)) 796 upats))
778 rest)) 797 (nreverse upats))))
779 (then-rest (car splitrest))
780 (else-rest (cdr splitrest))
781 (then-body (pcase--u1
782 `(,@(mapcar (lambda (s)
783 `(match ,(car s) .
784 ,(pcase--upat (aref qpat (cdr s)))))
785 syms)
786 ,@matches)
787 code vars then-rest)))
788 (pcase--if
789 `(and (vectorp ,sym) (= (length ,sym) ,len))
790 (macroexp-let* (delq nil (mapcar (lambda (s)
791 (and (get (car s) 'pcase-used)
792 `(,(car s) (aref ,sym ,(cdr s)))))
793 syms))
794 then-body)
795 (pcase--u else-rest))))
796 ((consp qpat) 798 ((consp qpat)
797 (let* ((syma (make-symbol "xcar")) 799 `(and (pred consp)
798 (symd (make-symbol "xcdr")) 800 (app car ,(list '\` (car qpat)))
799 (splitrest (pcase--split-rest 801 (app cdr ,(list '\` (cdr qpat)))))
800 sym 802 ((or (stringp qpat) (integerp qpat) (symbolp qpat)) `',qpat)))
801 (lambda (pat) (pcase--split-consp syma symd pat))
802 rest))
803 (then-rest (car splitrest))
804 (else-rest (cdr splitrest))
805 (then-body (pcase--u1 `((match ,syma . ,(pcase--upat (car qpat)))
806 (match ,symd . ,(pcase--upat (cdr qpat)))
807 ,@matches)
808 code vars then-rest)))
809 (pcase--if
810 `(consp ,sym)
811 ;; We want to be careful to only add bindings that are used.
812 ;; The byte-compiler could do that for us, but it would have to pay
813 ;; attention to the `consp' test in order to figure out that car/cdr
814 ;; can't signal errors and our byte-compiler is not that clever.
815 ;; FIXME: Some of those let bindings occur too early (they are used in
816 ;; `then-body', but only within some sub-branch).
817 (macroexp-let*
818 `(,@(if (get syma 'pcase-used) `((,syma (car ,sym))))
819 ,@(if (get symd 'pcase-used) `((,symd (cdr ,sym)))))
820 then-body)
821 (pcase--u else-rest))))
822 ((or (integerp qpat) (symbolp qpat) (stringp qpat))
823 (let* ((splitrest (pcase--split-rest
824 sym (lambda (pat) (pcase--split-equal qpat pat)) rest))
825 (then-rest (car splitrest))
826 (else-rest (cdr splitrest)))
827 (pcase--if (cond
828 ((stringp qpat) `(equal ,sym ,qpat))
829 ((null qpat) `(null ,sym))
830 (t `(eq ,sym ',qpat)))
831 (pcase--u1 matches code vars then-rest)
832 (pcase--u else-rest))))
833 (t (error "Unknown QPattern %s" qpat))))
834 803
835 804
836(provide 'pcase) 805(provide 'pcase)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index b911e9f1768..417f0b411c5 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -119,7 +119,8 @@
119 (let ((macroexp--pending-eager-loads '(skip))) 119 (let ((macroexp--pending-eager-loads '(skip)))
120 (load "emacs-lisp/pcase")) 120 (load "emacs-lisp/pcase"))
121 ;; Re-load macroexp so as to eagerly macro-expand its uses of pcase. 121 ;; Re-load macroexp so as to eagerly macro-expand its uses of pcase.
122 (load "emacs-lisp/macroexp")) 122 (let ((max-lisp-eval-depth (* 2 max-lisp-eval-depth)))
123 (load "emacs-lisp/macroexp")))
123 124
124(load "cus-face") 125(load "cus-face")
125(load "faces") ; after here, `defface' may be used. 126(load "faces") ; after here, `defface' may be used.
diff --git a/test/automated/pcase-tests.el b/test/automated/pcase-tests.el
new file mode 100644
index 00000000000..ec0c3bc7fd5
--- /dev/null
+++ b/test/automated/pcase-tests.el
@@ -0,0 +1,68 @@
1;;; pcase-tests.el --- Test suite for pcase macro.
2
3;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;;; Code:
23
24(require 'ert)
25(require 'cl-lib)
26
27(ert-deftest pcase-tests-base ()
28 "Test pcase code."
29 (should (equal (pcase '(1 . 2) ((app car '2) 6) ((app car '1) 5)) 5)))
30
31(pcase-defmacro pcase-tests-plus (pat n)
32 `(app (lambda (v) (- v ,n)) ,pat))
33
34(ert-deftest pcase-tests-macro ()
35 (should (equal (pcase 5 ((pcase-tests-plus x 3) x)) 2)))
36
37(defun pcase-tests-grep (fname exp)
38 (when (consp exp)
39 (or (eq fname (car exp))
40 (cl-some (lambda (exp) (pcase-tests-grep fname exp)) (cdr exp)))))
41
42(ert-deftest pcase-tests-tests ()
43 (should (pcase-tests-grep 'memq '(or (+ 2 3) (memq x y))))
44 (should-not (pcase-tests-grep 'memq '(or (+ 2 3) (- x y)))))
45
46(ert-deftest pcase-tests-member ()
47 (should (pcase-tests-grep
48 'memq (macroexpand-all '(pcase x ((or 1 2 3) body)))))
49 (should (pcase-tests-grep
50 'member (macroexpand-all '(pcase x ((or '"a" '2 '3) body)))))
51 (should-not (pcase-tests-grep
52 'memq (macroexpand-all '(pcase x ((or "a" 2 3) body)))))
53 (let ((exp (macroexpand-all
54 '(pcase x
55 ("a" body1)
56 (2 body2)
57 ((or "a" 2 3) body)))))
58 (should-not (pcase-tests-grep 'memq exp))
59 (should-not (pcase-tests-grep 'member exp))))
60
61(ert-deftest pcase-tests-vectors ()
62 (should (equal (pcase [1 2] (`[,x] 1) (`[,x ,y] (+ x y))) 3)))
63
64;; Local Variables:
65;; no-byte-compile: t
66;; End:
67
68;;; pcase-tests.el ends here.