diff options
| author | Nicolas Petton | 2015-05-10 20:19:38 +0200 |
|---|---|---|
| committer | Nicolas Petton | 2015-05-10 20:19:38 +0200 |
| commit | 387e1e19430c12eb7db5b58f861c4e85613476f8 (patch) | |
| tree | a0b1e5a92e64aa7fa844241ae17d5d86a233d6d9 | |
| parent | 0c81a8bfdefdc7e7ee687f6cce51649c9e808876 (diff) | |
| download | emacs-387e1e19430c12eb7db5b58f861c4e85613476f8.tar.gz emacs-387e1e19430c12eb7db5b58f861c4e85613476f8.zip | |
New version of `seq-let' based on a pcase pattern
* lisp/emacs-lisp/seq.el (seq-let): Define the macro in terms of a
pcase pattern if `pcase-defmacro' is defined (Emacs>=25.1).
| -rw-r--r-- | lisp/emacs-lisp/seq.el | 104 |
1 files changed, 81 insertions, 23 deletions
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index f1633ce8cd7..eef96e320a4 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | ;; Author: Nicolas Petton <nicolas@petton.fr> | 5 | ;; Author: Nicolas Petton <nicolas@petton.fr> |
| 6 | ;; Keywords: sequences | 6 | ;; Keywords: sequences |
| 7 | ;; Version: 1.6 | 7 | ;; Version: 1.7 |
| 8 | ;; Package: seq | 8 | ;; Package: seq |
| 9 | 9 | ||
| 10 | ;; Maintainer: emacs-devel@gnu.org | 10 | ;; Maintainer: emacs-devel@gnu.org |
| @@ -40,11 +40,6 @@ | |||
| 40 | ;; | 40 | ;; |
| 41 | ;; All functions are tested in test/automated/seq-tests.el | 41 | ;; All functions are tested in test/automated/seq-tests.el |
| 42 | 42 | ||
| 43 | ;;; TODO: | ||
| 44 | |||
| 45 | ;; - Add a pcase macro named using `pcase-defmacro' that `seq-let' | ||
| 46 | ;; - could wrap. | ||
| 47 | |||
| 48 | ;;; Code: | 43 | ;;; Code: |
| 49 | 44 | ||
| 50 | (defmacro seq-doseq (spec &rest body) | 45 | (defmacro seq-doseq (spec &rest body) |
| @@ -70,13 +65,32 @@ Evaluate BODY with VAR bound to each element of SEQ, in turn. | |||
| 70 | (pop ,index)))) | 65 | (pop ,index)))) |
| 71 | ,@body))))) | 66 | ,@body))))) |
| 72 | 67 | ||
| 73 | (defmacro seq-let (args seq &rest body) | 68 | (if (fboundp 'pcase-defmacro) |
| 74 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY." | 69 | ;; Implementation of `seq-let' based on a `pcase' |
| 75 | (declare (indent 2) (debug t)) | 70 | ;; pattern. Requires Emacs>=25.1. |
| 76 | (let ((seq-var (make-symbol "seq"))) | 71 | (progn |
| 77 | `(let* ((,seq-var ,seq) | 72 | (pcase-defmacro seq (bindings) |
| 78 | ,@(seq--make-bindings args seq-var)) | 73 | `(and ,@(seq--make-pcase-bindings bindings))) |
| 79 | ,@body))) | 74 | |
| 75 | (defmacro seq-let (args seq &rest body) | ||
| 76 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY. | ||
| 77 | |||
| 78 | ARGS can also include the `&rest' marker followed by a variable | ||
| 79 | name to be bound to the rest of SEQ." | ||
| 80 | (declare (indent 2) (debug t)) | ||
| 81 | `(pcase-let (((seq ,args) ,seq)) ,@body))) | ||
| 82 | |||
| 83 | ;; Implementation of `seq-let' compatible with Emacs<25.1. | ||
| 84 | (defmacro seq-let (args seq &rest body) | ||
| 85 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY. | ||
| 86 | |||
| 87 | ARGS can also include the `&rest' marker followed by a variable | ||
| 88 | name to be bound to the rest of SEQ." | ||
| 89 | (declare (indent 2) (debug t)) | ||
| 90 | (let ((seq-var (make-symbol "seq"))) | ||
| 91 | `(let* ((,seq-var ,seq) | ||
| 92 | ,@(seq--make-bindings args seq-var)) | ||
| 93 | ,@body)))) | ||
| 80 | 94 | ||
| 81 | (defun seq-drop (seq n) | 95 | (defun seq-drop (seq n) |
| 82 | "Return a subsequence of SEQ without its first N elements. | 96 | "Return a subsequence of SEQ without its first N elements. |
| @@ -346,19 +360,43 @@ This is an optimization for lists in `seq-take-while'." | |||
| 346 | (setq n (+ 1 n))) | 360 | (setq n (+ 1 n))) |
| 347 | n)) | 361 | n)) |
| 348 | 362 | ||
| 349 | (defun seq--activate-font-lock-keywords () | 363 | (defun seq--make-pcase-bindings (args &optional bindings nested-indexes) |
| 350 | "Activate font-lock keywords for some symbols defined in seq." | 364 | "Return a list of bindings of the variables in ARGS to the elements of a sequence. |
| 351 | (font-lock-add-keywords 'emacs-lisp-mode | 365 | if BINDINGS is non-nil, append new bindings to it, and return |
| 352 | '("\\<seq-doseq\\>" "\\<seq-let\\>"))) | 366 | BINDINGS." |
| 367 | (let ((index 0) | ||
| 368 | (rest-marker nil)) | ||
| 369 | (seq-doseq (name args) | ||
| 370 | (unless rest-marker | ||
| 371 | (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 | (progn (push `(app (seq--reverse-args #'seq-drop ,index) | ||
| 378 | ,(seq--elt-safe args (1+ index))) | ||
| 379 | bindings) | ||
| 380 | (setq rest-marker t))) | ||
| 381 | (t | ||
| 382 | (push `(app (seq--reverse-args #'seq--nested-elt | ||
| 383 | (reverse (cons ,index ',nested-indexes))) | ||
| 384 | ,name) | ||
| 385 | bindings)))) | ||
| 386 | (setq index (1+ index))) | ||
| 387 | bindings)) | ||
| 353 | 388 | ||
| 389 | |||
| 390 | ;; Helper function for the Backward-compatible version of `seq-let' | ||
| 391 | ;; for Emacs<25.1. | ||
| 354 | (defun seq--make-bindings (args seq &optional bindings) | 392 | (defun seq--make-bindings (args seq &optional bindings) |
| 355 | "Return a list of bindings of the variables in ARGS to the elements of SEQ. | 393 | "Return a list of bindings of the variables in ARGS to the elements of a sequence. |
| 356 | if BINDINGS is non-nil, append new bindings to it, and | 394 | if BINDINGS is non-nil, append new bindings to it, and return |
| 357 | return BINDINGS." | 395 | BINDINGS." |
| 358 | (let ((index 0) | 396 | (let ((index 0) |
| 359 | (rest-bound nil)) | 397 | (rest-marker nil)) |
| 360 | (seq-doseq (name args) | 398 | (seq-doseq (name args) |
| 361 | (unless rest-bound | 399 | (unless rest-marker |
| 362 | (pcase name | 400 | (pcase name |
| 363 | ((pred seq-p) | 401 | ((pred seq-p) |
| 364 | (setq bindings (seq--make-bindings (seq--elt-safe args index) | 402 | (setq bindings (seq--make-bindings (seq--elt-safe args index) |
| @@ -368,12 +406,13 @@ return BINDINGS." | |||
| 368 | (progn (push `(,(seq--elt-safe args (1+ index)) | 406 | (progn (push `(,(seq--elt-safe args (1+ index)) |
| 369 | (seq-drop ,seq ,index)) | 407 | (seq-drop ,seq ,index)) |
| 370 | bindings) | 408 | bindings) |
| 371 | (setq rest-bound t))) | 409 | (setq rest-marker t))) |
| 372 | (t | 410 | (t |
| 373 | (push `(,name (seq--elt-safe ,seq ,index)) bindings)))) | 411 | (push `(,name (seq--elt-safe ,seq ,index)) bindings)))) |
| 374 | (setq index (1+ index))) | 412 | (setq index (1+ index))) |
| 375 | bindings)) | 413 | bindings)) |
| 376 | 414 | ||
| 415 | |||
| 377 | (defun seq--elt-safe (seq n) | 416 | (defun seq--elt-safe (seq n) |
| 378 | "Return element of SEQ at the index N. | 417 | "Return element of SEQ at the index N. |
| 379 | If no element is found, return nil." | 418 | If no element is found, return nil." |
| @@ -382,6 +421,25 @@ If no element is found, return nil." | |||
| 382 | (> (seq-length seq) n))) | 421 | (> (seq-length seq) n))) |
| 383 | (seq-elt seq n))) | 422 | (seq-elt seq n))) |
| 384 | 423 | ||
| 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 () | ||
| 439 | "Activate font-lock keywords for some symbols defined in seq." | ||
| 440 | (font-lock-add-keywords 'emacs-lisp-mode | ||
| 441 | '("\\<seq-doseq\\>" "\\<seq-let\\>"))) | ||
| 442 | |||
| 385 | (defalias 'seq-copy #'copy-sequence) | 443 | (defalias 'seq-copy #'copy-sequence) |
| 386 | (defalias 'seq-elt #'elt) | 444 | (defalias 'seq-elt #'elt) |
| 387 | (defalias 'seq-length #'length) | 445 | (defalias 'seq-length #'length) |