aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorStefan Monnier2011-02-26 21:50:38 -0500
committerStefan Monnier2011-02-26 21:50:38 -0500
commit1f0816b69dfdbda486bf0329bbfb2e8ccee63d39 (patch)
treec6ed9cdc04df06eea3d23aa98d06c45f86647445 /lisp
parent53f963cf73d93a7d1dca07d4c338acd5b6c8cb2f (diff)
downloademacs-1f0816b69dfdbda486bf0329bbfb2e8ccee63d39.tar.gz
emacs-1f0816b69dfdbda486bf0329bbfb2e8ccee63d39.zip
* lisp/emacs-lisp/pcase.el (pcase--if): Try to invert test to reduce depth.
(pcase-mutually-exclusive-predicates): New var. (pcase--split-consp, pcase--split-pred): Use it. (pcase--split-equal, pcase--split-member): When splitting against a pure predicate, run it to know the outcome. (pcase--u1): Mark vars that are actually used. (pcase--q1): Avoid introducing unused vars.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/ChangeLog10
-rw-r--r--lisp/emacs-lisp/pcase.el101
2 files changed, 97 insertions, 14 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index d9f4c3c3ea2..c2731530e57 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,13 @@
12011-02-27 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * emacs-lisp/pcase.el (pcase--if): Try to invert test to reduce depth.
4 (pcase-mutually-exclusive-predicates): New var.
5 (pcase--split-consp, pcase--split-pred): Use it.
6 (pcase--split-equal, pcase--split-member): When splitting against
7 a pure predicate, run it to know the outcome.
8 (pcase--u1): Mark vars that are actually used.
9 (pcase--q1): Avoid introducing unused vars.
10
12011-02-27 Jay Belanger <jay.p.belanger@gmail.com> 112011-02-27 Jay Belanger <jay.p.belanger@gmail.com>
2 12
3 * calc/calc-ext.el (calc-init-extensions): 13 * calc/calc-ext.el (calc-init-extensions):
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 3179672a3ec..0d5fd99db5d 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -32,6 +32,14 @@
32;; could be defined this way, as a shorthand for (pred (lambda (_) BOOLEXP)). 32;; could be defined this way, as a shorthand for (pred (lambda (_) BOOLEXP)).
33;; But better would be if we could define new ways to match by having the 33;; But better would be if we could define new ways to match by having the
34;; extension provide its own `pcase--split-<foo>' thingy. 34;; extension provide its own `pcase--split-<foo>' thingy.
35;; - provide something like (setq VAR) so a var can be set rather than
36;; let-bound.
37;; - provide a way to fallthrough to other cases.
38;; - try and be more clever to reduce the size of the decision tree, and
39;; to reduce the number of leafs that need to be turned into function:
40;; - first, do the tests shared by all remaining branches (it will have
41;; to be performed anyway, so better so it first so it's shared).
42;; - then choose the test that discriminates more (?).
35;; - ideally we'd want (pcase s ((re RE1) E1) ((re RE2) E2)) to be able to 43;; - ideally we'd want (pcase s ((re RE1) E1) ((re RE2) E2)) to be able to
36;; generate a lex-style DFA to decide whether to run E1 or E2. 44;; generate a lex-style DFA to decide whether to run E1 or E2.
37 45
@@ -209,6 +217,7 @@ of the form (UPAT EXP)."
209(defun pcase--if (test then else) 217(defun pcase--if (test then else)
210 (cond 218 (cond
211 ((eq else :pcase--dontcare) then) 219 ((eq else :pcase--dontcare) then)
220 ((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen?
212 ((eq (car-safe else) 'if) 221 ((eq (car-safe else) 'if)
213 (if (equal test (nth 1 else)) 222 (if (equal test (nth 1 else))
214 ;; Doing a test a second time: get rid of the redundancy. 223 ;; Doing a test a second time: get rid of the redundancy.
@@ -223,6 +232,8 @@ of the form (UPAT EXP)."
223 `(cond (,test ,then) 232 `(cond (,test ,then)
224 ;; Doing a test a second time: get rid of the redundancy, as above. 233 ;; Doing a test a second time: get rid of the redundancy, as above.
225 ,@(remove (assoc test else) (cdr else)))) 234 ,@(remove (assoc test else) (cdr else))))
235 ;; Invert the test if that lets us reduce the depth of the tree.
236 ((memq (car-safe then) '(if cond)) (pcase--if `(not ,test) else then))
226 (t `(if ,test ,then ,else)))) 237 (t `(if ,test ,then ,else))))
227 238
228(defun pcase--upat (qpattern) 239(defun pcase--upat (qpattern)
@@ -264,6 +275,22 @@ MATCH is the pattern that needs to be matched, of the form:
264(defun pcase--and (match matches) 275(defun pcase--and (match matches)
265 (if matches `(and ,match ,@matches) match)) 276 (if matches `(and ,match ,@matches) match))
266 277
278(defconst pcase-mutually-exclusive-predicates
279 '((symbolp . integerp)
280 (symbolp . numberp)
281 (symbolp . consp)
282 (symbolp . arrayp)
283 (symbolp . stringp)
284 (integerp . consp)
285 (integerp . arrayp)
286 (integerp . stringp)
287 (numberp . consp)
288 (numberp . arrayp)
289 (numberp . stringp)
290 (consp . arrayp)
291 (consp . stringp)
292 (arrayp . stringp)))
293
267(defun pcase--split-match (sym splitter match) 294(defun pcase--split-match (sym splitter match)
268 (case (car match) 295 (case (car match)
269 ((match) 296 ((match)
@@ -324,8 +351,14 @@ MATCH is the pattern that needs to be matched, of the form:
324 (cons `(and (match ,syma . ,(pcase--upat (car qpat))) 351 (cons `(and (match ,syma . ,(pcase--upat (car qpat)))
325 (match ,symd . ,(pcase--upat (cdr qpat)))) 352 (match ,symd . ,(pcase--upat (cdr qpat))))
326 :pcase--fail))) 353 :pcase--fail)))
327 ;; A QPattern but not for a cons, can only go the `else' side. 354 ;; A QPattern but not for a cons, can only go to the `else' side.
328 ((eq (car-safe pat) '\`) (cons :pcase--fail nil)))) 355 ((eq (car-safe pat) '\`) (cons :pcase--fail nil))
356 ((and (eq (car-safe pat) 'pred)
357 (or (member (cons 'consp (cadr pat))
358 pcase-mutually-exclusive-predicates)
359 (member (cons (cadr pat) 'consp)
360 pcase-mutually-exclusive-predicates)))
361 (cons :pcase--fail nil))))
329 362
330(defun pcase--split-equal (elem pat) 363(defun pcase--split-equal (elem pat)
331 (cond 364 (cond
@@ -337,7 +370,12 @@ MATCH is the pattern that needs to be matched, of the form:
337 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) 370 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
338 ;; (consp (cadr pat))) 371 ;; (consp (cadr pat)))
339 ) 372 )
340 (cons :pcase--fail nil)))) 373 (cons :pcase--fail nil))
374 ((and (eq (car-safe pat) 'pred)
375 (symbolp (cadr pat))
376 (get (cadr pat) 'side-effect-free)
377 (funcall (cadr pat) elem))
378 (cons :pcase--succeed nil))))
341 379
342(defun pcase--split-member (elems pat) 380(defun pcase--split-member (elems pat)
343 ;; Based on pcase--split-equal. 381 ;; Based on pcase--split-equal.
@@ -354,13 +392,39 @@ MATCH is the pattern that needs to be matched, of the form:
354 ;; (or (integerp (cadr pat)) (symbolp (cadr pat)) 392 ;; (or (integerp (cadr pat)) (symbolp (cadr pat))
355 ;; (consp (cadr pat))) 393 ;; (consp (cadr pat)))
356 ) 394 )
357 (cons :pcase--fail nil)))) 395 (cons :pcase--fail nil))
396 ((and (eq (car-safe pat) 'pred)
397 (symbolp (cadr pat))
398 (get (cadr pat) 'side-effect-free)
399 (let ((p (cadr pat)) (all t))
400 (dolist (elem elems)
401 (unless (funcall p elem) (setq all nil)))
402 all))
403 (cons :pcase--succeed nil))))
358 404
359(defun pcase--split-pred (upat pat) 405(defun pcase--split-pred (upat pat)
360 ;; FIXME: For predicates like (pred (> a)), two such predicates may 406 ;; FIXME: For predicates like (pred (> a)), two such predicates may
361 ;; actually refer to different variables `a'. 407 ;; actually refer to different variables `a'.
362 (if (equal upat pat) 408 (cond
363 (cons :pcase--succeed :pcase--fail))) 409 ((equal upat pat) (cons :pcase--succeed :pcase--fail))
410 ((and (eq 'pred (car upat))
411 (eq 'pred (car-safe pat))
412 (or (member (cons (cadr upat) (cadr pat))
413 pcase-mutually-exclusive-predicates)
414 (member (cons (cadr pat) (cadr upat))
415 pcase-mutually-exclusive-predicates)))
416 (cons :pcase--fail nil))
417 ;; ((and (eq 'pred (car upat))
418 ;; (eq '\` (car-safe pat))
419 ;; (symbolp (cadr upat))
420 ;; (or (symbolp (cadr pat)) (stringp (cadr pat)) (numberp (cadr pat)))
421 ;; (get (cadr upat) 'side-effect-free)
422 ;; (progn (message "Trying predicate %S" (cadr upat))
423 ;; (ignore-errors
424 ;; (funcall (cadr upat) (cadr pat)))))
425 ;; (message "Simplify pred %S against %S" upat pat)
426 ;; (cons nil :pcase--fail))
427 ))
364 428
365(defun pcase--fgrep (vars sexp) 429(defun pcase--fgrep (vars sexp)
366 "Check which of the symbols VARS appear in SEXP." 430 "Check which of the symbols VARS appear in SEXP."
@@ -433,6 +497,7 @@ and otherwise defers to REST which is a list of branches of the form
433 ((eq upat 'dontcare) :pcase--dontcare) 497 ((eq upat 'dontcare) :pcase--dontcare)
434 ((functionp upat) (error "Feature removed, use (pred %s)" upat)) 498 ((functionp upat) (error "Feature removed, use (pred %s)" upat))
435 ((memq (car-safe upat) '(guard pred)) 499 ((memq (car-safe upat) '(guard pred))
500 (if (eq (car upat) 'pred) (put sym 'pcase-used t))
436 (destructuring-bind (then-rest &rest else-rest) 501 (destructuring-bind (then-rest &rest else-rest)
437 (pcase--split-rest 502 (pcase--split-rest
438 sym (apply-partially #'pcase--split-pred upat) rest) 503 sym (apply-partially #'pcase--split-pred upat) rest)
@@ -459,6 +524,7 @@ and otherwise defers to REST which is a list of branches of the form
459 (pcase--u1 matches code vars then-rest) 524 (pcase--u1 matches code vars then-rest)
460 (pcase--u else-rest)))) 525 (pcase--u else-rest))))
461 ((symbolp upat) 526 ((symbolp upat)
527 (put sym 'pcase-used t)
462 (if (not (assq upat vars)) 528 (if (not (assq upat vars))
463 (pcase--u1 matches code (cons (cons upat sym) vars) rest) 529 (pcase--u1 matches code (cons (cons upat sym) vars) rest)
464 ;; Non-linear pattern. Turn it into an `eq' test. 530 ;; Non-linear pattern. Turn it into an `eq' test.
@@ -466,6 +532,7 @@ and otherwise defers to REST which is a list of branches of the form
466 matches) 532 matches)
467 code vars rest))) 533 code vars rest)))
468 ((eq (car-safe upat) '\`) 534 ((eq (car-safe upat) '\`)
535 (put sym 'pcase-used t)
469 (pcase--q1 sym (cadr upat) matches code vars rest)) 536 (pcase--q1 sym (cadr upat) matches code vars rest))
470 ((eq (car-safe upat) 'or) 537 ((eq (car-safe upat) 'or)
471 (let ((all (> (length (cdr upat)) 1)) 538 (let ((all (> (length (cdr upat)) 1))
@@ -539,14 +606,20 @@ and if not, defers to REST which is a list of branches of the form
539 (pcase--split-rest sym 606 (pcase--split-rest sym
540 (apply-partially #'pcase--split-consp syma symd) 607 (apply-partially #'pcase--split-consp syma symd)
541 rest) 608 rest)
542 (pcase--if `(consp ,sym) 609 (let ((then-body (pcase--u1 `((match ,syma . ,(pcase--upat (car qpat)))
543 `(let ((,syma (car ,sym)) 610 (match ,symd . ,(pcase--upat (cdr qpat)))
544 (,symd (cdr ,sym))) 611 ,@matches)
545 ,(pcase--u1 `((match ,syma . ,(pcase--upat (car qpat))) 612 code vars then-rest)))
546 (match ,symd . ,(pcase--upat (cdr qpat))) 613 (pcase--if
547 ,@matches) 614 `(consp ,sym)
548 code vars then-rest)) 615 ;; We want to be careful to only add bindings that are used.
549 (pcase--u else-rest))))) 616 ;; The byte-compiler could do that for us, but it would have to pay
617 ;; attention to the `consp' test in order to figure out that car/cdr
618 ;; can't signal errors and our byte-compiler is not that clever.
619 `(let (,@(if (get syma 'pcase-used) `((,syma (car ,sym))))
620 ,@(if (get symd 'pcase-used) `((,symd (cdr ,sym)))))
621 ,then-body)
622 (pcase--u else-rest))))))
550 ((or (integerp qpat) (symbolp qpat) (stringp qpat)) 623 ((or (integerp qpat) (symbolp qpat) (stringp qpat))
551 (destructuring-bind (then-rest &rest else-rest) 624 (destructuring-bind (then-rest &rest else-rest)
552 (pcase--split-rest sym (apply-partially 'pcase--split-equal qpat) rest) 625 (pcase--split-rest sym (apply-partially 'pcase--split-equal qpat) rest)