Mercurial > emacs
annotate lisp/emacs-lisp/pcase.el @ 111654:1ce970bc77c0
ChangeLog fixes prompted by M-x authors.
author | Glenn Morris <rgm@gnu.org> |
---|---|
date | Sat, 20 Nov 2010 14:25:56 -0800 |
parents | df6573cbdd34 |
children | 31c8556ccad8 |
rev | line source |
---|---|
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
1 ;;; pcase.el --- ML-style pattern-matching macro for Elisp |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
2 |
109743
d40bac543314
* lisp/emacs-lisp/pcase.el: Fix copyright header.
Glenn Morris <rgm@gnu.org>
parents:
109739
diff
changeset
|
3 ;; Copyright (C) 2010 Free Software Foundation, Inc. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
4 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
5 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
6 ;; Keywords: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
7 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
8 ;; This file is part of GNU Emacs. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
9 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
10 ;; GNU Emacs is free software: you can redistribute it and/or modify |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
11 ;; it under the terms of the GNU General Public License as published by |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
12 ;; the Free Software Foundation, either version 3 of the License, or |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
13 ;; (at your option) any later version. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
14 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
15 ;; GNU Emacs is distributed in the hope that it will be useful, |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
18 ;; GNU General Public License for more details. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
19 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
20 ;; You should have received a copy of the GNU General Public License |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
22 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
23 ;;; Commentary: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
24 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
25 ;; ML-style pattern matching. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
26 ;; The entry points are autoloaded. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
27 |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
28 ;; Todo: |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
29 |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
30 ;; - provide ways to extend the set of primitives, with some kind of |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
31 ;; define-pcase-matcher. We could easily make it so that (guard BOOLEXP) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
32 ;; could be defined this way, as a shorthand for (pred (lambda (_) BOOLEXP)). |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
33 ;; But better would be if we could define new ways to match by having the |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
34 ;; extension provide its own `pcase-split-<foo>' thingy. |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
35 ;; - ideally we'd want (pcase s ((re RE1) E1) ((re RE2) E2)) to be able to |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
36 ;; generate a lex-style DFA to decide whether to run E1 or E2. |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
37 |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
38 ;;; Code: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
39 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
40 (eval-when-compile (require 'cl)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
41 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
42 ;; Macro-expansion of pcase is reasonably fast, so it's not a problem |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
43 ;; when byte-compiling a file, but when interpreting the code, if the pcase |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
44 ;; is in a loop, the repeated macro-expansion becomes terribly costly, so we |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
45 ;; memoize previous macro expansions to try and avoid recomputing them |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
46 ;; over and over again. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
47 (defconst pcase-memoize (make-hash-table :weakness t :test 'equal)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
48 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
49 ;;;###autoload |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
50 (defmacro pcase (exp &rest cases) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
51 "Perform ML-style pattern matching on EXP. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
52 CASES is a list of elements of the form (UPATTERN CODE...). |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
53 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
54 UPatterns can take the following forms: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
55 _ matches anything. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
56 SYMBOL matches anything and binds it to SYMBOL. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
57 (or UPAT...) matches if any of the patterns matches. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
58 (and UPAT...) matches if all the patterns match. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
59 `QPAT matches if the QPattern QPAT matches. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
60 (pred PRED) matches if PRED applied to the object returns non-nil. |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
61 (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
62 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
63 QPatterns can take the following forms: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
64 (QPAT1 . QPAT2) matches if QPAT1 matches the car and QPAT2 the cdr. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
65 ,UPAT matches if the UPattern UPAT matches. |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
66 STRING matches if the object is `equal' to STRING. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
67 ATOM matches if the object is `eq' to ATOM. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
68 QPatterns for vectors are not implemented yet. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
69 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
70 PRED can take the form |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
71 FUNCTION in which case it gets called with one argument. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
72 (FUN ARG1 .. ARGN) in which case it gets called with N+1 arguments. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
73 A PRED of the form FUNCTION is equivalent to one of the form (FUNCTION). |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
74 PRED patterns can refer to variables bound earlier in the pattern. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
75 E.g. you can match pairs where the cdr is larger than the car with a pattern |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
76 like `(,a . ,(pred (< a))) or, with more checks: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
77 `(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))" |
111502
df6573cbdd34
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): Add debug and
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
111221
diff
changeset
|
78 (declare (indent 1) (debug case)) ;FIXME: edebug `guard' and vars. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
79 (or (gethash (cons exp cases) pcase-memoize) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
80 (puthash (cons exp cases) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
81 (pcase-expand exp cases) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
82 pcase-memoize))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
83 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
84 ;;;###autoload |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
85 (defmacro pcase-let* (bindings body) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
86 "Like `let*' but where you can use `pcase' patterns for bindings. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
87 BODY should be an expression, and BINDINGS should be a list of bindings |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
88 of the form (UPAT EXP)." |
111502
df6573cbdd34
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): Add debug and
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
111221
diff
changeset
|
89 (declare (indent 1) (debug let)) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
90 (if (null bindings) body |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
91 `(pcase ,(cadr (car bindings)) |
110595
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
92 (,(caar bindings) (pcase-let* ,(cdr bindings) ,body)) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
93 ;; FIXME: In many cases `dontcare' would be preferable, so maybe we |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
94 ;; should have `let' and `elet', like we have `case' and `ecase'. |
110595
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
95 (t (error "Pattern match failure in `pcase-let'"))))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
96 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
97 ;;;###autoload |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
98 (defmacro pcase-let (bindings body) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
99 "Like `let' but where you can use `pcase' patterns for bindings. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
100 BODY should be an expression, and BINDINGS should be a list of bindings |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
101 of the form (UPAT EXP)." |
111502
df6573cbdd34
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): Add debug and
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
111221
diff
changeset
|
102 (declare (indent 1) (debug let)) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
103 (if (null (cdr bindings)) |
110595
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
104 `(pcase-let* ,bindings ,body) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
105 (setq bindings (mapcar (lambda (x) (cons (make-symbol "x") x)) bindings)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
106 `(let ,(mapcar (lambda (binding) (list (nth 0 binding) (nth 2 binding))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
107 bindings) |
110595
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
108 (pcase-let* |
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
109 ,(mapcar (lambda (binding) (list (nth 1 binding) (nth 0 binding))) |
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
110 bindings) |
ba4c4d4dddf5
* lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110087
diff
changeset
|
111 ,body)))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
112 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
113 (defun pcase-expand (exp cases) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
114 (let* ((defs (if (symbolp exp) '() |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
115 (let ((sym (make-symbol "x"))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
116 (prog1 `((,sym ,exp)) (setq exp sym))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
117 (seen '()) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
118 (codegen |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
119 (lambda (code vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
120 (let ((prev (assq code seen))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
121 (if (not prev) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
122 (let ((res (pcase-codegen code vars))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
123 (push (list code vars res) seen) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
124 res) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
125 ;; Since we use a tree-based pattern matching |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
126 ;; technique, the leaves (the places that contain the |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
127 ;; code to run once a pattern is matched) can get |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
128 ;; copied a very large number of times, so to avoid |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
129 ;; code explosion, we need to keep track of how many |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
130 ;; times we've used each leaf and move it |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
131 ;; to a separate function if that number is too high. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
132 ;; |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
133 ;; We've already used this branch. So it is shared. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
134 (destructuring-bind (code prevvars res) prev |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
135 (unless (symbolp res) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
136 ;; This is the first repeat, so we have to move |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
137 ;; the branch to a separate function. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
138 (let ((bsym |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
139 (make-symbol (format "pcase-%d" (length defs))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
140 (push `(,bsym (lambda ,(mapcar #'car prevvars) ,@code)) defs) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
141 (setcar res 'funcall) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
142 (setcdr res (cons bsym (mapcar #'cdr prevvars))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
143 (setcar (cddr prev) bsym) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
144 (setq res bsym))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
145 (setq vars (copy-sequence vars)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
146 (let ((args (mapcar (lambda (pa) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
147 (let ((v (assq (car pa) vars))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
148 (setq vars (delq v vars)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
149 (cdr v))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
150 prevvars))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
151 (when vars ;New additional vars. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
152 (error "The vars %s are only bound in some paths" |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
153 (mapcar #'car vars))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
154 `(funcall ,res ,@args))))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
155 (main |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
156 (pcase-u |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
157 (mapcar (lambda (case) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
158 `((match ,exp . ,(car case)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
159 ,(apply-partially |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
160 (if (pcase-small-branch-p (cdr case)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
161 ;; Don't bother sharing multiple |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
162 ;; occurrences of this leaf since it's small. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
163 #'pcase-codegen codegen) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
164 (cdr case)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
165 cases)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
166 `(let ,defs ,main))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
167 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
168 (defun pcase-codegen (code vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
169 `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
170 ,@code)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
171 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
172 (defun pcase-small-branch-p (code) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
173 (and (= 1 (length code)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
174 (or (not (consp (car code))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
175 (let ((small t)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
176 (dolist (e (car code)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
177 (if (consp e) (setq small nil))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
178 small)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
179 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
180 ;; Try to use `cond' rather than a sequence of `if's, so as to reduce |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
181 ;; the depth of the generated tree. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
182 (defun pcase-if (test then else) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
183 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
184 ((eq else :pcase-dontcare) then) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
185 ((eq (car-safe else) 'if) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
186 (if (equal test (nth 1 else)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
187 ;; Doing a test a second time: get rid of the redundancy. |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
188 ;; FIXME: ideally, this should never happen because the pcase-split-* |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
189 ;; functions should have eliminated such things, but pcase-split-member |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
190 ;; is imprecise, so in practice it does happen occasionally. |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
191 `(if ,test ,then ,@(nthcdr 3 else)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
192 `(cond (,test ,then) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
193 (,(nth 1 else) ,(nth 2 else)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
194 (t ,@(nthcdr 3 else))))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
195 ((eq (car-safe else) 'cond) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
196 `(cond (,test ,then) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
197 ;; Doing a test a second time: get rid of the redundancy, as above. |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
198 ,@(remove (assoc test else) (cdr else)))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
199 (t `(if ,test ,then ,else)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
200 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
201 (defun pcase-upat (qpattern) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
202 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
203 ((eq (car-safe qpattern) '\,) (cadr qpattern)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
204 (t (list '\` qpattern)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
205 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
206 ;; Note about MATCH: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
207 ;; When we have patterns like `(PAT1 . PAT2), after performing the `consp' |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
208 ;; check, we want to turn all the similar patterns into ones of the form |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
209 ;; (and (match car PAT1) (match cdr PAT2)), so you naturally need conjunction. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
210 ;; Earlier code hence used branches of the form (MATCHES . CODE) where |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
211 ;; MATCHES was a list (implicitly a conjunction) of (SYM . PAT). |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
212 ;; But if we have a pattern of the form (or `(PAT1 . PAT2) PAT3), there is |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
213 ;; no easy way to eliminate the `consp' check in such a representation. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
214 ;; So we replaced the MATCHES by the MATCH below which can be made up |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
215 ;; of conjunctions and disjunctions, so if we know `foo' is a cons, we can |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
216 ;; turn (match foo . (or `(PAT1 . PAT2) PAT3)) into |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
217 ;; (or (and (match car . `PAT1) (match cdr . `PAT2)) (match foo . PAT3)). |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
218 ;; The downside is that we now have `or' and `and' both in MATCH and |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
219 ;; in PAT, so there are different equivalent representations and we |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
220 ;; need to handle them all. We do not try to systematically |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
221 ;; canonicalize them to one form over another, but we do occasionally |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
222 ;; turn one into the other. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
223 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
224 (defun pcase-u (branches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
225 "Expand matcher for rules BRANCHES. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
226 Each BRANCH has the form (MATCH CODE . VARS) where |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
227 CODE is the code generator for that branch. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
228 VARS is the set of vars already bound by earlier matches. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
229 MATCH is the pattern that needs to be matched, of the form: |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
230 (match VAR . UPAT) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
231 (and MATCH ...) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
232 (or MATCH ...)" |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
233 (when (setq branches (delq nil branches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
234 (destructuring-bind (match code &rest vars) (car branches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
235 (pcase-u1 (list match) code vars (cdr branches))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
236 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
237 (defun pcase-and (match matches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
238 (if matches `(and ,match ,@matches) match)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
239 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
240 (defun pcase-split-match (sym splitter match) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
241 (case (car match) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
242 ((match) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
243 (if (not (eq sym (cadr match))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
244 (cons match match) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
245 (let ((pat (cddr match))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
246 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
247 ;; Hoist `or' and `and' patterns to `or' and `and' matches. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
248 ((memq (car-safe pat) '(or and)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
249 (pcase-split-match sym splitter |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
250 (cons (car pat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
251 (mapcar (lambda (alt) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
252 `(match ,sym . ,alt)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
253 (cdr pat))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
254 (t (let ((res (funcall splitter (cddr match)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
255 (cons (or (car res) match) (or (cdr res) match)))))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
256 ((or and) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
257 (let ((then-alts '()) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
258 (else-alts '()) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
259 (neutral-elem (if (eq 'or (car match)) :pcase-fail :pcase-succeed)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
260 (zero-elem (if (eq 'or (car match)) :pcase-succeed :pcase-fail))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
261 (dolist (alt (cdr match)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
262 (let ((split (pcase-split-match sym splitter alt))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
263 (unless (eq (car split) neutral-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
264 (push (car split) then-alts)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
265 (unless (eq (cdr split) neutral-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
266 (push (cdr split) else-alts)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
267 (cons (cond ((memq zero-elem then-alts) zero-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
268 ((null then-alts) neutral-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
269 ((null (cdr then-alts)) (car then-alts)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
270 (t (cons (car match) (nreverse then-alts)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
271 (cond ((memq zero-elem else-alts) zero-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
272 ((null else-alts) neutral-elem) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
273 ((null (cdr else-alts)) (car else-alts)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
274 (t (cons (car match) (nreverse else-alts))))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
275 (t (error "Uknown MATCH %s" match)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
276 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
277 (defun pcase-split-rest (sym splitter rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
278 (let ((then-rest '()) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
279 (else-rest '())) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
280 (dolist (branch rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
281 (let* ((match (car branch)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
282 (code&vars (cdr branch)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
283 (splitted |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
284 (pcase-split-match sym splitter match))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
285 (unless (eq (car splitted) :pcase-fail) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
286 (push (cons (car splitted) code&vars) then-rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
287 (unless (eq (cdr splitted) :pcase-fail) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
288 (push (cons (cdr splitted) code&vars) else-rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
289 (cons (nreverse then-rest) (nreverse else-rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
290 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
291 (defun pcase-split-consp (syma symd pat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
292 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
293 ;; A QPattern for a cons, can only go the `then' side. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
294 ((and (eq (car-safe pat) '\`) (consp (cadr pat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
295 (let ((qpat (cadr pat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
296 (cons `(and (match ,syma . ,(pcase-upat (car qpat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
297 (match ,symd . ,(pcase-upat (cdr qpat)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
298 :pcase-fail))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
299 ;; A QPattern but not for a cons, can only go the `else' side. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
300 ((eq (car-safe pat) '\`) (cons :pcase-fail nil)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
301 |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
302 (defun pcase-split-equal (elem pat) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
303 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
304 ;; The same match will give the same result. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
305 ((and (eq (car-safe pat) '\`) (equal (cadr pat) elem)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
306 (cons :pcase-succeed :pcase-fail)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
307 ;; A different match will fail if this one succeeds. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
308 ((and (eq (car-safe pat) '\`) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
309 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
310 ;; (consp (cadr pat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
311 ) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
312 (cons :pcase-fail nil)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
313 |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
314 (defun pcase-split-member (elems pat) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
315 ;; Based on pcase-split-equal. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
316 (cond |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
317 ;; The same match (or a match of membership in a superset) will |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
318 ;; give the same result, but we don't know how to check it. |
110087
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
319 ;; (??? |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
320 ;; (cons :pcase-succeed nil)) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
321 ;; A match for one of the elements may succeed or fail. |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
322 ((and (eq (car-safe pat) '\`) (member (cadr pat) elems)) |
110087
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
323 nil) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
324 ;; A different match will fail if this one succeeds. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
325 ((and (eq (car-safe pat) '\`) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
326 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
327 ;; (consp (cadr pat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
328 ) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
329 (cons :pcase-fail nil)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
330 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
331 (defun pcase-split-pred (upat pat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
332 ;; FIXME: For predicates like (pred (> a)), two such predicates may |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
333 ;; actually refer to different variables `a'. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
334 (if (equal upat pat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
335 (cons :pcase-succeed :pcase-fail))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
336 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
337 (defun pcase-fgrep (vars sexp) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
338 "Check which of the symbols VARS appear in SEXP." |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
339 (let ((res '())) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
340 (while (consp sexp) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
341 (dolist (var (pcase-fgrep vars (pop sexp))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
342 (unless (memq var res) (push var res)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
343 (and (memq sexp vars) (not (memq sexp res)) (push sexp res)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
344 res)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
345 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
346 ;; It's very tempting to use `pcase' below, tho obviously, it'd create |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
347 ;; bootstrapping problems. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
348 (defun pcase-u1 (matches code vars rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
349 "Return code that runs CODE (with VARS) if MATCHES match. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
350 and otherwise defers to REST which is a list of branches of the form |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
351 \(ELSE-MATCH ELSE-CODE . ELSE-VARS)." |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
352 ;; Depending on the order in which we choose to check each of the MATCHES, |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
353 ;; the resulting tree may be smaller or bigger. So in general, we'd want |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
354 ;; to be careful to chose the "optimal" order. But predicate |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
355 ;; patterns make this harder because they create dependencies |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
356 ;; between matches. So we don't bother trying to reorder anything. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
357 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
358 ((null matches) (funcall code vars)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
359 ((eq :pcase-fail (car matches)) (pcase-u rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
360 ((eq :pcase-succeed (car matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
361 (pcase-u1 (cdr matches) code vars rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
362 ((eq 'and (caar matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
363 (pcase-u1 (append (cdar matches) (cdr matches)) code vars rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
364 ((eq 'or (caar matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
365 (let* ((alts (cdar matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
366 (var (if (eq (caar alts) 'match) (cadr (car alts)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
367 (simples '()) (others '())) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
368 (when var |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
369 (dolist (alt alts) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
370 (if (and (eq (car alt) 'match) (eq var (cadr alt)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
371 (let ((upat (cddr alt))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
372 (and (eq (car-safe upat) '\`) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
373 (or (integerp (cadr upat)) (symbolp (cadr upat)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
374 (stringp (cadr upat)))))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
375 (push (cddr alt) simples) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
376 (push alt others)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
377 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
378 ((null alts) (error "Please avoid it") (pcase-u rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
379 ((> (length simples) 1) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
380 ;; De-hoist the `or' MATCH into an `or' pattern that will be |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
381 ;; turned into a `memq' below. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
382 (pcase-u1 (cons `(match ,var or . ,(nreverse simples)) (cdr matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
383 code vars |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
384 (if (null others) rest |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
385 (cons (list* |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
386 (pcase-and (if (cdr others) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
387 (cons 'or (nreverse others)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
388 (car others)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
389 (cdr matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
390 code vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
391 rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
392 (t |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
393 (pcase-u1 (cons (pop alts) (cdr matches)) code vars |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
394 (if (null alts) (progn (error "Please avoid it") rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
395 (cons (list* |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
396 (pcase-and (if (cdr alts) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
397 (cons 'or alts) (car alts)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
398 (cdr matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
399 code vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
400 rest))))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
401 ((eq 'match (caar matches)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
402 (destructuring-bind (op sym &rest upat) (pop matches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
403 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
404 ((memq upat '(t _)) (pcase-u1 matches code vars rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
405 ((eq upat 'dontcare) :pcase-dontcare) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
406 ((functionp upat) (error "Feature removed, use (pred %s)" upat)) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
407 ((memq (car-safe upat) '(guard pred)) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
408 (destructuring-bind (then-rest &rest else-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
409 (pcase-split-rest |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
410 sym (apply-partially 'pcase-split-pred upat) rest) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
411 (pcase-if (if (and (eq (car upat) 'pred) (symbolp (cadr upat))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
412 `(,(cadr upat) ,sym) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
413 (let* ((exp (cadr upat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
414 ;; `vs' is an upper bound on the vars we need. |
110087
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
415 (vs (pcase-fgrep (mapcar #'car vars) exp)) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
416 (call (cond |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
417 ((eq 'guard (car upat)) exp) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
418 ((functionp exp) `(,exp ,sym)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
419 (t `(,@exp ,sym))))) |
110087
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
420 (if (null vs) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
421 call |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
422 ;; Let's not replace `vars' in `exp' since it's |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
423 ;; too difficult to do it right, instead just |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
424 ;; let-bind `vars' around `exp'. |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
425 `(let ,(mapcar (lambda (var) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
426 (list var (cdr (assq var vars)))) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
427 vs) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
428 ;; FIXME: `vars' can capture `sym'. E.g. |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
429 ;; (pcase x ((and `(,x . ,y) (pred (fun x))))) |
b84898221ef1
* lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
109743
diff
changeset
|
430 ,call)))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
431 (pcase-u1 matches code vars then-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
432 (pcase-u else-rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
433 ((symbolp upat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
434 (pcase-u1 matches code (cons (cons upat sym) vars) rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
435 ((eq (car-safe upat) '\`) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
436 (pcase-q1 sym (cadr upat) matches code vars rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
437 ((eq (car-safe upat) 'or) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
438 (let ((all (> (length (cdr upat)) 1)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
439 (memq-fine t)) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
440 (when all |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
441 (dolist (alt (cdr upat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
442 (unless (and (eq (car-safe alt) '\`) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
443 (or (symbolp (cadr alt)) (integerp (cadr alt)) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
444 (setq memq-fine nil) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
445 (stringp (cadr alt)))) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
446 (setq all nil)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
447 (if all |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
448 ;; Use memq for (or `a `b `c `d) rather than a big tree. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
449 (let ((elems (mapcar 'cadr (cdr upat)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
450 (destructuring-bind (then-rest &rest else-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
451 (pcase-split-rest |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
452 sym (apply-partially 'pcase-split-member elems) rest) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
453 (pcase-if `(,(if memq-fine #'memq #'member) ,sym ',elems) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
454 (pcase-u1 matches code vars then-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
455 (pcase-u else-rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
456 (pcase-u1 (cons `(match ,sym ,@(cadr upat)) matches) code vars |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
457 (append (mapcar (lambda (upat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
458 `((and (match ,sym . ,upat) ,@matches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
459 ,code ,@vars)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
460 (cddr upat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
461 rest))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
462 ((eq (car-safe upat) 'and) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
463 (pcase-u1 (append (mapcar (lambda (upat) `(match ,sym ,@upat)) (cdr upat)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
464 matches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
465 code vars rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
466 ((eq (car-safe upat) 'not) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
467 ;; FIXME: The implementation below is naive and results in |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
468 ;; inefficient code. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
469 ;; To make it work right, we would need to turn pcase-u1's |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
470 ;; `code' and `vars' into a single argument of the same form as |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
471 ;; `rest'. We would also need to split this new `then-rest' argument |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
472 ;; for every test (currently we don't bother to do it since |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
473 ;; it's only useful for odd patterns like (and `(PAT1 . PAT2) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
474 ;; `(PAT3 . PAT4)) which the programmer can easily rewrite |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
475 ;; to the more efficient `(,(and PAT1 PAT3) . ,(and PAT2 PAT4))). |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
476 (pcase-u1 `((match ,sym . ,(cadr upat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
477 (lexical-let ((rest rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
478 ;; FIXME: This codegen is not careful to share its |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
479 ;; code if used several times: code blow up is likely. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
480 (lambda (vars) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
481 ;; `vars' will likely contain bindings which are |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
482 ;; not always available in other paths to |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
483 ;; `rest', so there' no point trying to pass |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
484 ;; them down. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
485 (pcase-u rest))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
486 vars |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
487 (list `((and . ,matches) ,code . ,vars)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
488 (t (error "Unknown upattern `%s'" upat))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
489 (t (error "Incorrect MATCH %s" (car matches))))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
490 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
491 (defun pcase-q1 (sym qpat matches code vars rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
492 "Return code that runs CODE if SYM matches QPAT and if MATCHES match. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
493 and if not, defers to REST which is a list of branches of the form |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
494 \(OTHER_MATCH OTHER-CODE . OTHER-VARS)." |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
495 (cond |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
496 ((eq (car-safe qpat) '\,) (error "Can't use `,UPATTERN")) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
497 ((floatp qpat) (error "Floating point patterns not supported")) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
498 ((vectorp qpat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
499 ;; FIXME. |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
500 (error "Vector QPatterns not implemented yet")) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
501 ((consp qpat) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
502 (let ((syma (make-symbol "xcar")) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
503 (symd (make-symbol "xcdr"))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
504 (destructuring-bind (then-rest &rest else-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
505 (pcase-split-rest sym (apply-partially 'pcase-split-consp syma symd) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
506 rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
507 (pcase-if `(consp ,sym) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
508 `(let ((,syma (car ,sym)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
509 (,symd (cdr ,sym))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
510 ,(pcase-u1 `((match ,syma . ,(pcase-upat (car qpat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
511 (match ,symd . ,(pcase-upat (cdr qpat))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
512 ,@matches) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
513 code vars then-rest)) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
514 (pcase-u else-rest))))) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
515 ((or (integerp qpat) (symbolp qpat) (stringp qpat)) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
516 (destructuring-bind (then-rest &rest else-rest) |
111221
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
517 (pcase-split-rest sym (apply-partially 'pcase-split-equal qpat) rest) |
e2284aa4cad3
* lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
110595
diff
changeset
|
518 (pcase-if `(,(if (stringp qpat) #'equal #'eq) ,sym ',qpat) |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
519 (pcase-u1 matches code vars then-rest) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
520 (pcase-u else-rest)))) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
521 (t (error "Unkown QPattern %s" qpat)))) |
109743
d40bac543314
* lisp/emacs-lisp/pcase.el: Fix copyright header.
Glenn Morris <rgm@gnu.org>
parents:
109739
diff
changeset
|
522 |
109739
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
523 |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
524 (provide 'pcase) |
405e0ceb00e2
* lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff
changeset
|
525 ;;; pcase.el ends here |