aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Petton2015-05-12 21:57:18 +0200
committerNicolas Petton2015-05-12 22:15:45 +0200
commitf9ba8dc074bf34d2b3d88df69ed19e9141368b81 (patch)
tree2f5edb91806a8b66239b8a019784777d93dd7b45
parent4f5382c6e43cb67e0da7765056866745a2295698 (diff)
downloademacs-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.el55
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.
74Matches if the object is a sequence (list, string or vector), and
75binds each element of ARGS to the corresponding element of the
76sequence."
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.
78ARGS can also include the `&rest' marker followed by a variable 83ARGS can also include the `&rest' marker followed by a variable
79name to be bound to the rest of SEQ." 84name 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."
365if BINDINGS is non-nil, append new bindings to it, and return 371 (let ((bindings '())
366BINDINGS." 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.
418If no element is found, return nil." 423If 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.
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 () 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