aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Petton2015-05-10 20:19:38 +0200
committerNicolas Petton2015-05-10 20:19:38 +0200
commit387e1e19430c12eb7db5b58f861c4e85613476f8 (patch)
treea0b1e5a92e64aa7fa844241ae17d5d86a233d6d9
parent0c81a8bfdefdc7e7ee687f6cce51649c9e808876 (diff)
downloademacs-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.el104
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
78ARGS can also include the `&rest' marker followed by a variable
79name 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
87ARGS can also include the `&rest' marker followed by a variable
88name 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 365if BINDINGS is non-nil, append new bindings to it, and return
352 '("\\<seq-doseq\\>" "\\<seq-let\\>"))) 366BINDINGS."
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.
356if BINDINGS is non-nil, append new bindings to it, and 394if BINDINGS is non-nil, append new bindings to it, and return
357return BINDINGS." 395BINDINGS."
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.
379If no element is found, return nil." 418If 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.
426SEQ 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)