annotate lisp/emacs-lisp/pcase.el @ 111405:942097a71997

Silence cl-macs.el compilation. * lisp/emacs-lisp/cl-macs.el (loop): Give local variable args a prefix. (cl-parse-loop-clause, cl-loop-handle-accum): Update for above change.
author Glenn Morris <rgm@gnu.org>
date Fri, 05 Nov 2010 00:34:45 -0700
parents e2284aa4cad3
children df6573cbdd34
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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))))"
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
78 (declare (indent 1) (debug case))
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)."
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
89 (if (null bindings) body
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
90 `(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
91 (,(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
92 ;; 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
93 ;; 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
94 (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
95
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
96 ;;;###autoload
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
97 (defmacro pcase-let (bindings body)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
98 "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
99 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
100 of the form (UPAT EXP)."
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
101 (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
102 `(pcase-let* ,bindings ,body)
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
103 (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
104 `(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
105 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
106 (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
107 ,(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
108 bindings)
ba4c4d4dddf5 * lisp/emacs-lisp/pcase.el (pcase-let*, pcase-let): plet -> pcase-let.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 110087
diff changeset
109 ,body))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
110
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
111 (defun pcase-expand (exp cases)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
112 (let* ((defs (if (symbolp exp) '()
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
113 (let ((sym (make-symbol "x")))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
114 (prog1 `((,sym ,exp)) (setq exp sym)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
115 (seen '())
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
116 (codegen
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
117 (lambda (code vars)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
118 (let ((prev (assq code seen)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
119 (if (not prev)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
120 (let ((res (pcase-codegen code vars)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
121 (push (list code vars res) seen)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
122 res)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
123 ;; 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
124 ;; 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
125 ;; 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
126 ;; 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
127 ;; 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
128 ;; 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
129 ;; 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
130 ;;
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
131 ;; 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
132 (destructuring-bind (code prevvars res) prev
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
133 (unless (symbolp res)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
134 ;; 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
135 ;; the branch to a separate function.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
136 (let ((bsym
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
137 (make-symbol (format "pcase-%d" (length defs)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
138 (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
139 (setcar res 'funcall)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
140 (setcdr res (cons bsym (mapcar #'cdr prevvars)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
141 (setcar (cddr prev) bsym)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
142 (setq res bsym)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
143 (setq vars (copy-sequence vars))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
144 (let ((args (mapcar (lambda (pa)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
145 (let ((v (assq (car pa) vars)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
146 (setq vars (delq v vars))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
147 (cdr v)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
148 prevvars)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
149 (when vars ;New additional vars.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
150 (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
151 (mapcar #'car vars)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
152 `(funcall ,res ,@args)))))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
153 (main
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
154 (pcase-u
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
155 (mapcar (lambda (case)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
156 `((match ,exp . ,(car case))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
157 ,(apply-partially
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
158 (if (pcase-small-branch-p (cdr case))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
159 ;; Don't bother sharing multiple
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
160 ;; 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
161 #'pcase-codegen codegen)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
162 (cdr case))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
163 cases))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
164 `(let ,defs ,main)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
165
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
166 (defun pcase-codegen (code vars)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
167 `(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
168 ,@code))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
169
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
170 (defun pcase-small-branch-p (code)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
171 (and (= 1 (length code))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
172 (or (not (consp (car code)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
173 (let ((small t))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
174 (dolist (e (car code))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
175 (if (consp e) (setq small nil)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
176 small))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
177
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
178 ;; 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
179 ;; the depth of the generated tree.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
180 (defun pcase-if (test then else)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
181 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
182 ((eq else :pcase-dontcare) then)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
183 ((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
184 (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
185 ;; 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
186 ;; 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
187 ;; 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
188 ;; 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
189 `(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
190 `(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
191 (,(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
192 (t ,@(nthcdr 3 else)))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
193 ((eq (car-safe else) 'cond)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
194 `(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
195 ;; 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
196 ,@(remove (assoc test else) (cdr else))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
197 (t `(if ,test ,then ,else))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
198
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
199 (defun pcase-upat (qpattern)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
200 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
201 ((eq (car-safe qpattern) '\,) (cadr qpattern))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
202 (t (list '\` qpattern))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
203
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
204 ;; Note about MATCH:
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
205 ;; 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
206 ;; 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
207 ;; (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
208 ;; 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
209 ;; 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
210 ;; 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
211 ;; 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
212 ;; 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
213 ;; 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
214 ;; 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
215 ;; (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
216 ;; 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
217 ;; 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
218 ;; 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
219 ;; 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
220 ;; turn one into the other.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
221
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
222 (defun pcase-u (branches)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
223 "Expand matcher for rules BRANCHES.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
224 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
225 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
226 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
227 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
228 (match VAR . UPAT)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
229 (and MATCH ...)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
230 (or MATCH ...)"
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
231 (when (setq branches (delq nil branches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
232 (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
233 (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
234
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
235 (defun pcase-and (match matches)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
236 (if matches `(and ,match ,@matches) match))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
237
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
238 (defun pcase-split-match (sym splitter match)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
239 (case (car match)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
240 ((match)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
241 (if (not (eq sym (cadr match)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
242 (cons match match)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
243 (let ((pat (cddr match)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
244 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
245 ;; 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
246 ((memq (car-safe pat) '(or and))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
247 (pcase-split-match sym splitter
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
248 (cons (car pat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
249 (mapcar (lambda (alt)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
250 `(match ,sym . ,alt))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
251 (cdr pat)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
252 (t (let ((res (funcall splitter (cddr match))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
253 (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
254 ((or and)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
255 (let ((then-alts '())
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
256 (else-alts '())
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
257 (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
258 (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
259 (dolist (alt (cdr match))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
260 (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
261 (unless (eq (car split) neutral-elem)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
262 (push (car split) then-alts))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
263 (unless (eq (cdr split) neutral-elem)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
264 (push (cdr split) else-alts))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
265 (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
266 ((null then-alts) neutral-elem)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
267 ((null (cdr then-alts)) (car then-alts))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
268 (t (cons (car match) (nreverse then-alts))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
269 (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
270 ((null else-alts) neutral-elem)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
271 ((null (cdr else-alts)) (car else-alts))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
272 (t (cons (car match) (nreverse else-alts)))))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
273 (t (error "Uknown MATCH %s" match))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
274
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
275 (defun pcase-split-rest (sym splitter rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
276 (let ((then-rest '())
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
277 (else-rest '()))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
278 (dolist (branch rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
279 (let* ((match (car branch))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
280 (code&vars (cdr branch))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
281 (splitted
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
282 (pcase-split-match sym splitter match)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
283 (unless (eq (car splitted) :pcase-fail)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
284 (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
285 (unless (eq (cdr splitted) :pcase-fail)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
286 (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
287 (cons (nreverse then-rest) (nreverse else-rest))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
288
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
289 (defun pcase-split-consp (syma symd pat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
290 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
291 ;; 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
292 ((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
293 (let ((qpat (cadr pat)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
294 (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
295 (match ,symd . ,(pcase-upat (cdr qpat))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
296 :pcase-fail)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
297 ;; 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
298 ((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
299
111221
e2284aa4cad3 * lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 110595
diff changeset
300 (defun pcase-split-equal (elem pat)
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
301 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
302 ;; 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
303 ((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
304 (cons :pcase-succeed :pcase-fail))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
305 ;; 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
306 ((and (eq (car-safe pat) '\`)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
307 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
308 ;; (consp (cadr pat)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
309 )
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
310 (cons :pcase-fail nil))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
311
111221
e2284aa4cad3 * lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 110595
diff changeset
312 (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
313 ;; Based on pcase-split-equal.
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
314 (cond
111221
e2284aa4cad3 * lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 110595
diff changeset
315 ;; 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
316 ;; 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
317 ;; (???
b84898221ef1 * lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 109743
diff changeset
318 ;; (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
319 ;; 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
320 ((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
321 nil)
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
322 ;; 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
323 ((and (eq (car-safe pat) '\`)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
324 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
325 ;; (consp (cadr pat)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
326 )
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
327 (cons :pcase-fail nil))))
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 (defun pcase-split-pred (upat pat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
330 ;; 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
331 ;; actually refer to different variables `a'.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
332 (if (equal upat pat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
333 (cons :pcase-succeed :pcase-fail)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
334
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
335 (defun pcase-fgrep (vars sexp)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
336 "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
337 (let ((res '()))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
338 (while (consp sexp)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
339 (dolist (var (pcase-fgrep vars (pop sexp)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
340 (unless (memq var res) (push var res))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
341 (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
342 res))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
343
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
344 ;; 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
345 ;; bootstrapping problems.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
346 (defun pcase-u1 (matches code vars rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
347 "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
348 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
349 \(ELSE-MATCH ELSE-CODE . ELSE-VARS)."
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
350 ;; 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
351 ;; 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
352 ;; 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
353 ;; 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
354 ;; 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
355 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
356 ((null matches) (funcall code vars))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
357 ((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
358 ((eq :pcase-succeed (car matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
359 (pcase-u1 (cdr matches) code vars rest))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
360 ((eq 'and (caar matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
361 (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
362 ((eq 'or (caar matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
363 (let* ((alts (cdar matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
364 (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
365 (simples '()) (others '()))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
366 (when var
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
367 (dolist (alt alts)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
368 (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
369 (let ((upat (cddr alt)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
370 (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
371 (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
372 (stringp (cadr upat))))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
373 (push (cddr alt) simples)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
374 (push alt others))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
375 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
376 ((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
377 ((> (length simples) 1)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
378 ;; 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
379 ;; turned into a `memq' below.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
380 (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
381 code vars
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
382 (if (null others) rest
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
383 (cons (list*
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
384 (pcase-and (if (cdr others)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
385 (cons 'or (nreverse others))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
386 (car others))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
387 (cdr matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
388 code vars)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
389 rest))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
390 (t
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
391 (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
392 (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
393 (cons (list*
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
394 (pcase-and (if (cdr alts)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
395 (cons 'or alts) (car alts))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
396 (cdr matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
397 code vars)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
398 rest)))))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
399 ((eq 'match (caar matches))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
400 (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
401 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
402 ((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
403 ((eq upat 'dontcare) :pcase-dontcare)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
404 ((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
405 ((memq (car-safe upat) '(guard pred))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
406 (destructuring-bind (then-rest &rest else-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
407 (pcase-split-rest
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
408 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
409 (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
410 `(,(cadr upat) ,sym)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
411 (let* ((exp (cadr upat))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
412 ;; `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
413 (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
414 (call (cond
e2284aa4cad3 * lisp/emacs-lisp/pcase.el (pcase): New `string' and `guard' patterns.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 110595
diff changeset
415 ((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
416 ((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
417 (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
418 (if (null vs)
b84898221ef1 * lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 109743
diff changeset
419 call
b84898221ef1 * lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 109743
diff changeset
420 ;; 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
421 ;; 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
422 ;; 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
423 `(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
424 (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
425 vs)
b84898221ef1 * lisp/emacs-lisp/pcase.el (pcase-split-memq): Overenthusiastic optimisation.
Stefan Monnier <monnier@iro.umontreal.ca>
parents: 109743
diff changeset
426 ;; 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
427 ;; (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
428 ,call))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
429 (pcase-u1 matches code vars then-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
430 (pcase-u else-rest))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
431 ((symbolp upat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
432 (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
433 ((eq (car-safe upat) '\`)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
434 (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
435 ((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
436 (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
437 (memq-fine t))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
438 (when all
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
439 (dolist (alt (cdr upat))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
440 (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
441 (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
442 (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
443 (stringp (cadr alt))))
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
444 (setq all nil))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
445 (if all
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
446 ;; 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
447 (let ((elems (mapcar 'cadr (cdr upat))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
448 (destructuring-bind (then-rest &rest else-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
449 (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
450 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
451 (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
452 (pcase-u1 matches code vars then-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
453 (pcase-u else-rest))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
454 (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
455 (append (mapcar (lambda (upat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
456 `((and (match ,sym . ,upat) ,@matches)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
457 ,code ,@vars))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
458 (cddr upat))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
459 rest)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
460 ((eq (car-safe upat) 'and)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
461 (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
462 matches)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
463 code vars rest))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
464 ((eq (car-safe upat) 'not)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
465 ;; 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
466 ;; inefficient code.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
467 ;; 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
468 ;; `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
469 ;; `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
470 ;; 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
471 ;; 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
472 ;; `(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
473 ;; 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
474 (pcase-u1 `((match ,sym . ,(cadr upat)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
475 (lexical-let ((rest rest))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
476 ;; 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
477 ;; 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
478 (lambda (vars)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
479 ;; `vars' will likely contain bindings which are
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
480 ;; not always available in other paths to
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
481 ;; `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
482 ;; them down.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
483 (pcase-u rest)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
484 vars
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
485 (list `((and . ,matches) ,code . ,vars))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
486 (t (error "Unknown upattern `%s'" upat)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
487 (t (error "Incorrect MATCH %s" (car matches)))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
488
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
489 (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
490 "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
491 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
492 \(OTHER_MATCH OTHER-CODE . OTHER-VARS)."
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
493 (cond
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
494 ((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
495 ((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
496 ((vectorp qpat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
497 ;; FIXME.
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
498 (error "Vector QPatterns not implemented yet"))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
499 ((consp qpat)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
500 (let ((syma (make-symbol "xcar"))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
501 (symd (make-symbol "xcdr")))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
502 (destructuring-bind (then-rest &rest else-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
503 (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
504 rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
505 (pcase-if `(consp ,sym)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
506 `(let ((,syma (car ,sym))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
507 (,symd (cdr ,sym)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
508 ,(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
509 (match ,symd . ,(pcase-upat (cdr qpat)))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
510 ,@matches)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
511 code vars then-rest))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
512 (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
513 ((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
514 (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
515 (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
516 (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
517 (pcase-u1 matches code vars then-rest)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
518 (pcase-u else-rest))))
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
519 (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
520
109739
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
521
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
522 (provide 'pcase)
405e0ceb00e2 * lisp/emacs-lisp/pcase.el: New file.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
diff changeset
523 ;;; pcase.el ends here