]> git.eshelyaron.com Git - emacs.git/commitdiff
Add a `pcase-setq' macro
authorEarl Hyatt <okamsn@protonmail.com>
Wed, 11 Aug 2021 21:54:31 +0000 (23:54 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Wed, 11 Aug 2021 21:54:31 +0000 (23:54 +0200)
* doc/lispref/control.texi (Destructuring with pcase Patterns):
Document this macro.

* lisp/emacs-lisp/pcase.el (pcase-setq): New macro.  This macro is
the 'setq' equivalent of 'pcase-let'.

* test/lisp/emacs-lisp/pcase-tests.el (pcase-setq): Test this new
macro. (bug#49809).

doc/lispref/control.texi
etc/NEWS
lisp/emacs-lisp/pcase.el
test/lisp/emacs-lisp/pcase-tests.el

index 5026d0a4d70628050023ee6e07779660923c434c..aacf66c5cf8dd580c6ebf5ff5b77fef0271dfcc1 100644 (file)
@@ -1312,6 +1312,10 @@ element of @var{list}.  The bindings are performed as if by
 up being equivalent to @code{dolist} (@pxref{Iteration}).
 @end defmac
 
+@defmac pcase-setq pattern value@dots{}
+Assign values to variables in a @code{setq} form, destructuring each
+@var{value} according to its respective @var{pattern}.
+@end defmac
 
 @node Iteration
 @section Iteration
index 18fa54b97e6b3ca6b00a92da9f0e4fec4fd76525..ffe8f5b32cb9bc64ed98be1136b1efd8ced96d1a 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -559,6 +559,10 @@ The new 'cl-type' pattern compares types using 'cl-typep', which allows
 comparing simple types like '(cl-type integer)', as well as forms like
 '(cl-type (integer 0 10))'.
 
+*** New macro 'pcase-setq'
+This macro is the 'setq' equivalent of 'pcase-let', which allows for
+destructuring patterns in a 'setq' form.
+
 +++
 ** profiler.el
 The results displayed by 'profiler-report' now have the usage figures
index 006517db7595d00e7af67aef5bb590dfa3fb6794..d111d9e41f8a6466ef9428895ba2b9c642c0420e 100644 (file)
@@ -317,6 +317,44 @@ of the elements of LIST is performed as if by `pcase-let'.
          (pcase-let* ((,(car spec) ,tmpvar))
            ,@body)))))
 
+;;;###autoload
+(defmacro pcase-setq (pat val &rest args)
+  "Assign values to variables by destructuring with `pcase'.
+PATTERNS are normal `pcase' patterns, and VALUES are expression.
+
+Evaluation happens sequentially as in `setq' (not in parallel).
+
+An example: (pcase-setq `((,a) [(,b)]) '((1) [(2)]))
+
+When a PATTERN doesn't match it's VALUE, the pair is silently skipped.
+
+\(fn PATTERNS VALUE PATTERN VALUES ...)"
+  (declare (debug (&rest [pcase-PAT form])))
+  (cond
+   (args
+    (let ((arg-length (length args)))
+      (unless (= 0 (mod arg-length 2))
+        (signal 'wrong-number-of-arguments
+                (list 'pcase-setq (+ 2 arg-length)))))
+    (let ((result))
+      (while args
+        (push `(pcase-setq ,(pop args) ,(pop args))
+              result))
+      `(progn
+         (pcase-setq ,pat ,val)
+         ,@(nreverse result))))
+   ((pcase--trivial-upat-p pat)
+    `(setq ,pat ,val))
+   (t
+    (pcase-compile-patterns
+     val
+     (list (cons pat
+                 (lambda (varvals &rest _)
+                   `(setq ,@(mapcan (lambda (varval)
+                                      (let ((var (car varval))
+                                            (val (cadr varval)))
+                                        (list var val)))
+                                    varvals)))))))))
 
 (defun pcase--trivial-upat-p (upat)
   (and (symbolp upat) (not (memq upat pcase--dontcare-upats))))
index 02d3878ad08149a866fcbd36dcb808a9cca27b5a..67882d00d8614e09f58c51f3eb2469815663e4b9 100644 (file)
   (should-error (pcase 1
                   ((cl-type notatype) 'integer))))
 
+(ert-deftest pcase-setq ()
+  (should (equal (let (a b)
+                   (pcase-setq `(,a ,b) nil)
+                   (list a b))
+                 (list nil nil)))
+
+  (should (equal (let (a b)
+                   (pcase-setq `((,a) (,b)) '((1) (2)))
+                   (list a b))
+                 (list 1 2)))
+
+  (should (equal (list 'unset 'unset)
+                 (let ((a 'unset)
+                       (b 'unset))
+                   (pcase-setq `(,a ,b) nil)
+                   (list a b))))
+
+  (should (equal (let (a b)
+                   (pcase-setq `[,a ,b] [1 2])
+                   (list a b))
+                 '(1 2)))
+
+  (should (equal (let (a b)
+                   (pcase-setq a 1 b 2)
+                   (list a b))
+                 '(1 2)))
+
+  (should (= (let (a)
+               (pcase-setq a 1 `(,a) '(2))
+               a)
+             2))
+
+  (should (equal (let (array list-item array-copy)
+                   (pcase-setq (or `(,list-item) array) [1 2 3]
+                               array-copy array
+                               ;; This re-sets `array' to nil.
+                               (or `(,list-item) array) '(4))
+                   (list array array-copy list-item))
+                 '(nil [1 2 3] 4)))
+
+  (let ((a nil))
+    (should-error (pcase-setq a 1 b)
+                  :type '(wrong-number-of-arguments))
+    (should (eq a nil)))
+
+  (should-error (pcase-setq a)
+                :type '(wrong-number-of-arguments)))
+
 ;;; pcase-tests.el ends here.