diff options
| author | Stefan Monnier | 2014-09-22 14:22:02 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2014-09-22 14:22:02 -0400 |
| commit | 6b33c17c85473d49a01f66f5ce2382d183ba079a (patch) | |
| tree | f048782a2cf4917698bc56c6c4d021a694537b53 | |
| parent | f11af8a48cfef05314e6e5d86e18861cffbde9f1 (diff) | |
| parent | f8b25a5169905206935ebf49a9e99a7536106e46 (diff) | |
| download | emacs-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/NEWS | 4 | ||||
| -rw-r--r-- | lisp/ChangeLog | 37 | ||||
| -rw-r--r-- | lisp/emacs-lisp/pcase.el | 397 | ||||
| -rw-r--r-- | lisp/loadup.el | 3 | ||||
| -rw-r--r-- | test/automated/pcase-tests.el | 68 |
5 files changed, 294 insertions, 215 deletions
| @@ -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 @@ | |||
| 1 | 2014-09-22 Stefan Monnier <monnier@iro.umontreal.ca> | 1 | 2014-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 | |||
| 38 | 2014-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. | ||
| 109 | If a SYMBOL is used twice in the same pattern (i.e. the pattern is | 111 | If 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 | ||
| 120 | PRED can take the form | 122 | FUN 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. |
| 124 | A PRED of the form FUNCTION is equivalent to one of the form (FUNCTION). | 126 | So a FUN of the form SYMBOL is equivalent to one of the form (FUN). |
| 125 | PRED patterns can refer to variables bound earlier in the pattern. | 127 | FUN can refer to variables bound earlier in the pattern. |
| 128 | FUN is assumed to be pure, i.e. it can be dropped if its result is not used, | ||
| 129 | and two identical calls can be merged into one. | ||
| 126 | E.g. you can match pairs where the cdr is larger than the car with a pattern | 130 | E.g. you can match pairs where the cdr is larger than the car with a pattern |
| 127 | like `(,a . ,(pred (< a))) or, with more checks: | 131 | like `(,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. | ||
| 766 | Otherwise, 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. | ||