2012-06-08 13:18:26 +00:00
|
|
|
;;; pcase.el --- ML-style pattern-matching macro for Elisp -*- lexical-binding: t; coding: utf-8 -*-
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2013-01-01 09:11:05 +00:00
|
|
|
;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
|
2011-02-28 04:24:40 +00:00
|
|
|
;; Keywords:
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
;; ML-style pattern matching.
|
|
|
|
;; The entry points are autoloaded.
|
|
|
|
|
2010-10-29 01:05:38 +00:00
|
|
|
;; Todo:
|
|
|
|
|
2011-03-16 20:08:39 +00:00
|
|
|
;; - (pcase e (`(,x . ,x) foo)) signals an "x unused" warning if `foo' doesn't
|
|
|
|
;; use x, because x is bound separately for the equality constraint
|
|
|
|
;; (as well as any pred/guard) and for the body, so uses at one place don't
|
|
|
|
;; count for the other.
|
2010-10-29 01:05:38 +00:00
|
|
|
;; - provide ways to extend the set of primitives, with some kind of
|
|
|
|
;; define-pcase-matcher. We could easily make it so that (guard BOOLEXP)
|
|
|
|
;; could be defined this way, as a shorthand for (pred (lambda (_) BOOLEXP)).
|
|
|
|
;; But better would be if we could define new ways to match by having the
|
2010-11-24 16:39:51 +00:00
|
|
|
;; extension provide its own `pcase--split-<foo>' thingy.
|
2011-03-16 20:08:39 +00:00
|
|
|
;; - along these lines, provide patterns to match CL structs.
|
2011-02-27 02:50:38 +00:00
|
|
|
;; - provide something like (setq VAR) so a var can be set rather than
|
|
|
|
;; let-bound.
|
2012-05-26 15:52:27 +00:00
|
|
|
;; - provide a way to fallthrough to subsequent cases (not sure what I meant by
|
|
|
|
;; this :-()
|
2011-02-27 02:50:38 +00:00
|
|
|
;; - try and be more clever to reduce the size of the decision tree, and
|
2011-03-16 20:08:39 +00:00
|
|
|
;; to reduce the number of leaves that need to be turned into function:
|
2011-02-27 02:50:38 +00:00
|
|
|
;; - first, do the tests shared by all remaining branches (it will have
|
2012-05-26 15:52:27 +00:00
|
|
|
;; to be performed anyway, so better do it first so it's shared).
|
2011-02-27 02:50:38 +00:00
|
|
|
;; - then choose the test that discriminates more (?).
|
2012-05-26 15:52:27 +00:00
|
|
|
;; - provide Agda's `with' (along with its `...' companion).
|
|
|
|
;; - implement (not UPAT). This might require a significant redesign.
|
2010-10-29 01:05:38 +00:00
|
|
|
;; - ideally we'd want (pcase s ((re RE1) E1) ((re RE2) E2)) to be able to
|
|
|
|
;; generate a lex-style DFA to decide whether to run E1 or E2.
|
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
;;; Code:
|
|
|
|
|
2012-06-07 19:25:48 +00:00
|
|
|
(require 'macroexp)
|
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
;; Macro-expansion of pcase is reasonably fast, so it's not a problem
|
|
|
|
;; when byte-compiling a file, but when interpreting the code, if the pcase
|
|
|
|
;; is in a loop, the repeated macro-expansion becomes terribly costly, so we
|
|
|
|
;; memoize previous macro expansions to try and avoid recomputing them
|
|
|
|
;; over and over again.
|
2012-09-04 17:40:25 +00:00
|
|
|
;; FIXME: Now that macroexpansion is also performed when loading an interpreted
|
|
|
|
;; file, this is not a real problem any more.
|
2011-03-06 04:48:17 +00:00
|
|
|
(defconst pcase--memoize (make-hash-table :weakness 'key :test 'eq))
|
2012-06-11 00:33:33 +00:00
|
|
|
;; (defconst pcase--memoize-1 (make-hash-table :test 'eq))
|
|
|
|
;; (defconst pcase--memoize-2 (make-hash-table :weakness 'key :test 'equal))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
More CL cleanups and reduction of use of cl.el.
* woman.el, winner.el, vc/vc-rcs.el, vc/vc-hooks.el, vc/vc-hg.el:
* vc/vc-git.el, vc/vc-dir.el, vc/vc-bzr.el, vc/vc-annotate.el:
* textmodes/tex-mode.el, textmodes/sgml-mode.el, tar-mode.el:
* strokes.el, ses.el, server.el, progmodes/js.el, progmodes/gdb-mi.el:
* progmodes/flymake.el, progmodes/ebrowse.el, progmodes/compile.el:
* play/tetris.el, play/snake.el, play/pong.el, play/landmark.el:
* play/hanoi.el, play/decipher.el, play/5x5.el, nxml/nxml-mode.el:
* net/secrets.el, net/quickurl.el, midnight.el, mail/footnote.el:
* image-dired.el, ibuffer.el, ibuf-macs.el, ibuf-ext.el, hexl.el:
* eshell/eshell.el, eshell/esh-io.el, eshell/esh-ext.el:
* eshell/esh-cmd.el, eshell/em-ls.el, eshell/em-hist.el:
* eshell/em-cmpl.el, eshell/em-banner.el:
* url/url.el, url/url-queue.el, url/url-parse.el, url/url-http.el:
* url/url-future.el, url/url-dav.el, url/url-cookie.el:
* calendar/parse-time.el, test/eshell.el: Use cl-lib.
* wid-browse.el, wdired.el, vc/vc.el, vc/vc-mtn.el, vc/vc-cvs.el:
* vc/vc-arch.el, tree-widget.el, textmodes/texinfo.el:
* textmodes/refill.el, textmodes/css-mode.el, term/tvi970.el:
* term/ns-win.el, term.el, shell.el, ps-samp.el:
* progmodes/perl-mode.el, progmodes/pascal.el, progmodes/gud.el:
* progmodes/glasses.el, progmodes/etags.el, progmodes/cwarn.el:
* play/gamegrid.el, play/bubbles.el, novice.el, notifications.el:
* net/zeroconf.el, net/xesam.el, net/snmp-mode.el, net/mairix.el:
* net/ldap.el, net/eudc.el, net/browse-url.el, man.el:
* mail/mailheader.el, mail/feedmail.el:
* url/url-util.el, url/url-privacy.el, url/url-nfs.el, url/url-misc.el:
* url/url-methods.el, url/url-gw.el, url/url-file.el, url/url-expand.el:
Dont use CL.
* ibuf-ext.el (ibuffer-mark-old-buffers): Use float-time.
* eshell/esh-opt.el (eshell-eval-using-options): Quote code with
`lambda' rather than with `quote'.
(eshell-do-opt): Adjust accordingly.
(eshell-process-option): Simplify.
* eshell/esh-var.el:
* eshell/em-script.el: Require `esh-opt' for eshell-eval-using-options.
* emacs-pcase.el (pcase--dontcare-upats, pcase--let*)
(pcase--expand, pcase--u1): Rename pcase's internal `dontcare' pattern
to `pcase--dontcare'.
* emacs-cl.el (labels): Mark obsolete.
(cl--letf, letf): Move to cl-lib.
(cl--letf*, letf*): Remove.
* emacs-cl-lib.el (cl-nth-value): Use defalias.
* emacs-cl-macs.el (cl-dolist, cl-dotimes): Add indent rule.
(cl-progv): Rewrite.
(cl--letf, cl-letf): Move from cl.el.
(cl-letf*): New macro.
* emacs-cl-extra.el (cl--progv-before, cl--progv-after): Remove.
2012-07-11 23:13:41 +00:00
|
|
|
(defconst pcase--dontcare-upats '(t _ pcase--dontcare))
|
2010-11-24 16:39:51 +00:00
|
|
|
|
2012-06-11 00:46:21 +00:00
|
|
|
(def-edebug-spec
|
|
|
|
pcase-UPAT
|
|
|
|
(&or symbolp
|
|
|
|
("or" &rest pcase-UPAT)
|
|
|
|
("and" &rest pcase-UPAT)
|
|
|
|
("`" pcase-QPAT)
|
|
|
|
("guard" form)
|
|
|
|
("let" pcase-UPAT form)
|
|
|
|
("pred"
|
|
|
|
&or lambda-expr
|
|
|
|
;; Punt on macros/special forms.
|
|
|
|
(functionp &rest form)
|
|
|
|
sexp)
|
|
|
|
sexp))
|
|
|
|
|
|
|
|
(def-edebug-spec
|
|
|
|
pcase-QPAT
|
|
|
|
(&or ("," pcase-UPAT)
|
|
|
|
(pcase-QPAT . pcase-QPAT)
|
|
|
|
sexp))
|
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
;;;###autoload
|
|
|
|
(defmacro pcase (exp &rest cases)
|
|
|
|
"Perform ML-style pattern matching on EXP.
|
|
|
|
CASES is a list of elements of the form (UPATTERN CODE...).
|
|
|
|
|
|
|
|
UPatterns can take the following forms:
|
|
|
|
_ matches anything.
|
2012-07-10 09:26:04 +00:00
|
|
|
SELFQUOTING matches itself. This includes keywords, numbers, and strings.
|
2010-08-10 13:18:14 +00:00
|
|
|
SYMBOL matches anything and binds it to SYMBOL.
|
|
|
|
(or UPAT...) matches if any of the patterns matches.
|
|
|
|
(and UPAT...) matches if all the patterns match.
|
|
|
|
`QPAT matches if the QPattern QPAT matches.
|
|
|
|
(pred PRED) matches if PRED applied to the object returns non-nil.
|
2010-10-29 01:05:38 +00:00
|
|
|
(guard BOOLEXP) matches if BOOLEXP evaluates to non-nil.
|
2011-03-16 20:08:39 +00:00
|
|
|
(let UPAT EXP) matches if EXP matches UPAT.
|
2011-02-18 04:58:21 +00:00
|
|
|
If a SYMBOL is used twice in the same pattern (i.e. the pattern is
|
|
|
|
\"non-linear\"), then the second occurrence is turned into an `eq'uality test.
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
QPatterns can take the following forms:
|
|
|
|
(QPAT1 . QPAT2) matches if QPAT1 matches the car and QPAT2 the cdr.
|
|
|
|
,UPAT matches if the UPattern UPAT matches.
|
2011-02-28 04:24:40 +00:00
|
|
|
STRING matches if the object is `equal' to STRING.
|
2010-08-10 13:18:14 +00:00
|
|
|
ATOM matches if the object is `eq' to ATOM.
|
|
|
|
QPatterns for vectors are not implemented yet.
|
|
|
|
|
|
|
|
PRED can take the form
|
2011-02-28 04:24:40 +00:00
|
|
|
FUNCTION in which case it gets called with one argument.
|
2012-07-26 01:27:33 +00:00
|
|
|
(FUN ARG1 .. ARGN) in which case it gets called with an N+1'th argument
|
|
|
|
which is the value being matched.
|
2010-08-10 13:18:14 +00:00
|
|
|
A PRED of the form FUNCTION is equivalent to one of the form (FUNCTION).
|
|
|
|
PRED patterns can refer to variables bound earlier in the pattern.
|
|
|
|
E.g. you can match pairs where the cdr is larger than the car with a pattern
|
|
|
|
like `(,a . ,(pred (< a))) or, with more checks:
|
|
|
|
`(,(and a (pred numberp)) . ,(and (pred numberp) (pred (< a))))"
|
2012-06-11 00:46:21 +00:00
|
|
|
(declare (indent 1) (debug (form &rest (pcase-UPAT body))))
|
2011-03-06 04:48:17 +00:00
|
|
|
;; We want to use a weak hash table as a cache, but the key will unavoidably
|
|
|
|
;; be based on `exp' and `cases', yet `cases' is a fresh new list each time
|
|
|
|
;; we're called so it'll be immediately GC'd. So we use (car cases) as key
|
|
|
|
;; which does come straight from the source code and should hence not be GC'd
|
|
|
|
;; so easily.
|
|
|
|
(let ((data (gethash (car cases) pcase--memoize)))
|
|
|
|
;; data = (EXP CASES . EXPANSION)
|
|
|
|
(if (and (equal exp (car data)) (equal cases (cadr data)))
|
|
|
|
;; We have the right expansion.
|
|
|
|
(cddr data)
|
2012-06-11 00:33:33 +00:00
|
|
|
;; (when (gethash (car cases) pcase--memoize-1)
|
|
|
|
;; (message "pcase-memoize failed because of weak key!!"))
|
|
|
|
;; (when (gethash (car cases) pcase--memoize-2)
|
|
|
|
;; (message "pcase-memoize failed because of eq test on %S"
|
|
|
|
;; (car cases)))
|
2011-03-06 04:48:17 +00:00
|
|
|
(when data
|
|
|
|
(message "pcase-memoize: equal first branch, yet different"))
|
|
|
|
(let ((expansion (pcase--expand exp cases)))
|
2012-06-11 00:33:33 +00:00
|
|
|
(puthash (car cases) `(,exp ,cases ,@expansion) pcase--memoize)
|
|
|
|
;; (puthash (car cases) `(,exp ,cases ,@expansion) pcase--memoize-1)
|
|
|
|
;; (puthash (car cases) `(,exp ,cases ,@expansion) pcase--memoize-2)
|
2011-03-06 04:48:17 +00:00
|
|
|
expansion))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2012-06-11 00:33:33 +00:00
|
|
|
(defun pcase--let* (bindings body)
|
|
|
|
(cond
|
|
|
|
((null bindings) (macroexp-progn body))
|
|
|
|
((pcase--trivial-upat-p (caar bindings))
|
|
|
|
(macroexp-let* `(,(car bindings)) (pcase--let* (cdr bindings) body)))
|
|
|
|
(t
|
|
|
|
(let ((binding (pop bindings)))
|
|
|
|
(pcase--expand
|
|
|
|
(cadr binding)
|
|
|
|
`((,(car binding) ,(pcase--let* bindings body))
|
More CL cleanups and reduction of use of cl.el.
* woman.el, winner.el, vc/vc-rcs.el, vc/vc-hooks.el, vc/vc-hg.el:
* vc/vc-git.el, vc/vc-dir.el, vc/vc-bzr.el, vc/vc-annotate.el:
* textmodes/tex-mode.el, textmodes/sgml-mode.el, tar-mode.el:
* strokes.el, ses.el, server.el, progmodes/js.el, progmodes/gdb-mi.el:
* progmodes/flymake.el, progmodes/ebrowse.el, progmodes/compile.el:
* play/tetris.el, play/snake.el, play/pong.el, play/landmark.el:
* play/hanoi.el, play/decipher.el, play/5x5.el, nxml/nxml-mode.el:
* net/secrets.el, net/quickurl.el, midnight.el, mail/footnote.el:
* image-dired.el, ibuffer.el, ibuf-macs.el, ibuf-ext.el, hexl.el:
* eshell/eshell.el, eshell/esh-io.el, eshell/esh-ext.el:
* eshell/esh-cmd.el, eshell/em-ls.el, eshell/em-hist.el:
* eshell/em-cmpl.el, eshell/em-banner.el:
* url/url.el, url/url-queue.el, url/url-parse.el, url/url-http.el:
* url/url-future.el, url/url-dav.el, url/url-cookie.el:
* calendar/parse-time.el, test/eshell.el: Use cl-lib.
* wid-browse.el, wdired.el, vc/vc.el, vc/vc-mtn.el, vc/vc-cvs.el:
* vc/vc-arch.el, tree-widget.el, textmodes/texinfo.el:
* textmodes/refill.el, textmodes/css-mode.el, term/tvi970.el:
* term/ns-win.el, term.el, shell.el, ps-samp.el:
* progmodes/perl-mode.el, progmodes/pascal.el, progmodes/gud.el:
* progmodes/glasses.el, progmodes/etags.el, progmodes/cwarn.el:
* play/gamegrid.el, play/bubbles.el, novice.el, notifications.el:
* net/zeroconf.el, net/xesam.el, net/snmp-mode.el, net/mairix.el:
* net/ldap.el, net/eudc.el, net/browse-url.el, man.el:
* mail/mailheader.el, mail/feedmail.el:
* url/url-util.el, url/url-privacy.el, url/url-nfs.el, url/url-misc.el:
* url/url-methods.el, url/url-gw.el, url/url-file.el, url/url-expand.el:
Dont use CL.
* ibuf-ext.el (ibuffer-mark-old-buffers): Use float-time.
* eshell/esh-opt.el (eshell-eval-using-options): Quote code with
`lambda' rather than with `quote'.
(eshell-do-opt): Adjust accordingly.
(eshell-process-option): Simplify.
* eshell/esh-var.el:
* eshell/em-script.el: Require `esh-opt' for eshell-eval-using-options.
* emacs-pcase.el (pcase--dontcare-upats, pcase--let*)
(pcase--expand, pcase--u1): Rename pcase's internal `dontcare' pattern
to `pcase--dontcare'.
* emacs-cl.el (labels): Mark obsolete.
(cl--letf, letf): Move to cl-lib.
(cl--letf*, letf*): Remove.
* emacs-cl-lib.el (cl-nth-value): Use defalias.
* emacs-cl-macs.el (cl-dolist, cl-dotimes): Add indent rule.
(cl-progv): Rewrite.
(cl--letf, cl-letf): Move from cl.el.
(cl-letf*): New macro.
* emacs-cl-extra.el (cl--progv-before, cl--progv-after): Remove.
2012-07-11 23:13:41 +00:00
|
|
|
;; We can either signal an error here, or just use `pcase--dontcare'
|
|
|
|
;; which generates more efficient code. In practice, if we use
|
|
|
|
;; `pcase--dontcare' we will still often get an error and the few
|
|
|
|
;; cases where we don't do not matter that much, so
|
|
|
|
;; it's a better choice.
|
|
|
|
(pcase--dontcare nil)))))))
|
2012-06-11 00:33:33 +00:00
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
;;;###autoload
|
2010-11-24 16:39:51 +00:00
|
|
|
(defmacro pcase-let* (bindings &rest body)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Like `let*' but where you can use `pcase' patterns for bindings.
|
|
|
|
BODY should be an expression, and BINDINGS should be a list of bindings
|
|
|
|
of the form (UPAT EXP)."
|
2012-05-15 18:45:27 +00:00
|
|
|
(declare (indent 1)
|
2012-06-11 00:46:21 +00:00
|
|
|
(debug ((&rest (pcase-UPAT &optional form)) body)))
|
2012-06-11 00:33:33 +00:00
|
|
|
(let ((cached (gethash bindings pcase--memoize)))
|
|
|
|
;; cached = (BODY . EXPANSION)
|
|
|
|
(if (equal (car cached) body)
|
|
|
|
(cdr cached)
|
|
|
|
(let ((expansion (pcase--let* bindings body)))
|
|
|
|
(puthash bindings (cons body expansion) pcase--memoize)
|
|
|
|
expansion))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
;;;###autoload
|
2010-11-24 16:39:51 +00:00
|
|
|
(defmacro pcase-let (bindings &rest body)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Like `let' but where you can use `pcase' patterns for bindings.
|
2010-11-24 16:39:51 +00:00
|
|
|
BODY should be a list of expressions, and BINDINGS should be a list of bindings
|
2010-08-10 13:18:14 +00:00
|
|
|
of the form (UPAT EXP)."
|
2012-05-15 18:45:27 +00:00
|
|
|
(declare (indent 1) (debug pcase-let*))
|
2010-08-10 13:18:14 +00:00
|
|
|
(if (null (cdr bindings))
|
2010-11-24 16:39:51 +00:00
|
|
|
`(pcase-let* ,bindings ,@body)
|
|
|
|
(let ((matches '()))
|
|
|
|
(dolist (binding (prog1 bindings (setq bindings nil)))
|
|
|
|
(cond
|
|
|
|
((memq (car binding) pcase--dontcare-upats)
|
|
|
|
(push (cons (make-symbol "_") (cdr binding)) bindings))
|
|
|
|
((pcase--trivial-upat-p (car binding)) (push binding bindings))
|
|
|
|
(t
|
|
|
|
(let ((tmpvar (make-symbol (format "x%d" (length bindings)))))
|
|
|
|
(push (cons tmpvar (cdr binding)) bindings)
|
|
|
|
(push (list (car binding) tmpvar) matches)))))
|
|
|
|
`(let ,(nreverse bindings) (pcase-let* ,matches ,@body)))))
|
|
|
|
|
|
|
|
(defmacro pcase-dolist (spec &rest body)
|
2012-06-11 00:46:21 +00:00
|
|
|
(declare (indent 1) (debug ((pcase-UPAT form) body)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(if (pcase--trivial-upat-p (car spec))
|
|
|
|
`(dolist ,spec ,@body)
|
|
|
|
(let ((tmpvar (make-symbol "x")))
|
|
|
|
`(dolist (,tmpvar ,@(cdr spec))
|
|
|
|
(pcase-let* ((,(car spec) ,tmpvar))
|
|
|
|
,@body)))))
|
|
|
|
|
|
|
|
|
|
|
|
(defun pcase--trivial-upat-p (upat)
|
|
|
|
(and (symbolp upat) (not (memq upat pcase--dontcare-upats))))
|
|
|
|
|
|
|
|
(defun pcase--expand (exp cases)
|
2011-03-06 04:48:17 +00:00
|
|
|
;; (message "pid=%S (pcase--expand %S ...hash=%S)"
|
|
|
|
;; (emacs-pid) exp (sxhash cases))
|
Provide generalized variables in core Elisp.
* lisp/emacs-lisp/gv.el: New file.
* lisp/subr.el (push, pop): Extend to generalized variables.
* lisp/loadup.el (macroexp): Unload if preloaded and uncompiled.
* lisp/emacs-lisp/cl-lib.el (cl-pop, cl-push, cl--set-nthcdr): Remove.
* lisp/emacs-lisp/cl-macs.el: Require gv. Use gv-define-setter,
gv-define-simple-setter, and gv-define-expander.
Remove setf-methods defined in gv. Rename cl-setf -> setf.
(cl-setf, cl-do-pop, cl-get-setf-method): Remove.
(cl-letf, cl-letf*, cl-define-modify-macro, cl-defsetf)
(cl-define-setf-expander, cl-struct-setf-expander): Move to cl.el.
(cl-remf, cl-shiftf, cl-rotatef, cl-callf, cl-callf2): Rewrite with
gv-letplace.
(cl-defstruct): Don't define setf-method any more.
* lisp/emacs-lisp/cl.el (flet): Don't autoload.
(cl--letf, letf, cl--letf*, letf*, cl--gv-adapt)
(define-setf-expander, defsetf, define-modify-macro)
(cl-struct-setf-expander): Move from cl-lib.el.
* lisp/emacs-lisp/syntax.el:
* lisp/emacs-lisp/ewoc.el:
* lisp/emacs-lisp/smie.el:
* lisp/emacs-lisp/cconv.el:
* lisp/emacs-lisp/timer.el: Rename cl-setf -> setf, cl-push -> push.
(timer--time): Use gv-define-simple-setter.
* lisp/emacs-lisp/macroexp.el (macroexp-let2): Rename from macroexp-let²
to avoid coding-system problems in subr.el. Adjust all users.
(macroexp--maxsize, macroexp-small-p): New functions.
* lisp/emacs-lisp/bytecomp.el (byte-compile-file): Don't use cl-letf.
* lisp/scroll-bar.el (scroll-bar-mode):
* lisp/simple.el (auto-fill-mode, overwrite-mode, binary-overwrite-mode)
(normal-erase-is-backspace-mode): Don't use the `eq' place.
* lisp/winner.el (winner-configuration, winner-make-point-alist)
(winner-set-conf, winner-get-point, winner-set): Don't abuse letf.
* lisp/files.el (locate-file-completion-table): Avoid list*.
Fixes: debbugs:11657
2012-06-22 13:42:38 +00:00
|
|
|
(macroexp-let2 macroexp-copyable-p val exp
|
2012-06-11 00:33:33 +00:00
|
|
|
(let* ((defs ())
|
|
|
|
(seen '())
|
|
|
|
(codegen
|
|
|
|
(lambda (code vars)
|
|
|
|
(let ((prev (assq code seen)))
|
|
|
|
(if (not prev)
|
|
|
|
(let ((res (pcase-codegen code vars)))
|
|
|
|
(push (list code vars res) seen)
|
|
|
|
res)
|
|
|
|
;; Since we use a tree-based pattern matching
|
|
|
|
;; technique, the leaves (the places that contain the
|
|
|
|
;; code to run once a pattern is matched) can get
|
|
|
|
;; copied a very large number of times, so to avoid
|
|
|
|
;; code explosion, we need to keep track of how many
|
|
|
|
;; times we've used each leaf and move it
|
|
|
|
;; to a separate function if that number is too high.
|
|
|
|
;;
|
|
|
|
;; We've already used this branch. So it is shared.
|
|
|
|
(let* ((code (car prev)) (cdrprev (cdr prev))
|
|
|
|
(prevvars (car cdrprev)) (cddrprev (cdr cdrprev))
|
|
|
|
(res (car cddrprev)))
|
|
|
|
(unless (symbolp res)
|
|
|
|
;; This is the first repeat, so we have to move
|
|
|
|
;; the branch to a separate function.
|
|
|
|
(let ((bsym
|
|
|
|
(make-symbol (format "pcase-%d" (length defs)))))
|
2012-06-18 19:23:35 +00:00
|
|
|
(push `(,bsym (lambda ,(mapcar #'car prevvars) ,@code))
|
|
|
|
defs)
|
2012-06-11 00:33:33 +00:00
|
|
|
(setcar res 'funcall)
|
|
|
|
(setcdr res (cons bsym (mapcar #'cdr prevvars)))
|
|
|
|
(setcar (cddr prev) bsym)
|
|
|
|
(setq res bsym)))
|
|
|
|
(setq vars (copy-sequence vars))
|
|
|
|
(let ((args (mapcar (lambda (pa)
|
|
|
|
(let ((v (assq (car pa) vars)))
|
|
|
|
(setq vars (delq v vars))
|
|
|
|
(cdr v)))
|
|
|
|
prevvars)))
|
|
|
|
;; If some of `vars' were not found in `prevvars', that's
|
|
|
|
;; OK it just means those vars aren't present in all
|
|
|
|
;; branches, so they can be used within the pattern
|
|
|
|
;; (e.g. by a `guard/let/pred') but not in the branch.
|
|
|
|
;; FIXME: But if some of `prevvars' are not in `vars' we
|
|
|
|
;; should remove them from `prevvars'!
|
|
|
|
`(funcall ,res ,@args)))))))
|
2012-06-18 19:23:35 +00:00
|
|
|
(used-cases ())
|
2012-06-11 00:33:33 +00:00
|
|
|
(main
|
|
|
|
(pcase--u
|
|
|
|
(mapcar (lambda (case)
|
|
|
|
`((match ,val . ,(car case))
|
2012-06-18 19:23:35 +00:00
|
|
|
,(lambda (vars)
|
|
|
|
(unless (memq case used-cases)
|
|
|
|
;; Keep track of the cases that are used.
|
|
|
|
(push case used-cases))
|
|
|
|
(funcall
|
|
|
|
(if (pcase--small-branch-p (cdr case))
|
|
|
|
;; Don't bother sharing multiple
|
|
|
|
;; occurrences of this leaf since it's small.
|
|
|
|
#'pcase-codegen codegen)
|
|
|
|
(cdr case)
|
|
|
|
vars))))
|
2012-06-11 00:33:33 +00:00
|
|
|
cases))))
|
2012-06-18 19:23:35 +00:00
|
|
|
(dolist (case cases)
|
More CL cleanups and reduction of use of cl.el.
* woman.el, winner.el, vc/vc-rcs.el, vc/vc-hooks.el, vc/vc-hg.el:
* vc/vc-git.el, vc/vc-dir.el, vc/vc-bzr.el, vc/vc-annotate.el:
* textmodes/tex-mode.el, textmodes/sgml-mode.el, tar-mode.el:
* strokes.el, ses.el, server.el, progmodes/js.el, progmodes/gdb-mi.el:
* progmodes/flymake.el, progmodes/ebrowse.el, progmodes/compile.el:
* play/tetris.el, play/snake.el, play/pong.el, play/landmark.el:
* play/hanoi.el, play/decipher.el, play/5x5.el, nxml/nxml-mode.el:
* net/secrets.el, net/quickurl.el, midnight.el, mail/footnote.el:
* image-dired.el, ibuffer.el, ibuf-macs.el, ibuf-ext.el, hexl.el:
* eshell/eshell.el, eshell/esh-io.el, eshell/esh-ext.el:
* eshell/esh-cmd.el, eshell/em-ls.el, eshell/em-hist.el:
* eshell/em-cmpl.el, eshell/em-banner.el:
* url/url.el, url/url-queue.el, url/url-parse.el, url/url-http.el:
* url/url-future.el, url/url-dav.el, url/url-cookie.el:
* calendar/parse-time.el, test/eshell.el: Use cl-lib.
* wid-browse.el, wdired.el, vc/vc.el, vc/vc-mtn.el, vc/vc-cvs.el:
* vc/vc-arch.el, tree-widget.el, textmodes/texinfo.el:
* textmodes/refill.el, textmodes/css-mode.el, term/tvi970.el:
* term/ns-win.el, term.el, shell.el, ps-samp.el:
* progmodes/perl-mode.el, progmodes/pascal.el, progmodes/gud.el:
* progmodes/glasses.el, progmodes/etags.el, progmodes/cwarn.el:
* play/gamegrid.el, play/bubbles.el, novice.el, notifications.el:
* net/zeroconf.el, net/xesam.el, net/snmp-mode.el, net/mairix.el:
* net/ldap.el, net/eudc.el, net/browse-url.el, man.el:
* mail/mailheader.el, mail/feedmail.el:
* url/url-util.el, url/url-privacy.el, url/url-nfs.el, url/url-misc.el:
* url/url-methods.el, url/url-gw.el, url/url-file.el, url/url-expand.el:
Dont use CL.
* ibuf-ext.el (ibuffer-mark-old-buffers): Use float-time.
* eshell/esh-opt.el (eshell-eval-using-options): Quote code with
`lambda' rather than with `quote'.
(eshell-do-opt): Adjust accordingly.
(eshell-process-option): Simplify.
* eshell/esh-var.el:
* eshell/em-script.el: Require `esh-opt' for eshell-eval-using-options.
* emacs-pcase.el (pcase--dontcare-upats, pcase--let*)
(pcase--expand, pcase--u1): Rename pcase's internal `dontcare' pattern
to `pcase--dontcare'.
* emacs-cl.el (labels): Mark obsolete.
(cl--letf, letf): Move to cl-lib.
(cl--letf*, letf*): Remove.
* emacs-cl-lib.el (cl-nth-value): Use defalias.
* emacs-cl-macs.el (cl-dolist, cl-dotimes): Add indent rule.
(cl-progv): Rewrite.
(cl--letf, cl-letf): Move from cl.el.
(cl-letf*): New macro.
* emacs-cl-extra.el (cl--progv-before, cl--progv-after): Remove.
2012-07-11 23:13:41 +00:00
|
|
|
(unless (or (memq case used-cases) (eq (car case) 'pcase--dontcare))
|
2012-06-18 19:23:35 +00:00
|
|
|
(message "Redundant pcase pattern: %S" (car case))))
|
2012-06-07 19:25:48 +00:00
|
|
|
(macroexp-let* defs main))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
(defun pcase-codegen (code vars)
|
2012-06-07 19:25:48 +00:00
|
|
|
;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding
|
2012-05-29 14:28:02 +00:00
|
|
|
;; let* which might prevent the setcar/setcdr in pcase--expand's fancy
|
|
|
|
;; codegen from later metamorphosing this let into a funcall.
|
|
|
|
`(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars)
|
2010-08-10 13:18:14 +00:00
|
|
|
,@code))
|
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--small-branch-p (code)
|
2010-08-10 13:18:14 +00:00
|
|
|
(and (= 1 (length code))
|
|
|
|
(or (not (consp (car code)))
|
|
|
|
(let ((small t))
|
|
|
|
(dolist (e (car code))
|
|
|
|
(if (consp e) (setq small nil)))
|
|
|
|
small))))
|
|
|
|
|
|
|
|
;; Try to use `cond' rather than a sequence of `if's, so as to reduce
|
|
|
|
;; the depth of the generated tree.
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--if (test then else)
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
2010-11-24 16:39:51 +00:00
|
|
|
((eq else :pcase--dontcare) then)
|
2011-02-27 02:50:38 +00:00
|
|
|
((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen?
|
2012-06-07 19:25:48 +00:00
|
|
|
(t (macroexp-if test then else))))
|
2012-05-05 02:05:49 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--upat (qpattern)
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
|
|
|
((eq (car-safe qpattern) '\,) (cadr qpattern))
|
|
|
|
(t (list '\` qpattern))))
|
|
|
|
|
|
|
|
;; Note about MATCH:
|
|
|
|
;; When we have patterns like `(PAT1 . PAT2), after performing the `consp'
|
|
|
|
;; check, we want to turn all the similar patterns into ones of the form
|
|
|
|
;; (and (match car PAT1) (match cdr PAT2)), so you naturally need conjunction.
|
|
|
|
;; Earlier code hence used branches of the form (MATCHES . CODE) where
|
|
|
|
;; MATCHES was a list (implicitly a conjunction) of (SYM . PAT).
|
|
|
|
;; But if we have a pattern of the form (or `(PAT1 . PAT2) PAT3), there is
|
|
|
|
;; no easy way to eliminate the `consp' check in such a representation.
|
|
|
|
;; So we replaced the MATCHES by the MATCH below which can be made up
|
|
|
|
;; of conjunctions and disjunctions, so if we know `foo' is a cons, we can
|
|
|
|
;; turn (match foo . (or `(PAT1 . PAT2) PAT3)) into
|
|
|
|
;; (or (and (match car . `PAT1) (match cdr . `PAT2)) (match foo . PAT3)).
|
|
|
|
;; The downside is that we now have `or' and `and' both in MATCH and
|
|
|
|
;; in PAT, so there are different equivalent representations and we
|
|
|
|
;; need to handle them all. We do not try to systematically
|
|
|
|
;; canonicalize them to one form over another, but we do occasionally
|
|
|
|
;; turn one into the other.
|
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--u (branches)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Expand matcher for rules BRANCHES.
|
|
|
|
Each BRANCH has the form (MATCH CODE . VARS) where
|
|
|
|
CODE is the code generator for that branch.
|
|
|
|
VARS is the set of vars already bound by earlier matches.
|
|
|
|
MATCH is the pattern that needs to be matched, of the form:
|
|
|
|
(match VAR . UPAT)
|
|
|
|
(and MATCH ...)
|
|
|
|
(or MATCH ...)"
|
|
|
|
(when (setq branches (delq nil branches))
|
2011-02-18 13:55:51 +00:00
|
|
|
(let* ((carbranch (car branches))
|
|
|
|
(match (car carbranch)) (cdarbranch (cdr carbranch))
|
|
|
|
(code (car cdarbranch))
|
|
|
|
(vars (cdr cdarbranch)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (list match) code vars (cdr branches)))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--and (match matches)
|
2010-08-10 13:18:14 +00:00
|
|
|
(if matches `(and ,match ,@matches) match))
|
|
|
|
|
2011-02-27 02:50:38 +00:00
|
|
|
(defconst pcase-mutually-exclusive-predicates
|
|
|
|
'((symbolp . integerp)
|
|
|
|
(symbolp . numberp)
|
|
|
|
(symbolp . consp)
|
|
|
|
(symbolp . arrayp)
|
|
|
|
(symbolp . stringp)
|
2011-03-16 20:08:39 +00:00
|
|
|
(symbolp . byte-code-function-p)
|
2011-02-27 02:50:38 +00:00
|
|
|
(integerp . consp)
|
|
|
|
(integerp . arrayp)
|
|
|
|
(integerp . stringp)
|
2011-03-16 20:08:39 +00:00
|
|
|
(integerp . byte-code-function-p)
|
2011-02-27 02:50:38 +00:00
|
|
|
(numberp . consp)
|
|
|
|
(numberp . arrayp)
|
|
|
|
(numberp . stringp)
|
2011-03-16 20:08:39 +00:00
|
|
|
(numberp . byte-code-function-p)
|
2011-02-27 02:50:38 +00:00
|
|
|
(consp . arrayp)
|
|
|
|
(consp . stringp)
|
2011-03-16 20:08:39 +00:00
|
|
|
(consp . byte-code-function-p)
|
|
|
|
(arrayp . stringp)
|
|
|
|
(arrayp . byte-code-function-p)
|
|
|
|
(stringp . byte-code-function-p)))
|
2011-02-27 02:50:38 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-match (sym splitter match)
|
2011-02-18 13:55:51 +00:00
|
|
|
(cond
|
|
|
|
((eq (car match) 'match)
|
2010-08-10 13:18:14 +00:00
|
|
|
(if (not (eq sym (cadr match)))
|
|
|
|
(cons match match)
|
|
|
|
(let ((pat (cddr match)))
|
|
|
|
(cond
|
|
|
|
;; Hoist `or' and `and' patterns to `or' and `and' matches.
|
|
|
|
((memq (car-safe pat) '(or and))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--split-match sym splitter
|
|
|
|
(cons (car pat)
|
|
|
|
(mapcar (lambda (alt)
|
|
|
|
`(match ,sym . ,alt))
|
|
|
|
(cdr pat)))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(t (let ((res (funcall splitter (cddr match))))
|
|
|
|
(cons (or (car res) match) (or (cdr res) match))))))))
|
2011-02-18 13:55:51 +00:00
|
|
|
((memq (car match) '(or and))
|
2010-08-10 13:18:14 +00:00
|
|
|
(let ((then-alts '())
|
|
|
|
(else-alts '())
|
2010-11-24 16:39:51 +00:00
|
|
|
(neutral-elem (if (eq 'or (car match))
|
|
|
|
:pcase--fail :pcase--succeed))
|
|
|
|
(zero-elem (if (eq 'or (car match)) :pcase--succeed :pcase--fail)))
|
2010-08-10 13:18:14 +00:00
|
|
|
(dolist (alt (cdr match))
|
2010-11-24 16:39:51 +00:00
|
|
|
(let ((split (pcase--split-match sym splitter alt)))
|
2010-08-10 13:18:14 +00:00
|
|
|
(unless (eq (car split) neutral-elem)
|
|
|
|
(push (car split) then-alts))
|
|
|
|
(unless (eq (cdr split) neutral-elem)
|
|
|
|
(push (cdr split) else-alts))))
|
|
|
|
(cons (cond ((memq zero-elem then-alts) zero-elem)
|
|
|
|
((null then-alts) neutral-elem)
|
|
|
|
((null (cdr then-alts)) (car then-alts))
|
|
|
|
(t (cons (car match) (nreverse then-alts))))
|
|
|
|
(cond ((memq zero-elem else-alts) zero-elem)
|
|
|
|
((null else-alts) neutral-elem)
|
|
|
|
((null (cdr else-alts)) (car else-alts))
|
|
|
|
(t (cons (car match) (nreverse else-alts)))))))
|
|
|
|
(t (error "Uknown MATCH %s" match))))
|
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-rest (sym splitter rest)
|
2010-08-10 13:18:14 +00:00
|
|
|
(let ((then-rest '())
|
|
|
|
(else-rest '()))
|
|
|
|
(dolist (branch rest)
|
|
|
|
(let* ((match (car branch))
|
|
|
|
(code&vars (cdr branch))
|
2011-12-15 07:24:10 +00:00
|
|
|
(split
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--split-match sym splitter match)))
|
2011-12-15 07:24:10 +00:00
|
|
|
(unless (eq (car split) :pcase--fail)
|
|
|
|
(push (cons (car split) code&vars) then-rest))
|
|
|
|
(unless (eq (cdr split) :pcase--fail)
|
|
|
|
(push (cons (cdr split) code&vars) else-rest))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(cons (nreverse then-rest) (nreverse else-rest))))
|
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-consp (syma symd pat)
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
|
|
|
;; A QPattern for a cons, can only go the `then' side.
|
|
|
|
((and (eq (car-safe pat) '\`) (consp (cadr pat)))
|
|
|
|
(let ((qpat (cadr pat)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(cons `(and (match ,syma . ,(pcase--upat (car qpat)))
|
|
|
|
(match ,symd . ,(pcase--upat (cdr qpat))))
|
|
|
|
:pcase--fail)))
|
2011-02-27 02:50:38 +00:00
|
|
|
;; A QPattern but not for a cons, can only go to the `else' side.
|
|
|
|
((eq (car-safe pat) '\`) (cons :pcase--fail nil))
|
|
|
|
((and (eq (car-safe pat) 'pred)
|
|
|
|
(or (member (cons 'consp (cadr pat))
|
|
|
|
pcase-mutually-exclusive-predicates)
|
|
|
|
(member (cons (cadr pat) 'consp)
|
|
|
|
pcase-mutually-exclusive-predicates)))
|
|
|
|
(cons :pcase--fail nil))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-equal (elem pat)
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
|
|
|
;; The same match will give the same result.
|
|
|
|
((and (eq (car-safe pat) '\`) (equal (cadr pat) elem))
|
2010-11-24 16:39:51 +00:00
|
|
|
(cons :pcase--succeed :pcase--fail))
|
2010-08-10 13:18:14 +00:00
|
|
|
;; A different match will fail if this one succeeds.
|
|
|
|
((and (eq (car-safe pat) '\`)
|
|
|
|
;; (or (integerp (cadr pat)) (symbolp (cadr pat))
|
|
|
|
;; (consp (cadr pat)))
|
|
|
|
)
|
2011-02-27 02:50:38 +00:00
|
|
|
(cons :pcase--fail nil))
|
|
|
|
((and (eq (car-safe pat) 'pred)
|
|
|
|
(symbolp (cadr pat))
|
|
|
|
(get (cadr pat) 'side-effect-free)
|
|
|
|
(funcall (cadr pat) elem))
|
|
|
|
(cons :pcase--succeed nil))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-member (elems pat)
|
|
|
|
;; Based on pcase--split-equal.
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
2010-10-29 01:05:38 +00:00
|
|
|
;; The same match (or a match of membership in a superset) will
|
|
|
|
;; give the same result, but we don't know how to check it.
|
2010-09-01 10:03:08 +00:00
|
|
|
;; (???
|
2010-11-24 16:39:51 +00:00
|
|
|
;; (cons :pcase--succeed nil))
|
2010-09-01 10:03:08 +00:00
|
|
|
;; A match for one of the elements may succeed or fail.
|
2010-08-10 13:18:14 +00:00
|
|
|
((and (eq (car-safe pat) '\`) (member (cadr pat) elems))
|
2010-09-01 10:03:08 +00:00
|
|
|
nil)
|
2010-08-10 13:18:14 +00:00
|
|
|
;; A different match will fail if this one succeeds.
|
|
|
|
((and (eq (car-safe pat) '\`)
|
|
|
|
;; (or (integerp (cadr pat)) (symbolp (cadr pat))
|
|
|
|
;; (consp (cadr pat)))
|
|
|
|
)
|
2011-02-27 02:50:38 +00:00
|
|
|
(cons :pcase--fail nil))
|
|
|
|
((and (eq (car-safe pat) 'pred)
|
|
|
|
(symbolp (cadr pat))
|
|
|
|
(get (cadr pat) 'side-effect-free)
|
|
|
|
(let ((p (cadr pat)) (all t))
|
|
|
|
(dolist (elem elems)
|
|
|
|
(unless (funcall p elem) (setq all nil)))
|
|
|
|
all))
|
|
|
|
(cons :pcase--succeed nil))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--split-pred (upat pat)
|
2010-08-10 13:18:14 +00:00
|
|
|
;; FIXME: For predicates like (pred (> a)), two such predicates may
|
|
|
|
;; actually refer to different variables `a'.
|
2012-05-05 02:05:49 +00:00
|
|
|
(let (test)
|
|
|
|
(cond
|
|
|
|
((equal upat pat) (cons :pcase--succeed :pcase--fail))
|
|
|
|
((and (eq 'pred (car upat))
|
|
|
|
(eq 'pred (car-safe pat))
|
|
|
|
(or (member (cons (cadr upat) (cadr pat))
|
|
|
|
pcase-mutually-exclusive-predicates)
|
|
|
|
(member (cons (cadr pat) (cadr upat))
|
|
|
|
pcase-mutually-exclusive-predicates)))
|
|
|
|
(cons :pcase--fail nil))
|
|
|
|
((and (eq 'pred (car upat))
|
|
|
|
(eq '\` (car-safe pat))
|
|
|
|
(symbolp (cadr upat))
|
|
|
|
(or (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat)))
|
|
|
|
(get (cadr upat) 'side-effect-free)
|
|
|
|
(ignore-errors
|
|
|
|
(setq test (list (funcall (cadr upat) (cadr pat))))))
|
|
|
|
(if (car test)
|
|
|
|
(cons nil :pcase--fail)
|
|
|
|
(cons :pcase--fail nil))))))
|
2010-08-10 13:18:14 +00:00
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--fgrep (vars sexp)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Check which of the symbols VARS appear in SEXP."
|
|
|
|
(let ((res '()))
|
|
|
|
(while (consp sexp)
|
2010-11-24 16:39:51 +00:00
|
|
|
(dolist (var (pcase--fgrep vars (pop sexp)))
|
2010-08-10 13:18:14 +00:00
|
|
|
(unless (memq var res) (push var res))))
|
|
|
|
(and (memq sexp vars) (not (memq sexp res)) (push sexp res))
|
|
|
|
res))
|
|
|
|
|
2012-07-10 09:26:04 +00:00
|
|
|
(defun pcase--self-quoting-p (upat)
|
|
|
|
(or (keywordp upat) (numberp upat) (stringp upat)))
|
|
|
|
|
2012-09-28 12:18:38 +00:00
|
|
|
(defsubst pcase--mark-used (sym)
|
|
|
|
;; Exceptionally, `sym' may be a constant expression rather than a symbol.
|
|
|
|
(if (symbolp sym) (put sym 'pcase-used t)))
|
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
;; It's very tempting to use `pcase' below, tho obviously, it'd create
|
|
|
|
;; bootstrapping problems.
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--u1 (matches code vars rest)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Return code that runs CODE (with VARS) if MATCHES match.
|
2011-02-28 04:24:40 +00:00
|
|
|
Otherwise, it defers to REST which is a list of branches of the form
|
2010-08-10 13:18:14 +00:00
|
|
|
\(ELSE-MATCH ELSE-CODE . ELSE-VARS)."
|
|
|
|
;; Depending on the order in which we choose to check each of the MATCHES,
|
|
|
|
;; the resulting tree may be smaller or bigger. So in general, we'd want
|
|
|
|
;; to be careful to chose the "optimal" order. But predicate
|
|
|
|
;; patterns make this harder because they create dependencies
|
|
|
|
;; between matches. So we don't bother trying to reorder anything.
|
|
|
|
(cond
|
|
|
|
((null matches) (funcall code vars))
|
2010-11-24 16:39:51 +00:00
|
|
|
((eq :pcase--fail (car matches)) (pcase--u rest))
|
|
|
|
((eq :pcase--succeed (car matches))
|
|
|
|
(pcase--u1 (cdr matches) code vars rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq 'and (caar matches))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (append (cdar matches) (cdr matches)) code vars rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq 'or (caar matches))
|
|
|
|
(let* ((alts (cdar matches))
|
|
|
|
(var (if (eq (caar alts) 'match) (cadr (car alts))))
|
|
|
|
(simples '()) (others '()))
|
|
|
|
(when var
|
|
|
|
(dolist (alt alts)
|
|
|
|
(if (and (eq (car alt) 'match) (eq var (cadr alt))
|
|
|
|
(let ((upat (cddr alt)))
|
|
|
|
(and (eq (car-safe upat) '\`)
|
2010-10-29 01:05:38 +00:00
|
|
|
(or (integerp (cadr upat)) (symbolp (cadr upat))
|
|
|
|
(stringp (cadr upat))))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(push (cddr alt) simples)
|
|
|
|
(push alt others))))
|
|
|
|
(cond
|
2010-11-24 16:39:51 +00:00
|
|
|
((null alts) (error "Please avoid it") (pcase--u rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((> (length simples) 1)
|
|
|
|
;; De-hoist the `or' MATCH into an `or' pattern that will be
|
|
|
|
;; turned into a `memq' below.
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (cons `(match ,var or . ,(nreverse simples)) (cdr matches))
|
|
|
|
code vars
|
|
|
|
(if (null others) rest
|
2011-02-18 13:55:51 +00:00
|
|
|
(cons (cons
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--and (if (cdr others)
|
|
|
|
(cons 'or (nreverse others))
|
|
|
|
(car others))
|
|
|
|
(cdr matches))
|
2011-02-18 13:55:51 +00:00
|
|
|
(cons code vars))
|
2010-11-24 16:39:51 +00:00
|
|
|
rest))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(t
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (cons (pop alts) (cdr matches)) code vars
|
|
|
|
(if (null alts) (progn (error "Please avoid it") rest)
|
2011-02-18 13:55:51 +00:00
|
|
|
(cons (cons
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--and (if (cdr alts)
|
|
|
|
(cons 'or alts) (car alts))
|
|
|
|
(cdr matches))
|
2011-02-18 13:55:51 +00:00
|
|
|
(cons code vars))
|
2010-11-24 16:39:51 +00:00
|
|
|
rest)))))))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq 'match (caar matches))
|
2011-02-18 13:55:51 +00:00
|
|
|
(let* ((popmatches (pop matches))
|
2011-03-01 05:03:24 +00:00
|
|
|
(_op (car popmatches)) (cdrpopmatches (cdr popmatches))
|
2011-02-18 13:55:51 +00:00
|
|
|
(sym (car cdrpopmatches))
|
|
|
|
(upat (cdr cdrpopmatches)))
|
2010-08-10 13:18:14 +00:00
|
|
|
(cond
|
2010-11-24 16:39:51 +00:00
|
|
|
((memq upat '(t _)) (pcase--u1 matches code vars rest))
|
More CL cleanups and reduction of use of cl.el.
* woman.el, winner.el, vc/vc-rcs.el, vc/vc-hooks.el, vc/vc-hg.el:
* vc/vc-git.el, vc/vc-dir.el, vc/vc-bzr.el, vc/vc-annotate.el:
* textmodes/tex-mode.el, textmodes/sgml-mode.el, tar-mode.el:
* strokes.el, ses.el, server.el, progmodes/js.el, progmodes/gdb-mi.el:
* progmodes/flymake.el, progmodes/ebrowse.el, progmodes/compile.el:
* play/tetris.el, play/snake.el, play/pong.el, play/landmark.el:
* play/hanoi.el, play/decipher.el, play/5x5.el, nxml/nxml-mode.el:
* net/secrets.el, net/quickurl.el, midnight.el, mail/footnote.el:
* image-dired.el, ibuffer.el, ibuf-macs.el, ibuf-ext.el, hexl.el:
* eshell/eshell.el, eshell/esh-io.el, eshell/esh-ext.el:
* eshell/esh-cmd.el, eshell/em-ls.el, eshell/em-hist.el:
* eshell/em-cmpl.el, eshell/em-banner.el:
* url/url.el, url/url-queue.el, url/url-parse.el, url/url-http.el:
* url/url-future.el, url/url-dav.el, url/url-cookie.el:
* calendar/parse-time.el, test/eshell.el: Use cl-lib.
* wid-browse.el, wdired.el, vc/vc.el, vc/vc-mtn.el, vc/vc-cvs.el:
* vc/vc-arch.el, tree-widget.el, textmodes/texinfo.el:
* textmodes/refill.el, textmodes/css-mode.el, term/tvi970.el:
* term/ns-win.el, term.el, shell.el, ps-samp.el:
* progmodes/perl-mode.el, progmodes/pascal.el, progmodes/gud.el:
* progmodes/glasses.el, progmodes/etags.el, progmodes/cwarn.el:
* play/gamegrid.el, play/bubbles.el, novice.el, notifications.el:
* net/zeroconf.el, net/xesam.el, net/snmp-mode.el, net/mairix.el:
* net/ldap.el, net/eudc.el, net/browse-url.el, man.el:
* mail/mailheader.el, mail/feedmail.el:
* url/url-util.el, url/url-privacy.el, url/url-nfs.el, url/url-misc.el:
* url/url-methods.el, url/url-gw.el, url/url-file.el, url/url-expand.el:
Dont use CL.
* ibuf-ext.el (ibuffer-mark-old-buffers): Use float-time.
* eshell/esh-opt.el (eshell-eval-using-options): Quote code with
`lambda' rather than with `quote'.
(eshell-do-opt): Adjust accordingly.
(eshell-process-option): Simplify.
* eshell/esh-var.el:
* eshell/em-script.el: Require `esh-opt' for eshell-eval-using-options.
* emacs-pcase.el (pcase--dontcare-upats, pcase--let*)
(pcase--expand, pcase--u1): Rename pcase's internal `dontcare' pattern
to `pcase--dontcare'.
* emacs-cl.el (labels): Mark obsolete.
(cl--letf, letf): Move to cl-lib.
(cl--letf*, letf*): Remove.
* emacs-cl-lib.el (cl-nth-value): Use defalias.
* emacs-cl-macs.el (cl-dolist, cl-dotimes): Add indent rule.
(cl-progv): Rewrite.
(cl--letf, cl-letf): Move from cl.el.
(cl-letf*): New macro.
* emacs-cl-extra.el (cl--progv-before, cl--progv-after): Remove.
2012-07-11 23:13:41 +00:00
|
|
|
((eq upat 'pcase--dontcare) :pcase--dontcare)
|
2010-10-29 01:05:38 +00:00
|
|
|
((memq (car-safe upat) '(guard pred))
|
2012-09-28 12:18:38 +00:00
|
|
|
(if (eq (car upat) 'pred) (pcase--mark-used sym))
|
2011-02-18 13:55:51 +00:00
|
|
|
(let* ((splitrest
|
2011-03-16 20:08:39 +00:00
|
|
|
(pcase--split-rest
|
2012-06-18 19:23:35 +00:00
|
|
|
sym (lambda (pat) (pcase--split-pred upat pat)) rest))
|
2011-02-18 13:55:51 +00:00
|
|
|
(then-rest (car splitrest))
|
|
|
|
(else-rest (cdr splitrest)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--if (if (and (eq (car upat) 'pred) (symbolp (cadr upat)))
|
|
|
|
`(,(cadr upat) ,sym)
|
|
|
|
(let* ((exp (cadr upat))
|
|
|
|
;; `vs' is an upper bound on the vars we need.
|
|
|
|
(vs (pcase--fgrep (mapcar #'car vars) exp))
|
2011-03-16 20:08:39 +00:00
|
|
|
(env (mapcar (lambda (var)
|
|
|
|
(list var (cdr (assq var vars))))
|
|
|
|
vs))
|
|
|
|
(call (if (eq 'guard (car upat))
|
|
|
|
exp
|
|
|
|
(when (memq sym vs)
|
|
|
|
;; `sym' is shadowed by `env'.
|
|
|
|
(let ((newsym (make-symbol "x")))
|
|
|
|
(push (list newsym sym) env)
|
|
|
|
(setq sym newsym)))
|
2012-05-17 21:40:47 +00:00
|
|
|
(if (functionp exp)
|
|
|
|
`(funcall #',exp ,sym)
|
2011-03-16 20:08:39 +00:00
|
|
|
`(,@exp ,sym)))))
|
2010-11-24 16:39:51 +00:00
|
|
|
(if (null vs)
|
|
|
|
call
|
|
|
|
;; Let's not replace `vars' in `exp' since it's
|
|
|
|
;; too difficult to do it right, instead just
|
|
|
|
;; let-bind `vars' around `exp'.
|
2011-03-16 20:08:39 +00:00
|
|
|
`(let* ,env ,call))))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 matches code vars then-rest)
|
|
|
|
(pcase--u else-rest))))
|
2012-07-10 09:26:04 +00:00
|
|
|
((pcase--self-quoting-p upat)
|
2012-09-28 12:18:38 +00:00
|
|
|
(pcase--mark-used sym)
|
2012-07-10 09:26:04 +00:00
|
|
|
(pcase--q1 sym upat matches code vars rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((symbolp upat)
|
2012-09-28 12:18:38 +00:00
|
|
|
(pcase--mark-used sym)
|
2011-02-18 04:58:21 +00:00
|
|
|
(if (not (assq upat vars))
|
|
|
|
(pcase--u1 matches code (cons (cons upat sym) vars) rest)
|
|
|
|
;; Non-linear pattern. Turn it into an `eq' test.
|
|
|
|
(pcase--u1 (cons `(match ,sym . (pred (eq ,(cdr (assq upat vars)))))
|
|
|
|
matches)
|
|
|
|
code vars rest)))
|
2011-03-16 20:08:39 +00:00
|
|
|
((eq (car-safe upat) 'let)
|
|
|
|
;; A upat of the form (let VAR EXP).
|
|
|
|
;; (pcase--u1 matches code
|
|
|
|
;; (cons (cons (nth 1 upat) (nth 2 upat)) vars) rest)
|
Provide generalized variables in core Elisp.
* lisp/emacs-lisp/gv.el: New file.
* lisp/subr.el (push, pop): Extend to generalized variables.
* lisp/loadup.el (macroexp): Unload if preloaded and uncompiled.
* lisp/emacs-lisp/cl-lib.el (cl-pop, cl-push, cl--set-nthcdr): Remove.
* lisp/emacs-lisp/cl-macs.el: Require gv. Use gv-define-setter,
gv-define-simple-setter, and gv-define-expander.
Remove setf-methods defined in gv. Rename cl-setf -> setf.
(cl-setf, cl-do-pop, cl-get-setf-method): Remove.
(cl-letf, cl-letf*, cl-define-modify-macro, cl-defsetf)
(cl-define-setf-expander, cl-struct-setf-expander): Move to cl.el.
(cl-remf, cl-shiftf, cl-rotatef, cl-callf, cl-callf2): Rewrite with
gv-letplace.
(cl-defstruct): Don't define setf-method any more.
* lisp/emacs-lisp/cl.el (flet): Don't autoload.
(cl--letf, letf, cl--letf*, letf*, cl--gv-adapt)
(define-setf-expander, defsetf, define-modify-macro)
(cl-struct-setf-expander): Move from cl-lib.el.
* lisp/emacs-lisp/syntax.el:
* lisp/emacs-lisp/ewoc.el:
* lisp/emacs-lisp/smie.el:
* lisp/emacs-lisp/cconv.el:
* lisp/emacs-lisp/timer.el: Rename cl-setf -> setf, cl-push -> push.
(timer--time): Use gv-define-simple-setter.
* lisp/emacs-lisp/macroexp.el (macroexp-let2): Rename from macroexp-let²
to avoid coding-system problems in subr.el. Adjust all users.
(macroexp--maxsize, macroexp-small-p): New functions.
* lisp/emacs-lisp/bytecomp.el (byte-compile-file): Don't use cl-letf.
* lisp/scroll-bar.el (scroll-bar-mode):
* lisp/simple.el (auto-fill-mode, overwrite-mode, binary-overwrite-mode)
(normal-erase-is-backspace-mode): Don't use the `eq' place.
* lisp/winner.el (winner-configuration, winner-make-point-alist)
(winner-set-conf, winner-get-point, winner-set): Don't abuse letf.
* lisp/files.el (locate-file-completion-table): Avoid list*.
Fixes: debbugs:11657
2012-06-22 13:42:38 +00:00
|
|
|
(macroexp-let2
|
2012-06-07 19:25:48 +00:00
|
|
|
macroexp-copyable-p sym
|
|
|
|
(let* ((exp (nth 2 upat))
|
|
|
|
(found (assq exp vars)))
|
|
|
|
(if found (cdr found)
|
|
|
|
(let* ((vs (pcase--fgrep (mapcar #'car vars) exp))
|
|
|
|
(env (mapcar (lambda (v) (list v (cdr (assq v vars))))
|
|
|
|
vs)))
|
|
|
|
(if env (macroexp-let* env exp) exp))))
|
|
|
|
(pcase--u1 (cons `(match ,sym . ,(nth 1 upat)) matches)
|
|
|
|
code vars rest)))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq (car-safe upat) '\`)
|
2012-09-28 12:18:38 +00:00
|
|
|
(pcase--mark-used sym)
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--q1 sym (cadr upat) matches code vars rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq (car-safe upat) 'or)
|
2010-10-29 01:05:38 +00:00
|
|
|
(let ((all (> (length (cdr upat)) 1))
|
|
|
|
(memq-fine t))
|
2010-08-10 13:18:14 +00:00
|
|
|
(when all
|
|
|
|
(dolist (alt (cdr upat))
|
2012-07-10 09:26:04 +00:00
|
|
|
(unless (or (pcase--self-quoting-p alt)
|
|
|
|
(and (eq (car-safe alt) '\`)
|
|
|
|
(or (symbolp (cadr alt)) (integerp (cadr alt))
|
|
|
|
(setq memq-fine nil)
|
|
|
|
(stringp (cadr alt)))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(setq all nil))))
|
|
|
|
(if all
|
|
|
|
;; Use memq for (or `a `b `c `d) rather than a big tree.
|
2012-07-10 09:26:04 +00:00
|
|
|
(let* ((elems (mapcar (lambda (x) (if (consp x) (cadr x) x))
|
|
|
|
(cdr upat)))
|
2011-02-18 13:55:51 +00:00
|
|
|
(splitrest
|
|
|
|
(pcase--split-rest
|
2012-06-18 19:23:35 +00:00
|
|
|
sym (lambda (pat) (pcase--split-member elems pat)) rest))
|
2011-02-18 13:55:51 +00:00
|
|
|
(then-rest (car splitrest))
|
|
|
|
(else-rest (cdr splitrest)))
|
2012-09-28 12:18:38 +00:00
|
|
|
(pcase--mark-used sym)
|
2011-02-18 13:55:51 +00:00
|
|
|
(pcase--if `(,(if memq-fine #'memq #'member) ,sym ',elems)
|
|
|
|
(pcase--u1 matches code vars then-rest)
|
|
|
|
(pcase--u else-rest)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (cons `(match ,sym ,@(cadr upat)) matches) code vars
|
|
|
|
(append (mapcar (lambda (upat)
|
|
|
|
`((and (match ,sym . ,upat) ,@matches)
|
|
|
|
,code ,@vars))
|
|
|
|
(cddr upat))
|
|
|
|
rest)))))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq (car-safe upat) 'and)
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 (append (mapcar (lambda (upat) `(match ,sym ,@upat))
|
|
|
|
(cdr upat))
|
|
|
|
matches)
|
|
|
|
code vars rest))
|
2010-08-10 13:18:14 +00:00
|
|
|
((eq (car-safe upat) 'not)
|
|
|
|
;; FIXME: The implementation below is naive and results in
|
|
|
|
;; inefficient code.
|
2010-11-24 16:39:51 +00:00
|
|
|
;; To make it work right, we would need to turn pcase--u1's
|
2010-08-10 13:18:14 +00:00
|
|
|
;; `code' and `vars' into a single argument of the same form as
|
|
|
|
;; `rest'. We would also need to split this new `then-rest' argument
|
|
|
|
;; for every test (currently we don't bother to do it since
|
|
|
|
;; it's only useful for odd patterns like (and `(PAT1 . PAT2)
|
|
|
|
;; `(PAT3 . PAT4)) which the programmer can easily rewrite
|
|
|
|
;; to the more efficient `(,(and PAT1 PAT3) . ,(and PAT2 PAT4))).
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 `((match ,sym . ,(cadr upat)))
|
2011-02-10 18:53:49 +00:00
|
|
|
;; FIXME: This codegen is not careful to share its
|
|
|
|
;; code if used several times: code blow up is likely.
|
2011-03-01 05:03:24 +00:00
|
|
|
(lambda (_vars)
|
2011-02-10 18:53:49 +00:00
|
|
|
;; `vars' will likely contain bindings which are
|
|
|
|
;; not always available in other paths to
|
|
|
|
;; `rest', so there' no point trying to pass
|
|
|
|
;; them down.
|
|
|
|
(pcase--u rest))
|
2010-11-24 16:39:51 +00:00
|
|
|
vars
|
|
|
|
(list `((and . ,matches) ,code . ,vars))))
|
2010-08-10 13:18:14 +00:00
|
|
|
(t (error "Unknown upattern `%s'" upat)))))
|
|
|
|
(t (error "Incorrect MATCH %s" (car matches)))))
|
|
|
|
|
2010-11-24 16:39:51 +00:00
|
|
|
(defun pcase--q1 (sym qpat matches code vars rest)
|
2010-08-10 13:18:14 +00:00
|
|
|
"Return code that runs CODE if SYM matches QPAT and if MATCHES match.
|
2011-02-28 04:24:40 +00:00
|
|
|
Otherwise, it defers to REST which is a list of branches of the form
|
2010-08-10 13:18:14 +00:00
|
|
|
\(OTHER_MATCH OTHER-CODE . OTHER-VARS)."
|
|
|
|
(cond
|
|
|
|
((eq (car-safe qpat) '\,) (error "Can't use `,UPATTERN"))
|
|
|
|
((floatp qpat) (error "Floating point patterns not supported"))
|
|
|
|
((vectorp qpat)
|
|
|
|
;; FIXME.
|
|
|
|
(error "Vector QPatterns not implemented yet"))
|
|
|
|
((consp qpat)
|
2011-03-06 21:22:16 +00:00
|
|
|
(let* ((syma (make-symbol "xcar"))
|
|
|
|
(symd (make-symbol "xcdr"))
|
|
|
|
(splitrest (pcase--split-rest
|
|
|
|
sym
|
2012-06-18 19:23:35 +00:00
|
|
|
(lambda (pat) (pcase--split-consp syma symd pat))
|
2011-03-06 21:22:16 +00:00
|
|
|
rest))
|
|
|
|
(then-rest (car splitrest))
|
|
|
|
(else-rest (cdr splitrest))
|
|
|
|
(then-body (pcase--u1 `((match ,syma . ,(pcase--upat (car qpat)))
|
|
|
|
(match ,symd . ,(pcase--upat (cdr qpat)))
|
|
|
|
,@matches)
|
|
|
|
code vars then-rest)))
|
|
|
|
(pcase--if
|
|
|
|
`(consp ,sym)
|
|
|
|
;; We want to be careful to only add bindings that are used.
|
|
|
|
;; The byte-compiler could do that for us, but it would have to pay
|
|
|
|
;; attention to the `consp' test in order to figure out that car/cdr
|
|
|
|
;; can't signal errors and our byte-compiler is not that clever.
|
2012-05-05 02:05:49 +00:00
|
|
|
;; FIXME: Some of those let bindings occur too early (they are used in
|
|
|
|
;; `then-body', but only within some sub-branch).
|
2012-06-07 19:25:48 +00:00
|
|
|
(macroexp-let*
|
2012-05-05 02:05:49 +00:00
|
|
|
`(,@(if (get syma 'pcase-used) `((,syma (car ,sym))))
|
2011-03-06 21:22:16 +00:00
|
|
|
,@(if (get symd 'pcase-used) `((,symd (cdr ,sym)))))
|
2012-05-05 02:05:49 +00:00
|
|
|
then-body)
|
2011-03-06 21:22:16 +00:00
|
|
|
(pcase--u else-rest))))
|
2010-10-29 01:05:38 +00:00
|
|
|
((or (integerp qpat) (symbolp qpat) (stringp qpat))
|
2011-02-18 13:55:51 +00:00
|
|
|
(let* ((splitrest (pcase--split-rest
|
2012-06-18 19:23:35 +00:00
|
|
|
sym (lambda (pat) (pcase--split-equal qpat pat)) rest))
|
2011-02-18 13:55:51 +00:00
|
|
|
(then-rest (car splitrest))
|
|
|
|
(else-rest (cdr splitrest)))
|
2012-05-05 02:05:49 +00:00
|
|
|
(pcase--if (cond
|
|
|
|
((stringp qpat) `(equal ,sym ,qpat))
|
|
|
|
((null qpat) `(null ,sym))
|
|
|
|
(t `(eq ,sym ',qpat)))
|
2010-11-24 16:39:51 +00:00
|
|
|
(pcase--u1 matches code vars then-rest)
|
|
|
|
(pcase--u else-rest))))
|
2011-12-31 01:27:15 +00:00
|
|
|
(t (error "Unknown QPattern %s" qpat))))
|
2010-08-11 02:14:53 +00:00
|
|
|
|
2010-08-10 13:18:14 +00:00
|
|
|
|
|
|
|
(provide 'pcase)
|
|
|
|
;;; pcase.el ends here
|