diff options
| author | Nicolas Petton | 2015-05-12 21:57:18 +0200 |
|---|---|---|
| committer | Nicolas Petton | 2015-05-12 22:15:45 +0200 |
| commit | f9ba8dc074bf34d2b3d88df69ed19e9141368b81 (patch) | |
| tree | 2f5edb91806a8b66239b8a019784777d93dd7b45 | |
| parent | 4f5382c6e43cb67e0da7765056866745a2295698 (diff) | |
| download | emacs-f9ba8dc074bf34d2b3d88df69ed19e9141368b81.tar.gz emacs-f9ba8dc074bf34d2b3d88df69ed19e9141368b81.zip | |
Improve the seq pcase pattern and the `seq-let' macro
* lisp/emacs-lisp/seq.el: The pcase pattern now matches only if the
object is a sequence, and binds each element of ARGS to the
corresponding element of the sequence.
| -rw-r--r-- | lisp/emacs-lisp/seq.el | 55 |
1 files changed, 23 insertions, 32 deletions
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index eef96e320a4..5553de658b2 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el | |||
| @@ -69,8 +69,13 @@ Evaluate BODY with VAR bound to each element of SEQ, in turn. | |||
| 69 | ;; Implementation of `seq-let' based on a `pcase' | 69 | ;; Implementation of `seq-let' based on a `pcase' |
| 70 | ;; pattern. Requires Emacs>=25.1. | 70 | ;; pattern. Requires Emacs>=25.1. |
| 71 | (progn | 71 | (progn |
| 72 | (pcase-defmacro seq (bindings) | 72 | (pcase-defmacro seq (&rest args) |
| 73 | `(and ,@(seq--make-pcase-bindings bindings))) | 73 | "pcase pattern matching sequence elements. |
| 74 | Matches if the object is a sequence (list, string or vector), and | ||
| 75 | binds each element of ARGS to the corresponding element of the | ||
| 76 | sequence." | ||
| 77 | `(and (pred seq-p) | ||
| 78 | ,@(seq--make-pcase-bindings args))) | ||
| 74 | 79 | ||
| 75 | (defmacro seq-let (args seq &rest body) | 80 | (defmacro seq-let (args seq &rest body) |
| 76 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY. | 81 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY. |
| @@ -78,7 +83,8 @@ Evaluate BODY with VAR bound to each element of SEQ, in turn. | |||
| 78 | ARGS can also include the `&rest' marker followed by a variable | 83 | ARGS can also include the `&rest' marker followed by a variable |
| 79 | name to be bound to the rest of SEQ." | 84 | name to be bound to the rest of SEQ." |
| 80 | (declare (indent 2) (debug t)) | 85 | (declare (indent 2) (debug t)) |
| 81 | `(pcase-let (((seq ,args) ,seq)) ,@body))) | 86 | `(pcase-let ((,(seq--make-pcase-patterns args) ,seq)) |
| 87 | ,@body))) | ||
| 82 | 88 | ||
| 83 | ;; Implementation of `seq-let' compatible with Emacs<25.1. | 89 | ;; Implementation of `seq-let' compatible with Emacs<25.1. |
| 84 | (defmacro seq-let (args seq &rest body) | 90 | (defmacro seq-let (args seq &rest body) |
| @@ -360,32 +366,32 @@ This is an optimization for lists in `seq-take-while'." | |||
| 360 | (setq n (+ 1 n))) | 366 | (setq n (+ 1 n))) |
| 361 | n)) | 367 | n)) |
| 362 | 368 | ||
| 363 | (defun seq--make-pcase-bindings (args &optional bindings nested-indexes) | 369 | (defun seq--make-pcase-bindings (args) |
| 364 | "Return a list of bindings of the variables in ARGS to the elements of a sequence. | 370 | "Return a list of bindings of the variables in ARGS to the elements of a sequence." |
| 365 | if BINDINGS is non-nil, append new bindings to it, and return | 371 | (let ((bindings '()) |
| 366 | BINDINGS." | 372 | (index 0) |
| 367 | (let ((index 0) | ||
| 368 | (rest-marker nil)) | 373 | (rest-marker nil)) |
| 369 | (seq-doseq (name args) | 374 | (seq-doseq (name args) |
| 370 | (unless rest-marker | 375 | (unless rest-marker |
| 371 | (pcase name | 376 | (pcase name |
| 372 | ((pred seq-p) | ||
| 373 | (setq bindings (seq--make-pcase-bindings (seq--elt-safe args index) | ||
| 374 | bindings | ||
| 375 | (cons index nested-indexes)))) | ||
| 376 | (`&rest | 377 | (`&rest |
| 377 | (progn (push `(app (seq--reverse-args #'seq-drop ,index) | 378 | (progn (push `(app (pcase--flip seq-drop ,index) |
| 378 | ,(seq--elt-safe args (1+ index))) | 379 | ,(seq--elt-safe args (1+ index))) |
| 379 | bindings) | 380 | bindings) |
| 380 | (setq rest-marker t))) | 381 | (setq rest-marker t))) |
| 381 | (t | 382 | (t |
| 382 | (push `(app (seq--reverse-args #'seq--nested-elt | 383 | (push `(app (pcase--flip seq--elt-safe ,index) ,name) bindings)))) |
| 383 | (reverse (cons ,index ',nested-indexes))) | ||
| 384 | ,name) | ||
| 385 | bindings)))) | ||
| 386 | (setq index (1+ index))) | 384 | (setq index (1+ index))) |
| 387 | bindings)) | 385 | bindings)) |
| 388 | 386 | ||
| 387 | (defun seq--make-pcase-patterns (args) | ||
| 388 | "Return a list of `(seq ...)' pcase patterns from the argument list ARGS." | ||
| 389 | (cons 'seq | ||
| 390 | (seq-map (lambda (elt) | ||
| 391 | (if (seq-p elt) | ||
| 392 | (seq--make-pcase-patterns elt) | ||
| 393 | elt)) | ||
| 394 | args))) | ||
| 389 | 395 | ||
| 390 | ;; Helper function for the Backward-compatible version of `seq-let' | 396 | ;; Helper function for the Backward-compatible version of `seq-let' |
| 391 | ;; for Emacs<25.1. | 397 | ;; for Emacs<25.1. |
| @@ -412,7 +418,6 @@ BINDINGS." | |||
| 412 | (setq index (1+ index))) | 418 | (setq index (1+ index))) |
| 413 | bindings)) | 419 | bindings)) |
| 414 | 420 | ||
| 415 | |||
| 416 | (defun seq--elt-safe (seq n) | 421 | (defun seq--elt-safe (seq n) |
| 417 | "Return element of SEQ at the index N. | 422 | "Return element of SEQ at the index N. |
| 418 | If no element is found, return nil." | 423 | If no element is found, return nil." |
| @@ -421,20 +426,6 @@ If no element is found, return nil." | |||
| 421 | (> (seq-length seq) n))) | 426 | (> (seq-length seq) n))) |
| 422 | (seq-elt seq n))) | 427 | (seq-elt seq n))) |
| 423 | 428 | ||
| 424 | (defun seq--nested-elt (seq indexes &optional default) | ||
| 425 | "Traverse SEQ using INDEXES and return the looked up element or DEFAULT if nil. | ||
| 426 | SEQ can be a nested sequence composed of lists, vectors and strings." | ||
| 427 | (or (seq-reduce (lambda (acc index) | ||
| 428 | (when (seq-p acc) | ||
| 429 | (seq--elt-safe acc index))) | ||
| 430 | indexes | ||
| 431 | seq) | ||
| 432 | default)) | ||
| 433 | |||
| 434 | (defun seq--reverse-args (fn &rest args) | ||
| 435 | "Call FN with ARGS reversed." | ||
| 436 | (apply fn (reverse args))) | ||
| 437 | |||
| 438 | (defun seq--activate-font-lock-keywords () | 429 | (defun seq--activate-font-lock-keywords () |
| 439 | "Activate font-lock keywords for some symbols defined in seq." | 430 | "Activate font-lock keywords for some symbols defined in seq." |
| 440 | (font-lock-add-keywords 'emacs-lisp-mode | 431 | (font-lock-add-keywords 'emacs-lisp-mode |