From 3e775aa46bdd249f412963280f14518fa684ca38 Mon Sep 17 00:00:00 2001 From: okamsn Date: Tue, 27 Feb 2024 21:18:49 -0500 Subject: [PATCH 1/2] Create a Pcase pattern that works like `-let`. - Create file `dash-pcase.el`. - Copy tests of `-let` (except for parallel binding tests) to test pattern in `pcase` and `pcase-let`. - Update `README.md` and Info documentation using `make`. --- README.md | 166 ++++++++++++++++++++++ dash-pcase.el | 191 +++++++++++++++++++++++++ dash.texi | 184 ++++++++++++++++++++++++ dev/examples.el | 361 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 901 insertions(+), 1 deletion(-) create mode 100644 dash-pcase.el diff --git a/README.md b/README.md index 9f457b53..5e68142b 100644 --- a/README.md +++ b/README.md @@ -336,6 +336,8 @@ Macros that combine `let` and `let*` with destructuring and flow control. * [`-let*`](#-let-varlist-rest-body) `(varlist &rest body)` * [`-lambda`](#-lambda-match-form-rest-body) `(match-form &rest body)` * [`-setq`](#-setq-match-form-val) `([match-form val] ...)` +* [`pcase-let`](#pcase-let-bindings-rest-body) `(bindings &rest body)` +* [`pcase`](#pcase-exp-rest-cases) `(exp &rest cases)` ### Side effects @@ -2873,6 +2875,170 @@ multiple assignments it does not cause unexpected side effects. (let (c) (-setq (&plist :c c) (list :c "c")) c) ;; => "c" ``` +#### pcase-let `(bindings &rest body)` + +Like `let`, but supports destructuring `bindings` using [`pcase`](#pcase-exp-rest-cases) patterns. +`body` should be a list of expressions, and `bindings` should be a list of +bindings of the form (`pattern` `exp`). +All EXPs are evaluated first, and then used to perform destructuring +bindings by matching each `exp` against its respective `pattern`. Then +`body` is evaluated with those bindings in effect. + +Each `exp` should match its respective `pattern` (i.e. be of structure +compatible to `pattern`); a mismatch may signal an error or may go +undetected, binding variables to arbitrary values, such as `nil`. + +```el +(pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) ;; => (1 2 3 4) +(pcase-let (((dash (a b c . d)) (list 1 2 3 4 5 6))) (list a b c d)) ;; => (1 2 3 (4 5 6)) +(pcase-let (((dash (&plist :foo foo :bar bar)) (list :baz 3 :foo 1 :qux 4 :bar 2))) (list foo bar)) ;; => (1 2) +``` + +#### pcase `(exp &rest cases)` + +Evaluate `exp` to get `expval`; try passing control to one of `cases`. +`cases` is a list of elements of the form (`pattern` `code`...). +For the first `case` whose `pattern` "matches" `expval`, +evaluate its `code`..., and return the value of the last form. +If no `case` has a `pattern` that matches, return `nil`. + +Each `pattern` expands, in essence, to a predicate to call +on `expval`. When the return value of that call is non-`nil`, +`pattern` matches. `pattern` can take one of the forms: + + _ matches anything. + '`val` matches if `expval` is `equal` to `val`. + `keyword` shorthand for '`keyword` + `integer` shorthand for '`integer` + `string` shorthand for '`string` + `symbol` matches anything and binds it to `symbol`. + If a `symbol` is used twice in the same pattern + the second occurrence becomes an `eq`uality test. + (pred `fun`) matches if `fun` called on `expval` returns non-`nil`. + (pred (not `fun`)) matches if `fun` called on `expval` returns `nil`. + (app `fun` `pat`) matches if `fun` called on `expval` matches `pat`. + (guard `boolexp`) matches if `boolexp` evaluates to non-`nil`. + (and `pat`...) matches if all the patterns match. + (or `pat`...) matches if any of the patterns matches. + +`fun` in `pred` and `app` can take one of the forms: + `symbol` or (lambda `args` `body`) + call it with one argument + (`f` `arg1` .. ARGn) + call `f` with `arg1`..ARGn and `expval` as n+1'th argument + +`fun`, `boolexp`, and subsequent `pat` can refer to variables +bound earlier in the pattern by a `symbol` pattern. + +Additional patterns can be defined using `pcase-defmacro`. + +See Info node `(elisp) Pattern-Matching Conditional' in the +Emacs Lisp manual for more information and examples. + +-- ``qpat` + +Backquote-style pcase patterns: ``qpat` +`qpat` can take the following forms: + (`qpat1` . `qpat2`) matches if `qpat1` matches the car and `qpat2` the cdr. + [`qpat1` `qpat2`..QPATn] matches a vector of length n and `qpat1`..QPATn match + its 0..(n-1)th elements, respectively. + ,`pat` matches if the [`pcase`](#pcase-exp-rest-cases) pattern `pat` matches. + `symbol` matches if `expval` is `equal` to `symbol`. + `keyword` likewise for `keyword`. + `number` likewise for `number`. + `string` likewise for `string`. + +The list or vector `qpat` is a template. The predicate formed +by a backquote-style pattern is a combination of those +formed by any sub-patterns, wrapped in a top-level condition: +`expval` must be "congruent" with the template. For example: + + `(technical ,forum) + +The predicate is the logical-`and` of: + - Is `expval` a list of two elements? + - Is the first element the symbol `technical`? + - True! (The second element can be anything, and for the sake + of the body forms, its value is bound to the symbol `forum`.) + +-- (rx &rest `regexps`) + +`a` pattern that matches strings against `rx` `regexps` in sexp form. +`regexps` are interpreted as in `rx`. The pattern matches any +string that is a match for `regexps`, as if by `string-match`. + +In addition to the usual `rx` syntax, `regexps` can contain the +following constructs: + + (let `ref` `rx`...) binds the symbol `ref` to a submatch that matches + the regular expressions `rx`. `ref` is bound in + `code` to the string of the submatch or `nil`, but + can also be used in `backref`. + (backref `ref`) matches whatever the submatch `ref` matched. + `ref` can be a number, as usual, or a name + introduced by a previous (let `ref` ...) + construct. + +-- (dash `pat`) + +Destructure `exp` according to `pat` like in [`-let`](#-let-varlist-rest-body). + +-- (let `pat` `expr`) + +Matches if `expr` matches `pat`. + +-- (map &rest `args`) + +Build a [`pcase`](#pcase-exp-rest-cases) pattern matching map elements. + +`args` is a list of elements to be matched in the map. + +Each element of `args` can be of the form (`key` `pat`), in which case `key` is +evaluated and searched for in the map. The match fails if for any `key` +found in the map, the corresponding `pat` doesn't match the value +associated with the `key`. + +Each element can also be a `symbol`, which is an abbreviation of +a (`key` `pat`) tuple of the form ('`symbol` `symbol`). When `symbol` +is a keyword, it is an abbreviation of the form (:`symbol` `symbol`), +useful for binding plist values. + +Keys in `args` not found in the map are ignored, and the match doesn't +fail. + +-- (seq &rest `patterns`) + +Build a [`pcase`](#pcase-exp-rest-cases) pattern that matches elements of `sequence`. + +The [`pcase`](#pcase-exp-rest-cases) pattern will match each element of `patterns` against the +corresponding element of `sequence`. + +Extra elements of the sequence are ignored if fewer `patterns` are +given, and the match does not fail. + +-- (radix-tree-leaf `vpat`) + +Pattern which matches a radix-tree leaf. +The pattern `vpat` is matched against the leaf's carried value. + +-- (cl-struct `type` &rest `fields`) + +Pcase patterns that match cl-struct `expval` of type `type`. +Elements of `fields` can be of the form (`name` `pat`) in which case the +contents of field `name` is matched against `pat`, or they can be of +the form `name` which is a shorthand for (`name` `name`). + +-- (cl-type `type`) + +Pcase pattern that matches objects of `type`. +`type` is a type descriptor as accepted by `cl-typep`, which see. + +```el +(pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) ;; => (1 2 3 4) +(pcase (list 1 2 3 4 5 6) ((dash (a b c . d)) (progn (list a b c d)))) ;; => (1 2 3 (4 5 6)) +(pcase (list :baz 3 :foo 1 :qux 4 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) ;; => (1 2) +``` + ## Side effects Functions iterating over lists for side effect only. diff --git a/dash-pcase.el b/dash-pcase.el new file mode 100644 index 00000000..395d9339 --- /dev/null +++ b/dash-pcase.el @@ -0,0 +1,191 @@ +;;; dash-pcase.el --- A pcase pattern following `-let' -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2024 Free Software Foundation, Inc. + +;; Author: Magnar Sveen +;; Keywords: extensions, lisp +;; Homepage: https://github.com/magnars/dash.el + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; A `pcase' pattern that works like `-let'. + +;;; Code: + +(require 'dash) +(require 'pcase) + +(eval-when-compile + ;; - 24.3 started complaining about unknown `declare' props. + ;; - 25 introduced `pure' and `side-effect-free'. + ;; - 30 introduced `important-return-value'. + (when (boundp 'defun-declarations-alist) + (dolist (prop '(important-return-value pure side-effect-free)) + (unless (assq prop defun-declarations-alist) + (push (list prop #'ignore) defun-declarations-alist))))) + +(defun dash--elem-pattern (elem) + "Return a pattern for ELEM, which may be further destructured." + (declare (important-return-value t) + (side-effect-free t)) + (cond + ((sequencep elem) + `(dash ,elem)) + ((eq ?_ (aref (symbol-name elem) 0)) + '_) + (t + elem))) + +(defun dash--vect-pattern (vect) + "Return a pattern for VECT matching a vector." + (declare (important-return-value t) + (side-effect-free t)) + (cond + ((= 1 (length vect)) + `(and (pred arrayp) + (app (pcase--flip aref 0) + (dash ,(aref vect 0))))) + ((eq (aref vect 1) '&as) + `(and (pred arrayp) + (dash ,(aref vect 0)) + (dash ,(substring vect 2)))) + (t + (let ((res) + (tag (gensym))) + (catch tag + (dotimes (idx (length vect)) + (let ((it (aref vect idx))) + (if (eq it '&rest) + (progn + (push `(app (pcase--flip substring ,idx) + (dash ,(aref vect (1+ idx)))) + res) + (throw tag nil)) + (push `(app (pcase--flip aref ,idx) + (dash ,it)) + res))))) + `(and (pred arrayp) + ,@(nreverse res)))))) + +(defun dash--hash-or-null-p (x) + "Return non-nil if X is a hash table or null." + (declare (important-return-value t) + (side-effect-free t)) + (or (null x) + (hash-table-p x))) + +(defun dash--hash-or-null-get (key map) + "Return value associated with KEY in MAP." + (declare (important-return-value t) + (side-effect-free t)) + (when map + (gethash key map))) + +(defun dash--hash-or-list-p (x) + "Return non-nil if X is a hash table or a list." + (declare (important-return-value t) + (side-effect-free t)) + (or (listp x) + (hash-table-p x))) + +(defun dash--hash-or-plist-get (key map) + "Return value associated with KEY in MAP." + (declare (important-return-value t) + (side-effect-free t)) + (if (hash-table-p map) + (gethash key map) + (plist-get map key))) + +(defun dash--keyvar-pattern (list) + "Make pattern matching LIST for `&alist', `&plist', `&hash', `&hash?', and `&hash-or-plist'." + (declare (important-return-value t) + (side-effect-free t)) + (let ((res) + (test) + (getter) + (type (car list)) + (list (cdr (dash--match-kv-normalize-match-form list)))) + (cond + ((eq type '&hash) + (setq getter (lambda (key) `(gethash ,key)) + test #'hash-table-p)) + ((eq type '&hash?) + (setq getter (lambda (key) `(dash--hash-or-null-get ,key)) + ;; FIXME and TODO: Should this pattern match or not match null? + test #'dash--hash-or-null-p)) + ((eq type '&hash-or-plist) + (setq getter (lambda (key) `(dash--hash-or-plist-get ,key)) + test #'dash--hash-or-list-p)) + ((eq type '&plist) + (setq getter (lambda (key) `(pcase--flip plist-get ,key)) + test #'listp)) + ((eq type '&alist) + (setq getter (let ((sym (gensym))) + (lambda (key) + `(lambda (,sym) (cdr (assoc ,key ,sym))))) + test #'listp))) + (while list + (let ((key (pop list)) + (var (pop list))) + (push `(app ,(funcall getter key) (dash ,var)) + res))) + `(and (pred ,test) + ,@(nreverse res)))) + +(defun dash--list-pattern (list) + "Return a pattern for LIST matching a list. + +Unlike the vector pattern, the list pattern does not require the +matched expression to be long enough to bind all sub-patterns." + (declare (important-return-value t) + (side-effect-free t)) + (let ((first (car list)) + (rest (cdr list))) + (cond + ((eq first '&keys) + `(dash ,(cons '&plist rest))) + ((null list) + '_) + ((null rest) + `(and (pred listp) + (app car (dash ,first)))) + ((and (consp rest) + (eq (car rest) '&as)) + `(and (pred listp) + (dash ,first) + (dash ,(cdr rest)))) + (t + `(and (pred listp) + (app car-safe (dash ,first)) + (app cdr-safe (dash ,rest))))))) + +(pcase-defmacro dash (pat) + "Destructure EXP according to PAT like in `-let'." + (declare (important-return-value t) + (side-effect-free t)) + (cond + ((symbolp pat) + (dash--elem-pattern pat)) + ((arrayp pat) + (dash--vect-pattern pat)) + ((memq (car-safe pat) '(&plist &alist &hash &hash? &hash-or-plist)) + (dash--keyvar-pattern pat)) + ((listp pat) + (dash--list-pattern pat)) + (t (error "Invalid Dash pattern: %s" pat)))) + +(provide 'dash-pcase) +;;; dash-pcase.el ends here diff --git a/dash.texi b/dash.texi index 6f512b0c..290279d4 100644 --- a/dash.texi +++ b/dash.texi @@ -4279,6 +4279,190 @@ multiple assignments it does not cause unexpected side effects. @end example @end defmac +@anchor{pcase-let} +@defmac pcase-let (bindings &rest body) +Like @code{let}, but supports destructuring @var{bindings} using @code{pcase} (@pxref{pcase}) patterns. +@var{body} should be a list of expressions, and @var{bindings} should be a list of +bindings of the form (@var{pattern} @var{exp}). +All EXPs are evaluated first, and then used to perform destructuring +bindings by matching each @var{exp} against its respective @var{pattern}. Then +@var{body} is evaluated with those bindings in effect. + +Each @var{exp} should match its respective @var{pattern} (i.e. be of structure +compatible to @var{pattern}); a mismatch may signal an error or may go +undetected, binding variables to arbitrary values, such as @code{nil}. + +@example +@group +(pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) + @result{} (1 2 3 4) +@end group +@group +(pcase-let (((dash (a b c . d)) (list 1 2 3 4 5 6))) (list a b c d)) + @result{} (1 2 3 (4 5 6)) +@end group +@group +(pcase-let (((dash (&plist :foo foo :bar bar)) (list :baz 3 :foo 1 :qux 4 :bar 2))) (list foo bar)) + @result{} (1 2) +@end group +@end example +@end defmac + +@anchor{pcase} +@defmac pcase (exp &rest cases) +Evaluate @var{exp} to get @var{expval}; try passing control to one of @var{cases}. +@var{cases} is a list of elements of the form (@var{pattern} @var{code}@dots{}). +For the first @var{case} whose @var{pattern} "matches" @var{expval}, +evaluate its @var{code}@dots{}, and return the value of the last form. +If no @var{case} has a @var{pattern} that matches, return @code{nil}. + +Each @var{pattern} expands, in essence, to a predicate to call +on @var{expval}. When the return value of that call is non-@code{nil}, +@var{pattern} matches. @var{pattern} can take one of the forms: + + _ matches anything. + '@var{val} matches if @var{expval} is @code{equal} to @var{val}. + @var{keyword} shorthand for '@var{keyword} + @var{integer} shorthand for '@var{integer} + @var{string} shorthand for '@var{string} + @var{symbol} matches anything and binds it to @var{symbol}. + If a @var{symbol} is used twice in the same pattern + the second occurrence becomes an @code{eq}uality test. + (pred @var{fun}) matches if @var{fun} called on @var{expval} returns non-@code{nil}. + (pred (not @var{fun})) matches if @var{fun} called on @var{expval} returns @code{nil}. + (app @var{fun} @var{pat}) matches if @var{fun} called on @var{expval} matches @var{pat}. + (guard @var{boolexp}) matches if @var{boolexp} evaluates to non-@code{nil}. + (and @var{pat}@dots{}) matches if all the patterns match. + (or @var{pat}@dots{}) matches if any of the patterns matches. + +@var{fun} in @code{pred} and @code{app} can take one of the forms: + @var{symbol} or (lambda @var{args} @var{body}) + call it with one argument + (@var{f} @var{arg1} .. ARGn) + call @var{f} with @var{arg1}..ARGn and @var{expval} as n+1'th argument + +@var{fun}, @var{boolexp}, and subsequent @var{pat} can refer to variables +bound earlier in the pattern by a @var{symbol} pattern. + +Additional patterns can be defined using @code{pcase-defmacro}. + +See Info node `(elisp) Pattern-Matching Conditional' in the +Emacs Lisp manual for more information and examples. + +-- `@var{qpat} + +Backquote-style pcase patterns: `@var{qpat} +@var{qpat} can take the following forms: + (@var{qpat1} . @var{qpat2}) matches if @var{qpat1} matches the car and @var{qpat2} the cdr. + [@var{qpat1} @var{qpat2}..QPATn] matches a vector of length n and @var{qpat1}..QPATn match + its 0..(n-1)th elements, respectively. + ,@var{pat} matches if the @code{pcase} (@pxref{pcase}) pattern @var{pat} matches. + @var{symbol} matches if @var{expval} is @code{equal} to @var{symbol}. + @var{keyword} likewise for @var{keyword}. + @var{number} likewise for @var{number}. + @var{string} likewise for @var{string}. + +The list or vector @var{qpat} is a template. The predicate formed +by a backquote-style pattern is a combination of those +formed by any sub-patterns, wrapped in a top-level condition: +@var{expval} must be "congruent" with the template. For example: + + `(technical ,forum) + +The predicate is the logical-@var{and} of: + - Is @var{expval} a list of two elements? + - Is the first element the symbol @code{technical}? + - True! (The second element can be anything, and for the sake + of the body forms, its value is bound to the symbol @code{forum}.) + +-- (rx &rest @var{regexps}) + +@var{a} pattern that matches strings against @code{rx} @var{regexps} in sexp form. +@var{regexps} are interpreted as in @code{rx}. The pattern matches any +string that is a match for @var{regexps}, as if by @code{string-match}. + +In addition to the usual @code{rx} syntax, @var{regexps} can contain the +following constructs: + + (let @var{ref} @var{rx}@dots{}) binds the symbol @var{ref} to a submatch that matches + the regular expressions @var{rx}. @var{ref} is bound in + @var{code} to the string of the submatch or @code{nil}, but + can also be used in @code{backref}. + (backref @var{ref}) matches whatever the submatch @var{ref} matched. + @var{ref} can be a number, as usual, or a name + introduced by a previous (let @var{ref} @dots{}) + construct. + +-- (dash @var{pat}) + +Destructure @var{exp} according to @var{pat} like in @code{-let} (@pxref{-let}). + +-- (let @var{pat} @var{expr}) + +Matches if @var{expr} matches @var{pat}. + +-- (map &rest @var{args}) + +Build a @code{pcase} (@pxref{pcase}) pattern matching map elements. + +@var{args} is a list of elements to be matched in the map. + +Each element of @var{args} can be of the form (@var{key} @var{pat}), in which case @var{key} is +evaluated and searched for in the map. The match fails if for any @var{key} +found in the map, the corresponding @var{pat} doesn't match the value +associated with the @var{key}. + +Each element can also be a @var{symbol}, which is an abbreviation of +a (@var{key} @var{pat}) tuple of the form ('@var{symbol} @var{symbol}). When @var{symbol} +is a keyword, it is an abbreviation of the form (:@var{symbol} @var{symbol}), +useful for binding plist values. + +Keys in @var{args} not found in the map are ignored, and the match doesn't +fail. + +-- (seq &rest @var{patterns}) + +Build a @code{pcase} (@pxref{pcase}) pattern that matches elements of @var{sequence}. + +The @code{pcase} (@pxref{pcase}) pattern will match each element of @var{patterns} against the +corresponding element of @var{sequence}. + +Extra elements of the sequence are ignored if fewer @var{patterns} are +given, and the match does not fail. + +-- (radix-tree-leaf @var{vpat}) + +Pattern which matches a radix-tree leaf. +The pattern @var{vpat} is matched against the leaf's carried value. + +-- (cl-struct @var{type} &rest @var{fields}) + +Pcase patterns that match cl-struct @var{expval} of type @var{type}. +Elements of @var{fields} can be of the form (@var{name} @var{pat}) in which case the +contents of field @var{name} is matched against @var{pat}, or they can be of +the form @var{name} which is a shorthand for (@var{name} @var{name}). + +-- (cl-type @var{type}) + +Pcase pattern that matches objects of @var{type}. +@var{type} is a type descriptor as accepted by @code{cl-typep}, which see. + +@example +@group +(pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) + @result{} (1 2 3 4) +@end group +@group +(pcase (list 1 2 3 4 5 6) ((dash (a b c . d)) (progn (list a b c d)))) + @result{} (1 2 3 (4 5 6)) +@end group +@group +(pcase (list :baz 3 :foo 1 :qux 4 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) + @result{} (1 2) +@end group +@end example +@end defmac + @node Side effects @section Side effects diff --git a/dev/examples.el b/dev/examples.el index 291b37dc..8444927b 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -27,6 +27,7 @@ (require 'dash) (require 'dash-defs "dev/dash-defs") +(require 'dash-pcase) (require 'ert) (eval-when-compile @@ -2367,7 +2368,365 @@ or readability." (-setq (a b (&plist 'x x 'y y)) '(1 2 (x 3 y 4)) z x)) => 3 ;; FIXME: Byte-compiler chokes on this in Emacs < 26. - (eval '(let (a) (-setq a)) t) !!> wrong-number-of-arguments)) + (eval '(let (a) (-setq a)) t) !!> wrong-number-of-arguments) + + (defexamples pcase-let + (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a b c . d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 (4 5 6)) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :baz 3 :foo 1 :qux 4 :bar 2))) (list foo bar)) => '(1 2) + (let ((a (list 1 2 3)) + (b (list 'a 'b 'c))) + (pcase-let (((dash (a . b)) a) ((dash (c . d)) b)) (list a b c d))) => '(1 (2 3) a (b c)) + (pcase-let (((dash a) "foo") ((dash b) "bar")) (list a b)) => '("foo" "bar") + (pcase-let (((dash foo) (list 1 2 3))) foo) => '(1 2 3) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo (a b) :bar c)) (list :foo (list 1 2) :bar 3))) (list a b c)) => '(1 2 3) + ;; nil value in plist means subsequent cons matches are nil, because + ;; (car nil) => nil + (pcase-let (((dash (&plist :foo (a b))) (list :bar 1))) (list a b)) => '(nil nil) + (pcase-let (((dash (&plist :foo (&plist :baz baz) :bar bar)) (list :foo (list 1 2 :baz 2 :bar 4) :bar 3))) (list baz bar)) => '(2 3) + (pcase-let (((dash (_ (&plist :level level :title title))) (list 'paragraph (list :title "foo" :level 2)))) (list level title)) => '(2 "foo") + (pcase-let (((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))))) (list bar face inv)) => '(2 foo-face t) + (pcase-let (((dash (a (b c) d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash [a _ c]) [1 2 3 4])) (list a c)) => '(1 3) + (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 + (pcase-let (((dash [a _ _ _ b]) (vector 1 2 3 4 5))) (list a b)) => '(1 5) + (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash [a b c]) (string 102 111 98 97 114))) (list a b c)) => '(?f ?o ?b) + (pcase-let (((dash [a b c]) "abcdef")) (list a b c)) => '(?a ?b ?c) + (pcase-let (((dash [a (b [c]) d]) [1 (2 [3 4]) 5 6])) (list a b c d)) => '(1 2 3 5) + (pcase-let (((dash (a b c d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash ([a b])) (list (vector 1 2 3)))) (list a b)) => '(1 2) + ;; d is bound to nil. I don't think we want to error in such a case. + ;; After all (car nil) => nil + (pcase-let (((dash (a b c d)) (list 1 2 3))) (list a b c d)) => '(1 2 3 nil) + (pcase-let (((dash [a b c]) [1 2 3 4])) (list a b c)) => '(1 2 3) + (pcase-let (((dash [a]) [1 2 3 4])) a) => 1 + (pcase-let (((dash [a b &rest c]) "abcdef")) (list a b c)) => '(?a ?b "cdef") + (pcase-let (((dash [a b &rest c]) [1 2 3 4 5 6])) (list a b c)) => '(1 2 [3 4 5 6]) + (pcase-let (((dash [a b &rest [c d]]) [1 2 3 4 5 6])) (list a b c d)) => '(1 2 3 4) + ;; here we error, because "vectors" are rigid, immutable structures, + ;; so we should know how many elements there are + (pcase-let (((dash [a b c d]) [1 2 3])) (+ a b c d)) !!> args-out-of-range + (pcase-let (((dash (a b . c)) (cons 1 (cons 2 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (_ _ . [a b])) (cons 1 (cons 2 (vector 3 4))))) (list a b)) => '(3 4) + (pcase-let (((dash (_ _ a b)) (cons 1 (cons 2 (list 3 4))))) (list a b)) => '(3 4) + (pcase-let (((dash ([a b] _ _ c)) (list (vector 1 2) 3 4 5))) (list a b c)) => '(1 2 5) + ;; final cdr optimization + (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 + (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (((a b) . c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 (3) (4)) + (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (((a b) . c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 (3)) + ;; cdr-skip optimization + (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 + (pcase-let (((dash (_ (a))) (list 1 (list 2)))) a) => 2 + (pcase-let (((dash (_ _ _ a)) (list 1 2 3 4 5))) a) => 4 + (pcase-let (((dash (_ _ _ (a b))) (list 1 2 3 (list 4 5)))) (list a b)) => '(4 5) + (pcase-let (((dash (_ a _ b)) (list 1 2 3 4 5))) (list a b)) => '(2 4) + (pcase-let (((dash (_ a _ b _ c)) (list 1 2 3 4 5 6))) (list a b c)) => '(2 4 6) + (pcase-let (((dash (_ a _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 4 8) + (pcase-let (((dash (_ a _ _ _ b _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 6 8) + (pcase-let (((dash (_ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c)) => '(4 8 12) + (pcase-let (((dash (_ (a b) _ c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 5) + (pcase-let (((dash (_ (a b) _ . c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 (5)) + (pcase-let (((dash (_ (a b) _ (c d))) (list 1 (list 2 3) 4 (list 5 6)))) (list a b c d)) => '(2 3 5 6) + (pcase-let (((dash (_ (a b) _ _ _ (c d))) (list 1 (list 2 3) 4 5 6 (list 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ (a b) _ c d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(2 3 5 6) + (pcase-let (((dash (_ (a b) _ _ _ [c d])) (list 1 (list 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ [a b] _ _ _ [c d])) (list 1 (vector 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ _ _ . a)) (list 1 2 3 4 5))) a) => '(4 5) + (pcase-let (((dash (_ a _ _)) (list 1 2 3 4 5))) a) => 2 + (pcase-let (((dash (_ . b)) (cons 1 2))) b) => 2 + (pcase-let (((dash ([a b c d] . e)) (cons (vector 1 2 3 4) 5))) (list a b c d e)) => '(1 2 3 4 5) + (pcase-let (((dash ([a b c d] _ . e)) (cons (vector 1 2 3 4) (cons 5 6)))) (list a b c d e)) => '(1 2 3 4 6) + ;; late-binding optimization + (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 + (pcase-let (((dash (((&plist :foo a :bar b)))) (list (list (list :bar 1 :foo 2) 3) 4))) (list a b)) => '(2 1) + (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (((a b) c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 (4)) + (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (a b c d)) (list 1 2 3 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a)) (list 1 2 3 4))) (list a)) => '(1) + (pcase-let (((dash (_ a)) (list 1 2 3 4))) (list a)) => '(2) + (pcase-let (((dash (_ _ a)) (list 1 2 3 4))) (list a)) => '(3) + (pcase-let (((dash (_ _ . a)) (list 1 2 3 4))) a) => '(3 4) + (pcase-let (((dash (_ _ [a b])) (list 1 2 (vector 3 4)))) (list a b)) => '(3 4) + (pcase-let (((dash (a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(1 4) + (pcase-let (((dash (_ _ a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(3 6) + (pcase-let (((dash (_ _ a _ _ . b)) (list 1 2 3 4 5 6 7 8))) (cons a b)) => '(3 6 7 8) + (pcase-let (((dash (_ a _ b)) (list 1 2 3 4))) (list a b)) => '(2 4) + (pcase-let (((dash (a b c (d e))) (list 1 2 3 (list 4 5)))) (list a b c d e)) => '(1 2 3 4 5) + (pcase-let (((dash (_ _ (_ _ (_ _ a)))) (list 1 2 (list 3 4 (list 5 6 7))))) a) => 7 + (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 + (pcase-let (((dash (_ _ &plist :foo a :bar b)) (list 1 2 :bar 2 :foo 1))) (list a b)) => '(1 2) + ;; &keys support + (pcase-let (((dash (_ _ &keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) + (pcase-let (((dash (a _ &keys :foo b :bar c)) (list 1 2 :bar 4 :foo 3))) (list a b c)) => '(1 3 4) + (pcase-let (((dash (a _ _ _ &keys :foo b :bar c)) (list 1 2 3 4 :bar 6 :foo 5))) (list a b c)) => '(1 5 6) + (pcase-let (((dash (a b &keys :foo c :bar d)) (list 1 2 :bar 4 :foo 3))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a b &keys)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(1 2) + (pcase-let (((dash (&keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) + (pcase-let (((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9))) (list a b c d e f g h i)) => '(1 2 3 4 5 6 7 8 9) + ;; single-binding optimization for vectors and kv + (pcase-let (((dash [_ [_ [_ a]]]) (vector 1 (vector 2 (vector 3 4))))) a) => 4 + (pcase-let (((dash [a _ _ _]) (vector 1 2 3 4))) a) => 1 + (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 + (pcase-let (((dash [_ _ a _]) (vector 1 2 3 4))) a) => 3 + (pcase-let (((dash [a [_ [_ b]]]) (vector 1 (vector 2 (vector 3 4))))) (list a b)) => '(1 4) + (pcase-let (((dash [(a _ b)]) (vector (list 1 2 3 4)))) (list a b)) => '(1 3) + (pcase-let (((dash (&plist 'a a)) (list 'a 1 'b 2))) a) => 1 + (pcase-let (((dash (&plist 'a [a b])) (list 'a [1 2] 'b 3))) (list a b)) => '(1 2) + (pcase-let (((dash (&plist 'a [a b] 'c c)) (list 'a [1 2] 'c 3))) (list a b c)) => '(1 2 3) + ;; mixing dot and &alist + (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) + (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) + (pcase-let (((dash (x y (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list a c)) => '(b d) + ;; test bindings with no explicit val + (pcase-let (((dash a) nil)) a) => nil + (pcase-let (((dash a) nil)) a) => nil + (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) + (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) + ;; auto-derived match forms for kv destructuring + ;;; test that we normalize all the supported kv stores + (pcase-let (((dash (&plist :foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&alist :foo :bar)) (list (cons :foo 1) (cons :bar 2)))) (list foo bar)) => '(1 2) + (let ((hash (make-hash-table))) + (puthash :foo 1 hash) + (puthash :bar 2 hash) + (pcase-let (((dash (&hash :foo :bar)) hash)) (list foo bar))) => '(1 2) + (pcase-let (((dash (&hash :foo (&hash? :bar))) (make-hash-table))) bar) => nil + ;; Ensure `hash?' expander evaluates its arg only once + (let* ((ht (make-hash-table :test #'equal)) + (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) + (puthash 'a nil ht) + (pcase-let (((dash (&hash? 'a)) (funcall fn ht))) a)) => '(3) + (pcase-let (((dash (_ &keys :foo :bar)) (list 'ignored :foo 1 :bar 2))) (list foo bar)) => '(1 2) + ;;; go over all the variations of match-form derivation + (pcase-let (((dash (&plist :foo foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo x :bar y)) (list :foo 1 :bar 2))) (list x y)) => '(1 2) + (pcase-let (((dash (&plist :foo (x) :bar [y])) (list :foo (list 1) :bar (vector 2)))) (list x y)) => '(1 2) + (pcase-let (((dash (&plist 'foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo foo 'bar bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo x 'bar y)) (list 'foo 1 'bar 2))) (list x y)) => '(1 2) + (pcase-let (((dash (&alist "foo" "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&alist "foo" x "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list x bar)) => '(1 2) + (pcase-let (((dash (&alist "foo" x "bar" y)) (list (cons "foo" 1) (cons "bar" 2)))) (list x y)) => '(1 2) + (pcase-let (((dash (&alist :a 'b "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist 'b :a "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist 'b "c" :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist "c" 'b :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist "c" :a 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist :a "c" 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + ;; FIXME: Byte-compiler chokes on these in Emacs < 26, which were + ;; for `-let'. + (eval '(pcase-let (((dash (&plist 'foo 1)) (list 'foo 'bar))) (list foo))) !!> error + (eval '(pcase-let (((dash (&plist foo :bar)) (list :foo :bar))) (list foo))) !!> error + ;; test the &as form + (pcase-let (((dash (items &as first . rest)) (list 1 2 3))) (list first rest items)) => '(1 (2 3) (1 2 3)) + (pcase-let (((dash (all &as [vect &as a b] bar)) (list [1 2] 3))) (list a b bar vect all)) => '(1 2 3 [1 2] ([1 2] 3)) + (pcase-let (((dash (all &as (list &as a b) bar)) (list (list 1 2) 3))) (list a b bar list all)) => '(1 2 3 (1 2) ((1 2) 3)) + (pcase-let (((dash (x &as [a b])) (list (vector 1 2 3)))) (list a b x)) => '(1 2 ([1 2 3])) + (pcase-let (((dash (result &as [_ a] [_ b])) (list [1 2] [3 4]))) (list a b result)) => '(2 4 ([1 2] [3 4])) + (pcase-let (((dash (result &as [fst &as _ a] [snd &as _ b])) (list [1 2] [3 4]))) (list a b fst snd result)) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) + (pcase-let (((dash [x &as a b &rest r]) (vector 1 2 3))) (list a b r x)) => '(1 2 [3] [1 2 3]) + (pcase-let (((dash [x &as a]) (vector 1 2 3))) (list a x)) => '(1 [1 2 3]) + (pcase-let (((dash [x &as _ _ a]) (vector 1 2 3))) (list a x)) => '(3 [1 2 3]) + (pcase-let (((dash [x &as _ _ a]) (vector 1 2 (list 3 4)))) (list a x)) => '((3 4) [1 2 (3 4)]) + (pcase-let (((dash [x &as _ _ (a b)]) (vector 1 2 (list 3 4)))) (list a b x)) => '(3 4 [1 2 (3 4)]) + (pcase-let (((dash (b &as beg . end)) (cons 1 2))) (list beg end b)) => '(1 2 (1 . 2)) + (pcase-let (((dash (plist &as &plist :a a :b b)) (list :a 1 :b 2))) (list a b plist)) => '(1 2 (:a 1 :b 2)) + (pcase-let (((dash (alist &as &alist :a a :b b)) (list (cons :a 1) (cons :b 2)))) (list a b alist)) => '(1 2 ((:a . 1) (:b . 2))) + (pcase-let (((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c list)) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) + (pcase-let (((dash (x &as a b)) (list 1 2)) ((dash (y &as c d)) (list 3 4))) (list a b c d x y)) + => '(1 2 3 4 (1 2) (3 4)) + (pcase-let (((dash (&hash-or-plist :key)) (--doto (make-hash-table) (puthash :key "value" it)))) key) + => "value" + (pcase-let (((dash (&hash-or-plist :key)) '(:key "value"))) key) + => "value") + + (defexamples pcase + (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 3 4 5 6) ((dash (a b c . d)) (progn (list a b c d)))) => '(1 2 3 (4 5 6)) + (pcase (list :baz 3 :foo 1 :qux 4 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase "foo" ((dash a) (pcase "bar" ((dash b) (progn (list a b)))))) => '("foo" "bar") + (pcase (list 1 2 3) ((dash foo) (progn foo))) => '(1 2 3) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo (list 1 2) :bar 3) ((dash (&plist :foo (a b) :bar c)) (progn (list a b c)))) => '(1 2 3) + ;; nil value in plist means subsequent cons matches are nil, because + ;; (car nil) => nil + (pcase (list :bar 1) ((dash (&plist :foo (a b))) (progn (list a b)))) => '(nil nil) + (pcase (list :foo (list 1 2 :baz 2 :bar 4) :bar 3) ((dash (&plist :foo (&plist :baz baz) :bar bar)) (progn (list baz bar)))) => '(2 3) + (pcase (list 'paragraph (list :title "foo" :level 2)) ((dash (_ (&plist :level level :title title))) (progn (list level title)))) => '(2 "foo") + (pcase (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))) ((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (progn (list bar face inv)))) => '(2 foo-face t) + (pcase (list 1 (list 2 3) 4 5 6) ((dash (a (b c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase [1 2 3 4] ((dash [a _ c]) (progn (list a c)))) => '(1 3) + (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 + (pcase (vector 1 2 3 4 5) ((dash [a _ _ _ b]) (progn (list a b)))) => '(1 5) + (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (string 102 111 98 97 114) ((dash [a b c]) (progn (list a b c)))) => '(?f ?o ?b) + (pcase "abcdef" ((dash [a b c]) (progn (list a b c)))) => '(?a ?b ?c) + (pcase [1 (2 [3 4]) 5 6] ((dash [a (b [c]) d]) (progn (list a b c d)))) => '(1 2 3 5) + (pcase (list 1 2 3 4 5 6) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (vector 1 2 3)) ((dash ([a b])) (progn (list a b)))) => '(1 2) + ;; d is bound to nil. I don't think we want to error in such a case. + ;; After all (car nil) => nil + (pcase (list 1 2 3) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 nil) + (pcase [1 2 3 4] ((dash [a b c]) (progn (list a b c)))) => '(1 2 3) + (pcase [1 2 3 4] ((dash [a]) (progn a))) => 1 + (pcase "abcdef" ((dash [a b &rest c]) (progn (list a b c)))) => '(?a ?b "cdef") + (pcase [1 2 3 4 5 6] ((dash [a b &rest c]) (progn (list a b c)))) => '(1 2 [3 4 5 6]) + (pcase [1 2 3 4 5 6] ((dash [a b &rest [c d]]) (progn (list a b c d)))) => '(1 2 3 4) + ;; here we error, because "vectors" are rigid, immutable structures, + ;; so we should know how many elements there are + (pcase [1 2 3] ((dash [a b c d]) (progn (+ a b c d)))) !!> args-out-of-range + (pcase (cons 1 (cons 2 3)) ((dash (a b . c)) (progn (list a b c)))) => '(1 2 3) + (pcase (cons 1 (cons 2 (vector 3 4))) ((dash (_ _ . [a b])) (progn (list a b)))) => '(3 4) + (pcase (cons 1 (cons 2 (list 3 4))) ((dash (_ _ a b)) (progn (list a b)))) => '(3 4) + (pcase (list (vector 1 2) 3 4 5) ((dash ([a b] _ _ c)) (progn (list a b c)))) => '(1 2 5) + ;; final cdr optimization + (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c) . d)) (progn (list a b c d)))) => '(1 2 (3) (4)) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c))) (progn (list a b c)))) => '(1 2 (3)) + ;; cdr-skip optimization + (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 + (pcase (list 1 (list 2)) ((dash (_ (a))) (progn a))) => 2 + (pcase (list 1 2 3 4 5) ((dash (_ _ _ a)) (progn a))) => 4 + (pcase (list 1 2 3 (list 4 5)) ((dash (_ _ _ (a b))) (progn (list a b)))) => '(4 5) + (pcase (list 1 2 3 4 5) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) + (pcase (list 1 2 3 4 5 6) ((dash (_ a _ b _ c)) (progn (list a b c)))) => '(2 4 6) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ b _ _ _ c)) (progn (list a b c)))) => '(2 4 8) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ _ _ b _ c)) (progn (list a b c)))) => '(2 6 8) + (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (_ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c)))) => '(4 8 12) + (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ c)) (progn (list a b c)))) => '(2 3 5) + (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ . c)) (progn (list a b c)))) => '(2 3 (5)) + (pcase (list 1 (list 2 3) 4 (list 5 6)) ((dash (_ (a b) _ (c d))) (progn (list a b c d)))) => '(2 3 5 6) + (pcase (list 1 (list 2 3) 4 5 6 (list 7 8)) ((dash (_ (a b) _ _ _ (c d))) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 (list 2 3) 4 5 6) ((dash (_ (a b) _ c d)) (progn (list a b c d)))) => '(2 3 5 6) + (pcase (list 1 (list 2 3) 4 5 6 (vector 7 8)) ((dash (_ (a b) _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 (vector 2 3) 4 5 6 (vector 7 8)) ((dash (_ [a b] _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 2 3 4 5) ((dash (_ _ _ . a)) (progn a))) => '(4 5) + (pcase (list 1 2 3 4 5) ((dash (_ a _ _)) (progn a))) => 2 + (pcase (cons 1 2) ((dash (_ . b)) (progn b))) => 2 + (pcase (cons (vector 1 2 3 4) 5) ((dash ([a b c d] . e)) (progn (list a b c d e)))) => '(1 2 3 4 5) + (pcase (cons (vector 1 2 3 4) (cons 5 6)) ((dash ([a b c d] _ . e)) (progn (list a b c d e)))) => '(1 2 3 4 6) + ;; late-binding optimization + (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 + (pcase (list (list (list :bar 1 :foo 2) 3) 4) ((dash (((&plist :foo a :bar b)))) (progn (list a b)))) => '(2 1) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) . d)) (progn (list a b c d)))) => '(1 2 3 (4)) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) + (pcase (list 1 2 3 4) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 3 4) ((dash (a)) (progn (list a)))) => '(1) + (pcase (list 1 2 3 4) ((dash (_ a)) (progn (list a)))) => '(2) + (pcase (list 1 2 3 4) ((dash (_ _ a)) (progn (list a)))) => '(3) + (pcase (list 1 2 3 4) ((dash (_ _ . a)) (progn a))) => '(3 4) + (pcase (list 1 2 (vector 3 4)) ((dash (_ _ [a b])) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (a _ _ b)) (progn (list a b)))) => '(1 4) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ b)) (progn (list a b)))) => '(3 6) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ . b)) (progn (cons a b)))) => '(3 6 7 8) + (pcase (list 1 2 3 4) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) + (pcase (list 1 2 3 (list 4 5)) ((dash (a b c (d e))) (progn (list a b c d e)))) => '(1 2 3 4 5) + (pcase (list 1 2 (list 3 4 (list 5 6 7))) ((dash (_ _ (_ _ (_ _ a)))) (progn a))) => 7 + (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 + (pcase (list 1 2 :bar 2 :foo 1) ((dash (_ _ &plist :foo a :bar b)) (progn (list a b)))) => '(1 2) + ;; &keys support + (pcase (list 1 2 :bar 4 :foo 3) ((dash (_ _ &keys :foo a :bar b)) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 3 4) + (pcase (list 1 2 3 4 :bar 6 :foo 5) ((dash (a _ _ _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 5 6) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys :foo c :bar d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys)) (progn (list a b)))) => '(1 2) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (&keys :foo a :bar b)) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9) ((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (progn (list a b c d e f g h i)))) => '(1 2 3 4 5 6 7 8 9) + ;; single-binding optimization for vectors and kv + (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [_ [_ [_ a]]]) (progn a))) => 4 + (pcase (vector 1 2 3 4) ((dash [a _ _ _]) (progn a))) => 1 + (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 + (pcase (vector 1 2 3 4) ((dash [_ _ a _]) (progn a))) => 3 + (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [a [_ [_ b]]]) (progn (list a b)))) => '(1 4) + (pcase (vector (list 1 2 3 4)) ((dash [(a _ b)]) (progn (list a b)))) => '(1 3) + (pcase (list 'a 1 'b 2) ((dash (&plist 'a a)) (progn a))) => 1 + (pcase (list 'a [1 2] 'b 3) ((dash (&plist 'a [a b])) (progn (list a b)))) => '(1 2) + (pcase (list 'a [1 2] 'c 3) ((dash (&plist 'a [a b] 'c c)) (progn (list a b c)))) => '(1 2 3) + ;; mixing dot and &alist + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) + (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (x y (&alist 'a a 'c c))) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (_ _ (&alist 'a a 'c c))) (progn (list a c)))) => '(b d) + ;; test bindings with no explicit val + (pcase nil ((dash a) (progn a))) => nil + (pcase nil ((dash a) (progn a))) => nil + (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) + (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) + ;; auto-derived match forms for kv destructuring + ;;; test that we normalize all the supported kv stores + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo :bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list (cons :foo 1) (cons :bar 2)) ((dash (&alist :foo :bar)) (progn (list foo bar)))) => '(1 2) + (let ((hash (make-hash-table))) + (puthash :foo 1 hash) + (puthash :bar 2 hash) + (pcase hash ((dash (&hash :foo :bar)) (progn (list foo bar))))) => '(1 2) + (pcase (make-hash-table) ((dash (&hash :foo (&hash? :bar))) (progn bar))) => nil + ;; Ensure `hash?' expander evaluates its arg only once + (let* ((ht (make-hash-table :test #'equal)) + (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) + (puthash 'a nil ht) + (pcase (funcall fn ht) ((dash (&hash? 'a)) (progn a)))) => '(3) + (pcase (list 'ignored :foo 1 :bar 2) ((dash (_ &keys :foo :bar)) (progn (list foo bar)))) => '(1 2) + ;;; go over all the variations of match-form derivation + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo x :bar y)) (progn (list x y)))) => '(1 2) + (pcase (list :foo (list 1) :bar (vector 2)) ((dash (&plist :foo (x) :bar [y])) (progn (list x y)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo 'bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo x 'bar y)) (progn (list x y)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" "bar")) (progn (list foo bar)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar")) (progn (list x bar)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar" y)) (progn (list x y)))) => '(1 2) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a 'b "c")) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b :a "c")) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b "c" :a)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" 'b :a)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" :a 'b)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a "c" 'b)) (progn (list a b c)))) => '(1 2 3) + ;; FIXME: Byte-compiler chokes on these in Emacs < 26. + (eval '(pcase (list 'foo 'bar) ((dash (&plist 'foo 1)) (progn (list foo))))) !!> error + (eval '(pcase (list :foo :bar) ((dash (&plist foo :bar)) (progn (list foo)))) t) !!> error + ;; test the &as form + (pcase (list 1 2 3) ((dash (items &as first . rest)) (progn (list first rest items)))) => '(1 (2 3) (1 2 3)) + (pcase (list [1 2] 3) ((dash (all &as [vect &as a b] bar)) (progn (list a b bar vect all)))) => '(1 2 3 [1 2] ([1 2] 3)) + (pcase (list (list 1 2) 3) ((dash (all &as (list &as a b) bar)) (progn (list a b bar list all)))) => '(1 2 3 (1 2) ((1 2) 3)) + (pcase (list (vector 1 2 3)) ((dash (x &as [a b])) (progn (list a b x)))) => '(1 2 ([1 2 3])) + (pcase (list [1 2] [3 4]) ((dash (result &as [_ a] [_ b])) (progn (list a b result)))) => '(2 4 ([1 2] [3 4])) + (pcase (list [1 2] [3 4]) ((dash (result &as [fst &as _ a] [snd &as _ b])) (progn (list a b fst snd result)))) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) + (pcase (vector 1 2 3) ((dash [x &as a b &rest r]) (progn (list a b r x)))) => '(1 2 [3] [1 2 3]) + (pcase (vector 1 2 3) ((dash [x &as a]) (progn (list a x)))) => '(1 [1 2 3]) + (pcase (vector 1 2 3) ((dash [x &as _ _ a]) (progn (list a x)))) => '(3 [1 2 3]) + (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ a]) (progn (list a x)))) => '((3 4) [1 2 (3 4)]) + (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ (a b)]) (progn (list a b x)))) => '(3 4 [1 2 (3 4)]) + (pcase (cons 1 2) ((dash (b &as beg . end)) (progn (list beg end b)))) => '(1 2 (1 . 2)) + (pcase (list :a 1 :b 2) ((dash (plist &as &plist :a a :b b)) (progn (list a b plist)))) => '(1 2 (:a 1 :b 2)) + (pcase (list (cons :a 1) (cons :b 2)) ((dash (alist &as &alist :a a :b b)) (progn (list a b alist)))) => '(1 2 ((:a . 1) (:b . 2))) + (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c list)))) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) + (pcase (list 1 2) ((dash (x &as a b)) (pcase (list 3 4) ((dash (y &as c d)) (progn (list a b c d x y)))))) + => '(1 2 3 4 (1 2) (3 4)) + (pcase (--doto (make-hash-table) (puthash :key "value" it)) ((dash (&hash-or-plist :key)) (progn key))) + => "value" + (pcase '(:key "value") ((dash (&hash-or-plist :key)) (progn key))) + => "value") + + ) (def-example-group "Side effects" "Functions iterating over lists for side effect only." From f28df37b06bf9020562accf690e41c479d5ede21 Mon Sep 17 00:00:00 2001 From: okamsn Date: Sun, 28 Jul 2024 15:36:24 -0400 Subject: [PATCH 2/2] Update based on failed tests. - Create a substitute for `gensym` if `gensym` is not available. - Warn that `dash-pcase` not supported in Emacs versions less than 25.1. - Only run tests if `pcase-defmacro` is bound. --- dash-pcase.el | 38 +-- dev/examples.el | 713 ++++++++++++++++++++++++------------------------ 2 files changed, 378 insertions(+), 373 deletions(-) diff --git a/dash-pcase.el b/dash-pcase.el index 395d9339..102a9d91 100644 --- a/dash-pcase.el +++ b/dash-pcase.el @@ -37,6 +37,10 @@ (unless (assq prop defun-declarations-alist) (push (list prop #'ignore) defun-declarations-alist))))) +(if (fboundp 'gensym) + (defalias 'pcase-dash--gensym 'gensym) + (defalias 'pcase-dash--gensym 'dash--match-make-source-symbol)) + (defun dash--elem-pattern (elem) "Return a pattern for ELEM, which may be further destructured." (declare (important-return-value t) @@ -64,7 +68,7 @@ (dash ,(substring vect 2)))) (t (let ((res) - (tag (gensym))) + (tag (pcase-dash--gensym))) (catch tag (dotimes (idx (length vect)) (let ((it (aref vect idx))) @@ -133,7 +137,7 @@ (setq getter (lambda (key) `(pcase--flip plist-get ,key)) test #'listp)) ((eq type '&alist) - (setq getter (let ((sym (gensym))) + (setq getter (let ((sym (pcase-dash--gensym))) (lambda (key) `(lambda (,sym) (cdr (assoc ,key ,sym))))) test #'listp))) @@ -172,20 +176,22 @@ matched expression to be long enough to bind all sub-patterns." (app car-safe (dash ,first)) (app cdr-safe (dash ,rest))))))) -(pcase-defmacro dash (pat) - "Destructure EXP according to PAT like in `-let'." - (declare (important-return-value t) - (side-effect-free t)) - (cond - ((symbolp pat) - (dash--elem-pattern pat)) - ((arrayp pat) - (dash--vect-pattern pat)) - ((memq (car-safe pat) '(&plist &alist &hash &hash? &hash-or-plist)) - (dash--keyvar-pattern pat)) - ((listp pat) - (dash--list-pattern pat)) - (t (error "Invalid Dash pattern: %s" pat)))) +(if (fboundp 'pcase-defmacro) + (pcase-defmacro dash (pat) + "Destructure EXP according to PAT like in `-let'." + (declare (important-return-value t) + (side-effect-free t)) + (cond + ((symbolp pat) + (dash--elem-pattern pat)) + ((arrayp pat) + (dash--vect-pattern pat)) + ((memq (car-safe pat) '(&plist &alist &hash &hash? &hash-or-plist)) + (dash--keyvar-pattern pat)) + ((listp pat) + (dash--list-pattern pat)) + (t (error "Invalid Dash pattern: %s" pat)))) + (warn "`dash-pcase' does not support Emacs versions less than 25.1")) (provide 'dash-pcase) ;;; dash-pcase.el ends here diff --git a/dev/examples.el b/dev/examples.el index 8444927b..9008883a 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -2370,363 +2370,362 @@ or readability." ;; FIXME: Byte-compiler chokes on this in Emacs < 26. (eval '(let (a) (-setq a)) t) !!> wrong-number-of-arguments) - (defexamples pcase-let - (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash (a b c . d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 (4 5 6)) - (pcase-let (((dash (&plist :foo foo :bar bar)) (list :baz 3 :foo 1 :qux 4 :bar 2))) (list foo bar)) => '(1 2) - (let ((a (list 1 2 3)) - (b (list 'a 'b 'c))) - (pcase-let (((dash (a . b)) a) ((dash (c . d)) b)) (list a b c d))) => '(1 (2 3) a (b c)) - (pcase-let (((dash a) "foo") ((dash b) "bar")) (list a b)) => '("foo" "bar") - (pcase-let (((dash foo) (list 1 2 3))) foo) => '(1 2 3) - (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist :foo (a b) :bar c)) (list :foo (list 1 2) :bar 3))) (list a b c)) => '(1 2 3) - ;; nil value in plist means subsequent cons matches are nil, because - ;; (car nil) => nil - (pcase-let (((dash (&plist :foo (a b))) (list :bar 1))) (list a b)) => '(nil nil) - (pcase-let (((dash (&plist :foo (&plist :baz baz) :bar bar)) (list :foo (list 1 2 :baz 2 :bar 4) :bar 3))) (list baz bar)) => '(2 3) - (pcase-let (((dash (_ (&plist :level level :title title))) (list 'paragraph (list :title "foo" :level 2)))) (list level title)) => '(2 "foo") - (pcase-let (((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))))) (list bar face inv)) => '(2 foo-face t) - (pcase-let (((dash (a (b c) d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash [a _ c]) [1 2 3 4])) (list a c)) => '(1 3) - (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 - (pcase-let (((dash [a _ _ _ b]) (vector 1 2 3 4 5))) (list a b)) => '(1 5) - (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash [a b c]) (string 102 111 98 97 114))) (list a b c)) => '(?f ?o ?b) - (pcase-let (((dash [a b c]) "abcdef")) (list a b c)) => '(?a ?b ?c) - (pcase-let (((dash [a (b [c]) d]) [1 (2 [3 4]) 5 6])) (list a b c d)) => '(1 2 3 5) - (pcase-let (((dash (a b c d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash ([a b])) (list (vector 1 2 3)))) (list a b)) => '(1 2) - ;; d is bound to nil. I don't think we want to error in such a case. - ;; After all (car nil) => nil - (pcase-let (((dash (a b c d)) (list 1 2 3))) (list a b c d)) => '(1 2 3 nil) - (pcase-let (((dash [a b c]) [1 2 3 4])) (list a b c)) => '(1 2 3) - (pcase-let (((dash [a]) [1 2 3 4])) a) => 1 - (pcase-let (((dash [a b &rest c]) "abcdef")) (list a b c)) => '(?a ?b "cdef") - (pcase-let (((dash [a b &rest c]) [1 2 3 4 5 6])) (list a b c)) => '(1 2 [3 4 5 6]) - (pcase-let (((dash [a b &rest [c d]]) [1 2 3 4 5 6])) (list a b c d)) => '(1 2 3 4) - ;; here we error, because "vectors" are rigid, immutable structures, - ;; so we should know how many elements there are - (pcase-let (((dash [a b c d]) [1 2 3])) (+ a b c d)) !!> args-out-of-range - (pcase-let (((dash (a b . c)) (cons 1 (cons 2 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (_ _ . [a b])) (cons 1 (cons 2 (vector 3 4))))) (list a b)) => '(3 4) - (pcase-let (((dash (_ _ a b)) (cons 1 (cons 2 (list 3 4))))) (list a b)) => '(3 4) - (pcase-let (((dash ([a b] _ _ c)) (list (vector 1 2) 3 4 5))) (list a b c)) => '(1 2 5) - ;; final cdr optimization - (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 - (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash (((a b) . c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 (3) (4)) - (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (((a b) . c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 (3)) - ;; cdr-skip optimization - (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 - (pcase-let (((dash (_ (a))) (list 1 (list 2)))) a) => 2 - (pcase-let (((dash (_ _ _ a)) (list 1 2 3 4 5))) a) => 4 - (pcase-let (((dash (_ _ _ (a b))) (list 1 2 3 (list 4 5)))) (list a b)) => '(4 5) - (pcase-let (((dash (_ a _ b)) (list 1 2 3 4 5))) (list a b)) => '(2 4) - (pcase-let (((dash (_ a _ b _ c)) (list 1 2 3 4 5 6))) (list a b c)) => '(2 4 6) - (pcase-let (((dash (_ a _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 4 8) - (pcase-let (((dash (_ a _ _ _ b _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 6 8) - (pcase-let (((dash (_ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c)) => '(4 8 12) - (pcase-let (((dash (_ (a b) _ c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 5) - (pcase-let (((dash (_ (a b) _ . c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 (5)) - (pcase-let (((dash (_ (a b) _ (c d))) (list 1 (list 2 3) 4 (list 5 6)))) (list a b c d)) => '(2 3 5 6) - (pcase-let (((dash (_ (a b) _ _ _ (c d))) (list 1 (list 2 3) 4 5 6 (list 7 8)))) (list a b c d)) => '(2 3 7 8) - (pcase-let (((dash (_ (a b) _ c d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(2 3 5 6) - (pcase-let (((dash (_ (a b) _ _ _ [c d])) (list 1 (list 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) - (pcase-let (((dash (_ [a b] _ _ _ [c d])) (list 1 (vector 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) - (pcase-let (((dash (_ _ _ . a)) (list 1 2 3 4 5))) a) => '(4 5) - (pcase-let (((dash (_ a _ _)) (list 1 2 3 4 5))) a) => 2 - (pcase-let (((dash (_ . b)) (cons 1 2))) b) => 2 - (pcase-let (((dash ([a b c d] . e)) (cons (vector 1 2 3 4) 5))) (list a b c d e)) => '(1 2 3 4 5) - (pcase-let (((dash ([a b c d] _ . e)) (cons (vector 1 2 3 4) (cons 5 6)))) (list a b c d e)) => '(1 2 3 4 6) - ;; late-binding optimization - (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 - (pcase-let (((dash (((&plist :foo a :bar b)))) (list (list (list :bar 1 :foo 2) 3) 4))) (list a b)) => '(2 1) - (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash (((a b) c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 (4)) - (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (a b c d)) (list 1 2 3 4))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash (a)) (list 1 2 3 4))) (list a)) => '(1) - (pcase-let (((dash (_ a)) (list 1 2 3 4))) (list a)) => '(2) - (pcase-let (((dash (_ _ a)) (list 1 2 3 4))) (list a)) => '(3) - (pcase-let (((dash (_ _ . a)) (list 1 2 3 4))) a) => '(3 4) - (pcase-let (((dash (_ _ [a b])) (list 1 2 (vector 3 4)))) (list a b)) => '(3 4) - (pcase-let (((dash (a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(1 4) - (pcase-let (((dash (_ _ a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(3 6) - (pcase-let (((dash (_ _ a _ _ . b)) (list 1 2 3 4 5 6 7 8))) (cons a b)) => '(3 6 7 8) - (pcase-let (((dash (_ a _ b)) (list 1 2 3 4))) (list a b)) => '(2 4) - (pcase-let (((dash (a b c (d e))) (list 1 2 3 (list 4 5)))) (list a b c d e)) => '(1 2 3 4 5) - (pcase-let (((dash (_ _ (_ _ (_ _ a)))) (list 1 2 (list 3 4 (list 5 6 7))))) a) => 7 - (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 - (pcase-let (((dash (_ _ &plist :foo a :bar b)) (list 1 2 :bar 2 :foo 1))) (list a b)) => '(1 2) - ;; &keys support - (pcase-let (((dash (_ _ &keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) - (pcase-let (((dash (a _ &keys :foo b :bar c)) (list 1 2 :bar 4 :foo 3))) (list a b c)) => '(1 3 4) - (pcase-let (((dash (a _ _ _ &keys :foo b :bar c)) (list 1 2 3 4 :bar 6 :foo 5))) (list a b c)) => '(1 5 6) - (pcase-let (((dash (a b &keys :foo c :bar d)) (list 1 2 :bar 4 :foo 3))) (list a b c d)) => '(1 2 3 4) - (pcase-let (((dash (a b &keys)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(1 2) - (pcase-let (((dash (&keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) - (pcase-let (((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9))) (list a b c d e f g h i)) => '(1 2 3 4 5 6 7 8 9) - ;; single-binding optimization for vectors and kv - (pcase-let (((dash [_ [_ [_ a]]]) (vector 1 (vector 2 (vector 3 4))))) a) => 4 - (pcase-let (((dash [a _ _ _]) (vector 1 2 3 4))) a) => 1 - (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 - (pcase-let (((dash [_ _ a _]) (vector 1 2 3 4))) a) => 3 - (pcase-let (((dash [a [_ [_ b]]]) (vector 1 (vector 2 (vector 3 4))))) (list a b)) => '(1 4) - (pcase-let (((dash [(a _ b)]) (vector (list 1 2 3 4)))) (list a b)) => '(1 3) - (pcase-let (((dash (&plist 'a a)) (list 'a 1 'b 2))) a) => 1 - (pcase-let (((dash (&plist 'a [a b])) (list 'a [1 2] 'b 3))) (list a b)) => '(1 2) - (pcase-let (((dash (&plist 'a [a b] 'c c)) (list 'a [1 2] 'c 3))) (list a b c)) => '(1 2 3) - ;; mixing dot and &alist - (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) - (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) - (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) - (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) - (pcase-let (((dash (x y (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list x y a c)) => '(1 2 b d) - (pcase-let (((dash (_ _ (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list a c)) => '(b d) - ;; test bindings with no explicit val - (pcase-let (((dash a) nil)) a) => nil - (pcase-let (((dash a) nil)) a) => nil - (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) - (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) - ;; auto-derived match forms for kv destructuring - ;;; test that we normalize all the supported kv stores - (pcase-let (((dash (&plist :foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&alist :foo :bar)) (list (cons :foo 1) (cons :bar 2)))) (list foo bar)) => '(1 2) - (let ((hash (make-hash-table))) - (puthash :foo 1 hash) - (puthash :bar 2 hash) - (pcase-let (((dash (&hash :foo :bar)) hash)) (list foo bar))) => '(1 2) - (pcase-let (((dash (&hash :foo (&hash? :bar))) (make-hash-table))) bar) => nil - ;; Ensure `hash?' expander evaluates its arg only once - (let* ((ht (make-hash-table :test #'equal)) - (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) - (puthash 'a nil ht) - (pcase-let (((dash (&hash? 'a)) (funcall fn ht))) a)) => '(3) - (pcase-let (((dash (_ &keys :foo :bar)) (list 'ignored :foo 1 :bar 2))) (list foo bar)) => '(1 2) - ;;; go over all the variations of match-form derivation - (pcase-let (((dash (&plist :foo foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist :foo x :bar y)) (list :foo 1 :bar 2))) (list x y)) => '(1 2) - (pcase-let (((dash (&plist :foo (x) :bar [y])) (list :foo (list 1) :bar (vector 2)))) (list x y)) => '(1 2) - (pcase-let (((dash (&plist 'foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist 'foo foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist 'foo foo 'bar bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&plist 'foo x 'bar y)) (list 'foo 1 'bar 2))) (list x y)) => '(1 2) - (pcase-let (((dash (&alist "foo" "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list foo bar)) => '(1 2) - (pcase-let (((dash (&alist "foo" x "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list x bar)) => '(1 2) - (pcase-let (((dash (&alist "foo" x "bar" y)) (list (cons "foo" 1) (cons "bar" 2)))) (list x y)) => '(1 2) - (pcase-let (((dash (&alist :a 'b "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (&alist 'b :a "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (&alist 'b "c" :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (&alist "c" 'b :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (&alist "c" :a 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - (pcase-let (((dash (&alist :a "c" 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) - ;; FIXME: Byte-compiler chokes on these in Emacs < 26, which were - ;; for `-let'. - (eval '(pcase-let (((dash (&plist 'foo 1)) (list 'foo 'bar))) (list foo))) !!> error - (eval '(pcase-let (((dash (&plist foo :bar)) (list :foo :bar))) (list foo))) !!> error - ;; test the &as form - (pcase-let (((dash (items &as first . rest)) (list 1 2 3))) (list first rest items)) => '(1 (2 3) (1 2 3)) - (pcase-let (((dash (all &as [vect &as a b] bar)) (list [1 2] 3))) (list a b bar vect all)) => '(1 2 3 [1 2] ([1 2] 3)) - (pcase-let (((dash (all &as (list &as a b) bar)) (list (list 1 2) 3))) (list a b bar list all)) => '(1 2 3 (1 2) ((1 2) 3)) - (pcase-let (((dash (x &as [a b])) (list (vector 1 2 3)))) (list a b x)) => '(1 2 ([1 2 3])) - (pcase-let (((dash (result &as [_ a] [_ b])) (list [1 2] [3 4]))) (list a b result)) => '(2 4 ([1 2] [3 4])) - (pcase-let (((dash (result &as [fst &as _ a] [snd &as _ b])) (list [1 2] [3 4]))) (list a b fst snd result)) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) - (pcase-let (((dash [x &as a b &rest r]) (vector 1 2 3))) (list a b r x)) => '(1 2 [3] [1 2 3]) - (pcase-let (((dash [x &as a]) (vector 1 2 3))) (list a x)) => '(1 [1 2 3]) - (pcase-let (((dash [x &as _ _ a]) (vector 1 2 3))) (list a x)) => '(3 [1 2 3]) - (pcase-let (((dash [x &as _ _ a]) (vector 1 2 (list 3 4)))) (list a x)) => '((3 4) [1 2 (3 4)]) - (pcase-let (((dash [x &as _ _ (a b)]) (vector 1 2 (list 3 4)))) (list a b x)) => '(3 4 [1 2 (3 4)]) - (pcase-let (((dash (b &as beg . end)) (cons 1 2))) (list beg end b)) => '(1 2 (1 . 2)) - (pcase-let (((dash (plist &as &plist :a a :b b)) (list :a 1 :b 2))) (list a b plist)) => '(1 2 (:a 1 :b 2)) - (pcase-let (((dash (alist &as &alist :a a :b b)) (list (cons :a 1) (cons :b 2)))) (list a b alist)) => '(1 2 ((:a . 1) (:b . 2))) - (pcase-let (((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c list)) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) - (pcase-let (((dash (x &as a b)) (list 1 2)) ((dash (y &as c d)) (list 3 4))) (list a b c d x y)) - => '(1 2 3 4 (1 2) (3 4)) - (pcase-let (((dash (&hash-or-plist :key)) (--doto (make-hash-table) (puthash :key "value" it)))) key) - => "value" - (pcase-let (((dash (&hash-or-plist :key)) '(:key "value"))) key) - => "value") - - (defexamples pcase - (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list 1 2 3 4 5 6) ((dash (a b c . d)) (progn (list a b c d)))) => '(1 2 3 (4 5 6)) - (pcase (list :baz 3 :foo 1 :qux 4 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) - (pcase "foo" ((dash a) (pcase "bar" ((dash b) (progn (list a b)))))) => '("foo" "bar") - (pcase (list 1 2 3) ((dash foo) (progn foo))) => '(1 2 3) - (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list :foo (list 1 2) :bar 3) ((dash (&plist :foo (a b) :bar c)) (progn (list a b c)))) => '(1 2 3) - ;; nil value in plist means subsequent cons matches are nil, because - ;; (car nil) => nil - (pcase (list :bar 1) ((dash (&plist :foo (a b))) (progn (list a b)))) => '(nil nil) - (pcase (list :foo (list 1 2 :baz 2 :bar 4) :bar 3) ((dash (&plist :foo (&plist :baz baz) :bar bar)) (progn (list baz bar)))) => '(2 3) - (pcase (list 'paragraph (list :title "foo" :level 2)) ((dash (_ (&plist :level level :title title))) (progn (list level title)))) => '(2 "foo") - (pcase (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))) ((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (progn (list bar face inv)))) => '(2 foo-face t) - (pcase (list 1 (list 2 3) 4 5 6) ((dash (a (b c) d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase [1 2 3 4] ((dash [a _ c]) (progn (list a c)))) => '(1 3) - (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 - (pcase (vector 1 2 3 4 5) ((dash [a _ _ _ b]) (progn (list a b)))) => '(1 5) - (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (string 102 111 98 97 114) ((dash [a b c]) (progn (list a b c)))) => '(?f ?o ?b) - (pcase "abcdef" ((dash [a b c]) (progn (list a b c)))) => '(?a ?b ?c) - (pcase [1 (2 [3 4]) 5 6] ((dash [a (b [c]) d]) (progn (list a b c d)))) => '(1 2 3 5) - (pcase (list 1 2 3 4 5 6) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list (vector 1 2 3)) ((dash ([a b])) (progn (list a b)))) => '(1 2) - ;; d is bound to nil. I don't think we want to error in such a case. - ;; After all (car nil) => nil - (pcase (list 1 2 3) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 nil) - (pcase [1 2 3 4] ((dash [a b c]) (progn (list a b c)))) => '(1 2 3) - (pcase [1 2 3 4] ((dash [a]) (progn a))) => 1 - (pcase "abcdef" ((dash [a b &rest c]) (progn (list a b c)))) => '(?a ?b "cdef") - (pcase [1 2 3 4 5 6] ((dash [a b &rest c]) (progn (list a b c)))) => '(1 2 [3 4 5 6]) - (pcase [1 2 3 4 5 6] ((dash [a b &rest [c d]]) (progn (list a b c d)))) => '(1 2 3 4) - ;; here we error, because "vectors" are rigid, immutable structures, - ;; so we should know how many elements there are - (pcase [1 2 3] ((dash [a b c d]) (progn (+ a b c d)))) !!> args-out-of-range - (pcase (cons 1 (cons 2 3)) ((dash (a b . c)) (progn (list a b c)))) => '(1 2 3) - (pcase (cons 1 (cons 2 (vector 3 4))) ((dash (_ _ . [a b])) (progn (list a b)))) => '(3 4) - (pcase (cons 1 (cons 2 (list 3 4))) ((dash (_ _ a b)) (progn (list a b)))) => '(3 4) - (pcase (list (vector 1 2) 3 4 5) ((dash ([a b] _ _ c)) (progn (list a b c)))) => '(1 2 5) - ;; final cdr optimization - (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c) . d)) (progn (list a b c d)))) => '(1 2 (3) (4)) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c))) (progn (list a b c)))) => '(1 2 (3)) - ;; cdr-skip optimization - (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 - (pcase (list 1 (list 2)) ((dash (_ (a))) (progn a))) => 2 - (pcase (list 1 2 3 4 5) ((dash (_ _ _ a)) (progn a))) => 4 - (pcase (list 1 2 3 (list 4 5)) ((dash (_ _ _ (a b))) (progn (list a b)))) => '(4 5) - (pcase (list 1 2 3 4 5) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) - (pcase (list 1 2 3 4 5 6) ((dash (_ a _ b _ c)) (progn (list a b c)))) => '(2 4 6) - (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ b _ _ _ c)) (progn (list a b c)))) => '(2 4 8) - (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ _ _ b _ c)) (progn (list a b c)))) => '(2 6 8) - (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (_ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c)))) => '(4 8 12) - (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ c)) (progn (list a b c)))) => '(2 3 5) - (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ . c)) (progn (list a b c)))) => '(2 3 (5)) - (pcase (list 1 (list 2 3) 4 (list 5 6)) ((dash (_ (a b) _ (c d))) (progn (list a b c d)))) => '(2 3 5 6) - (pcase (list 1 (list 2 3) 4 5 6 (list 7 8)) ((dash (_ (a b) _ _ _ (c d))) (progn (list a b c d)))) => '(2 3 7 8) - (pcase (list 1 (list 2 3) 4 5 6) ((dash (_ (a b) _ c d)) (progn (list a b c d)))) => '(2 3 5 6) - (pcase (list 1 (list 2 3) 4 5 6 (vector 7 8)) ((dash (_ (a b) _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) - (pcase (list 1 (vector 2 3) 4 5 6 (vector 7 8)) ((dash (_ [a b] _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) - (pcase (list 1 2 3 4 5) ((dash (_ _ _ . a)) (progn a))) => '(4 5) - (pcase (list 1 2 3 4 5) ((dash (_ a _ _)) (progn a))) => 2 - (pcase (cons 1 2) ((dash (_ . b)) (progn b))) => 2 - (pcase (cons (vector 1 2 3 4) 5) ((dash ([a b c d] . e)) (progn (list a b c d e)))) => '(1 2 3 4 5) - (pcase (cons (vector 1 2 3 4) (cons 5 6)) ((dash ([a b c d] _ . e)) (progn (list a b c d e)))) => '(1 2 3 4 6) - ;; late-binding optimization - (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 - (pcase (list (list (list :bar 1 :foo 2) 3) 4) ((dash (((&plist :foo a :bar b)))) (progn (list a b)))) => '(2 1) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) . d)) (progn (list a b c d)))) => '(1 2 3 (4)) - (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) - (pcase (list 1 2 3 4) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list 1 2 3 4) ((dash (a)) (progn (list a)))) => '(1) - (pcase (list 1 2 3 4) ((dash (_ a)) (progn (list a)))) => '(2) - (pcase (list 1 2 3 4) ((dash (_ _ a)) (progn (list a)))) => '(3) - (pcase (list 1 2 3 4) ((dash (_ _ . a)) (progn a))) => '(3 4) - (pcase (list 1 2 (vector 3 4)) ((dash (_ _ [a b])) (progn (list a b)))) => '(3 4) - (pcase (list 1 2 3 4 5 6 7 8) ((dash (a _ _ b)) (progn (list a b)))) => '(1 4) - (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ b)) (progn (list a b)))) => '(3 6) - (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ . b)) (progn (cons a b)))) => '(3 6 7 8) - (pcase (list 1 2 3 4) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) - (pcase (list 1 2 3 (list 4 5)) ((dash (a b c (d e))) (progn (list a b c d e)))) => '(1 2 3 4 5) - (pcase (list 1 2 (list 3 4 (list 5 6 7))) ((dash (_ _ (_ _ (_ _ a)))) (progn a))) => 7 - (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 - (pcase (list 1 2 :bar 2 :foo 1) ((dash (_ _ &plist :foo a :bar b)) (progn (list a b)))) => '(1 2) - ;; &keys support - (pcase (list 1 2 :bar 4 :foo 3) ((dash (_ _ &keys :foo a :bar b)) (progn (list a b)))) => '(3 4) - (pcase (list 1 2 :bar 4 :foo 3) ((dash (a _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 3 4) - (pcase (list 1 2 3 4 :bar 6 :foo 5) ((dash (a _ _ _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 5 6) - (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys :foo c :bar d)) (progn (list a b c d)))) => '(1 2 3 4) - (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys)) (progn (list a b)))) => '(1 2) - (pcase (list 1 2 :bar 4 :foo 3) ((dash (&keys :foo a :bar b)) (progn (list a b)))) => '(3 4) - (pcase (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9) ((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (progn (list a b c d e f g h i)))) => '(1 2 3 4 5 6 7 8 9) - ;; single-binding optimization for vectors and kv - (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [_ [_ [_ a]]]) (progn a))) => 4 - (pcase (vector 1 2 3 4) ((dash [a _ _ _]) (progn a))) => 1 - (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 - (pcase (vector 1 2 3 4) ((dash [_ _ a _]) (progn a))) => 3 - (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [a [_ [_ b]]]) (progn (list a b)))) => '(1 4) - (pcase (vector (list 1 2 3 4)) ((dash [(a _ b)]) (progn (list a b)))) => '(1 3) - (pcase (list 'a 1 'b 2) ((dash (&plist 'a a)) (progn a))) => 1 - (pcase (list 'a [1 2] 'b 3) ((dash (&plist 'a [a b])) (progn (list a b)))) => '(1 2) - (pcase (list 'a [1 2] 'c 3) ((dash (&plist 'a [a b] 'c c)) (progn (list a b c)))) => '(1 2 3) - ;; mixing dot and &alist - (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) - (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) - (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) - (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) - (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (x y (&alist 'a a 'c c))) (progn (list x y a c)))) => '(1 2 b d) - (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (_ _ (&alist 'a a 'c c))) (progn (list a c)))) => '(b d) - ;; test bindings with no explicit val - (pcase nil ((dash a) (progn a))) => nil - (pcase nil ((dash a) (progn a))) => nil - (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) - (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) - ;; auto-derived match forms for kv destructuring - ;;; test that we normalize all the supported kv stores - (pcase (list :foo 1 :bar 2) ((dash (&plist :foo :bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list (cons :foo 1) (cons :bar 2)) ((dash (&alist :foo :bar)) (progn (list foo bar)))) => '(1 2) - (let ((hash (make-hash-table))) - (puthash :foo 1 hash) - (puthash :bar 2 hash) - (pcase hash ((dash (&hash :foo :bar)) (progn (list foo bar))))) => '(1 2) - (pcase (make-hash-table) ((dash (&hash :foo (&hash? :bar))) (progn bar))) => nil - ;; Ensure `hash?' expander evaluates its arg only once - (let* ((ht (make-hash-table :test #'equal)) - (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) - (puthash 'a nil ht) - (pcase (funcall fn ht) ((dash (&hash? 'a)) (progn a)))) => '(3) - (pcase (list 'ignored :foo 1 :bar 2) ((dash (_ &keys :foo :bar)) (progn (list foo bar)))) => '(1 2) - ;;; go over all the variations of match-form derivation - (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list :foo 1 :bar 2) ((dash (&plist :foo x :bar y)) (progn (list x y)))) => '(1 2) - (pcase (list :foo (list 1) :bar (vector 2)) ((dash (&plist :foo (x) :bar [y])) (progn (list x y)))) => '(1 2) - (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo 'bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar bar)) (progn (list foo bar)))) => '(1 2) - (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo x 'bar y)) (progn (list x y)))) => '(1 2) - (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" "bar")) (progn (list foo bar)))) => '(1 2) - (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar")) (progn (list x bar)))) => '(1 2) - (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar" y)) (progn (list x y)))) => '(1 2) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a 'b "c")) (progn (list a b c)))) => '(1 2 3) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b :a "c")) (progn (list a b c)))) => '(1 2 3) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b "c" :a)) (progn (list a b c)))) => '(1 2 3) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" 'b :a)) (progn (list a b c)))) => '(1 2 3) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" :a 'b)) (progn (list a b c)))) => '(1 2 3) - (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a "c" 'b)) (progn (list a b c)))) => '(1 2 3) - ;; FIXME: Byte-compiler chokes on these in Emacs < 26. - (eval '(pcase (list 'foo 'bar) ((dash (&plist 'foo 1)) (progn (list foo))))) !!> error - (eval '(pcase (list :foo :bar) ((dash (&plist foo :bar)) (progn (list foo)))) t) !!> error - ;; test the &as form - (pcase (list 1 2 3) ((dash (items &as first . rest)) (progn (list first rest items)))) => '(1 (2 3) (1 2 3)) - (pcase (list [1 2] 3) ((dash (all &as [vect &as a b] bar)) (progn (list a b bar vect all)))) => '(1 2 3 [1 2] ([1 2] 3)) - (pcase (list (list 1 2) 3) ((dash (all &as (list &as a b) bar)) (progn (list a b bar list all)))) => '(1 2 3 (1 2) ((1 2) 3)) - (pcase (list (vector 1 2 3)) ((dash (x &as [a b])) (progn (list a b x)))) => '(1 2 ([1 2 3])) - (pcase (list [1 2] [3 4]) ((dash (result &as [_ a] [_ b])) (progn (list a b result)))) => '(2 4 ([1 2] [3 4])) - (pcase (list [1 2] [3 4]) ((dash (result &as [fst &as _ a] [snd &as _ b])) (progn (list a b fst snd result)))) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) - (pcase (vector 1 2 3) ((dash [x &as a b &rest r]) (progn (list a b r x)))) => '(1 2 [3] [1 2 3]) - (pcase (vector 1 2 3) ((dash [x &as a]) (progn (list a x)))) => '(1 [1 2 3]) - (pcase (vector 1 2 3) ((dash [x &as _ _ a]) (progn (list a x)))) => '(3 [1 2 3]) - (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ a]) (progn (list a x)))) => '((3 4) [1 2 (3 4)]) - (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ (a b)]) (progn (list a b x)))) => '(3 4 [1 2 (3 4)]) - (pcase (cons 1 2) ((dash (b &as beg . end)) (progn (list beg end b)))) => '(1 2 (1 . 2)) - (pcase (list :a 1 :b 2) ((dash (plist &as &plist :a a :b b)) (progn (list a b plist)))) => '(1 2 (:a 1 :b 2)) - (pcase (list (cons :a 1) (cons :b 2)) ((dash (alist &as &alist :a a :b b)) (progn (list a b alist)))) => '(1 2 ((:a . 1) (:b . 2))) - (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c list)))) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) - (pcase (list 1 2) ((dash (x &as a b)) (pcase (list 3 4) ((dash (y &as c d)) (progn (list a b c d x y)))))) - => '(1 2 3 4 (1 2) (3 4)) - (pcase (--doto (make-hash-table) (puthash :key "value" it)) ((dash (&hash-or-plist :key)) (progn key))) - => "value" - (pcase '(:key "value") ((dash (&hash-or-plist :key)) (progn key))) - => "value") - - ) + (when (fboundp 'pcase-defmacro) + (defexamples pcase-let + (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a b c . d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 (4 5 6)) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :baz 3 :foo 1 :qux 4 :bar 2))) (list foo bar)) => '(1 2) + (let ((a (list 1 2 3)) + (b (list 'a 'b 'c))) + (pcase-let (((dash (a . b)) a) ((dash (c . d)) b)) (list a b c d))) => '(1 (2 3) a (b c)) + (pcase-let (((dash a) "foo") ((dash b) "bar")) (list a b)) => '("foo" "bar") + (pcase-let (((dash foo) (list 1 2 3))) foo) => '(1 2 3) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo (a b) :bar c)) (list :foo (list 1 2) :bar 3))) (list a b c)) => '(1 2 3) + ;; nil value in plist means subsequent cons matches are nil, because + ;; (car nil) => nil + (pcase-let (((dash (&plist :foo (a b))) (list :bar 1))) (list a b)) => '(nil nil) + (pcase-let (((dash (&plist :foo (&plist :baz baz) :bar bar)) (list :foo (list 1 2 :baz 2 :bar 4) :bar 3))) (list baz bar)) => '(2 3) + (pcase-let (((dash (_ (&plist :level level :title title))) (list 'paragraph (list :title "foo" :level 2)))) (list level title)) => '(2 "foo") + (pcase-let (((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))))) (list bar face inv)) => '(2 foo-face t) + (pcase-let (((dash (a (b c) d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash [a _ c]) [1 2 3 4])) (list a c)) => '(1 3) + (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 + (pcase-let (((dash [a _ _ _ b]) (vector 1 2 3 4 5))) (list a b)) => '(1 5) + (pcase-let (((dash [a (b c) d]) [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash [a b c]) (string 102 111 98 97 114))) (list a b c)) => '(?f ?o ?b) + (pcase-let (((dash [a b c]) "abcdef")) (list a b c)) => '(?a ?b ?c) + (pcase-let (((dash [a (b [c]) d]) [1 (2 [3 4]) 5 6])) (list a b c d)) => '(1 2 3 5) + (pcase-let (((dash (a b c d)) (list 1 2 3 4 5 6))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash ([a b])) (list (vector 1 2 3)))) (list a b)) => '(1 2) + ;; d is bound to nil. I don't think we want to error in such a case. + ;; After all (car nil) => nil + (pcase-let (((dash (a b c d)) (list 1 2 3))) (list a b c d)) => '(1 2 3 nil) + (pcase-let (((dash [a b c]) [1 2 3 4])) (list a b c)) => '(1 2 3) + (pcase-let (((dash [a]) [1 2 3 4])) a) => 1 + (pcase-let (((dash [a b &rest c]) "abcdef")) (list a b c)) => '(?a ?b "cdef") + (pcase-let (((dash [a b &rest c]) [1 2 3 4 5 6])) (list a b c)) => '(1 2 [3 4 5 6]) + (pcase-let (((dash [a b &rest [c d]]) [1 2 3 4 5 6])) (list a b c d)) => '(1 2 3 4) + ;; here we error, because "vectors" are rigid, immutable structures, + ;; so we should know how many elements there are + (pcase-let (((dash [a b c d]) [1 2 3])) (+ a b c d)) !!> args-out-of-range + (pcase-let (((dash (a b . c)) (cons 1 (cons 2 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (_ _ . [a b])) (cons 1 (cons 2 (vector 3 4))))) (list a b)) => '(3 4) + (pcase-let (((dash (_ _ a b)) (cons 1 (cons 2 (list 3 4))))) (list a b)) => '(3 4) + (pcase-let (((dash ([a b] _ _ c)) (list (vector 1 2) 3 4 5))) (list a b c)) => '(1 2 5) + ;; final cdr optimization + (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 + (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (((a b) . c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 (3) (4)) + (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (((a b) . c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 (3)) + ;; cdr-skip optimization + (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 + (pcase-let (((dash (_ (a))) (list 1 (list 2)))) a) => 2 + (pcase-let (((dash (_ _ _ a)) (list 1 2 3 4 5))) a) => 4 + (pcase-let (((dash (_ _ _ (a b))) (list 1 2 3 (list 4 5)))) (list a b)) => '(4 5) + (pcase-let (((dash (_ a _ b)) (list 1 2 3 4 5))) (list a b)) => '(2 4) + (pcase-let (((dash (_ a _ b _ c)) (list 1 2 3 4 5 6))) (list a b c)) => '(2 4 6) + (pcase-let (((dash (_ a _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 4 8) + (pcase-let (((dash (_ a _ _ _ b _ c)) (list 1 2 3 4 5 6 7 8))) (list a b c)) => '(2 6 8) + (pcase-let (((dash (_ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c)) => '(4 8 12) + (pcase-let (((dash (_ (a b) _ c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 5) + (pcase-let (((dash (_ (a b) _ . c)) (list 1 (list 2 3) 4 5))) (list a b c)) => '(2 3 (5)) + (pcase-let (((dash (_ (a b) _ (c d))) (list 1 (list 2 3) 4 (list 5 6)))) (list a b c d)) => '(2 3 5 6) + (pcase-let (((dash (_ (a b) _ _ _ (c d))) (list 1 (list 2 3) 4 5 6 (list 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ (a b) _ c d)) (list 1 (list 2 3) 4 5 6))) (list a b c d)) => '(2 3 5 6) + (pcase-let (((dash (_ (a b) _ _ _ [c d])) (list 1 (list 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ [a b] _ _ _ [c d])) (list 1 (vector 2 3) 4 5 6 (vector 7 8)))) (list a b c d)) => '(2 3 7 8) + (pcase-let (((dash (_ _ _ . a)) (list 1 2 3 4 5))) a) => '(4 5) + (pcase-let (((dash (_ a _ _)) (list 1 2 3 4 5))) a) => 2 + (pcase-let (((dash (_ . b)) (cons 1 2))) b) => 2 + (pcase-let (((dash ([a b c d] . e)) (cons (vector 1 2 3 4) 5))) (list a b c d e)) => '(1 2 3 4 5) + (pcase-let (((dash ([a b c d] _ . e)) (cons (vector 1 2 3 4) (cons 5 6)))) (list a b c d e)) => '(1 2 3 4 6) + ;; late-binding optimization + (pcase-let (((dash (((a)))) (list (list (list 1 2) 3) 4))) a) => 1 + (pcase-let (((dash (((&plist :foo a :bar b)))) (list (list (list :bar 1 :foo 2) 3) 4))) (list a b)) => '(2 1) + (pcase-let (((dash (((a b) c) d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (((a b) c) . d)) (list (list (list 1 2) 3) 4))) (list a b c d)) => '(1 2 3 (4)) + (pcase-let (((dash (((a b) c))) (list (list (list 1 2) 3) 4))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (a b c d)) (list 1 2 3 4))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a)) (list 1 2 3 4))) (list a)) => '(1) + (pcase-let (((dash (_ a)) (list 1 2 3 4))) (list a)) => '(2) + (pcase-let (((dash (_ _ a)) (list 1 2 3 4))) (list a)) => '(3) + (pcase-let (((dash (_ _ . a)) (list 1 2 3 4))) a) => '(3 4) + (pcase-let (((dash (_ _ [a b])) (list 1 2 (vector 3 4)))) (list a b)) => '(3 4) + (pcase-let (((dash (a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(1 4) + (pcase-let (((dash (_ _ a _ _ b)) (list 1 2 3 4 5 6 7 8))) (list a b)) => '(3 6) + (pcase-let (((dash (_ _ a _ _ . b)) (list 1 2 3 4 5 6 7 8))) (cons a b)) => '(3 6 7 8) + (pcase-let (((dash (_ a _ b)) (list 1 2 3 4))) (list a b)) => '(2 4) + (pcase-let (((dash (a b c (d e))) (list 1 2 3 (list 4 5)))) (list a b c d e)) => '(1 2 3 4 5) + (pcase-let (((dash (_ _ (_ _ (_ _ a)))) (list 1 2 (list 3 4 (list 5 6 7))))) a) => 7 + (pcase-let (((dash (_ (_ (_ a)))) (list 1 (list 2 (list 3 4))))) a) => 4 + (pcase-let (((dash (_ _ &plist :foo a :bar b)) (list 1 2 :bar 2 :foo 1))) (list a b)) => '(1 2) + ;; &keys support + (pcase-let (((dash (_ _ &keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) + (pcase-let (((dash (a _ &keys :foo b :bar c)) (list 1 2 :bar 4 :foo 3))) (list a b c)) => '(1 3 4) + (pcase-let (((dash (a _ _ _ &keys :foo b :bar c)) (list 1 2 3 4 :bar 6 :foo 5))) (list a b c)) => '(1 5 6) + (pcase-let (((dash (a b &keys :foo c :bar d)) (list 1 2 :bar 4 :foo 3))) (list a b c d)) => '(1 2 3 4) + (pcase-let (((dash (a b &keys)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(1 2) + (pcase-let (((dash (&keys :foo a :bar b)) (list 1 2 :bar 4 :foo 3))) (list a b)) => '(3 4) + (pcase-let (((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9))) (list a b c d e f g h i)) => '(1 2 3 4 5 6 7 8 9) + ;; single-binding optimization for vectors and kv + (pcase-let (((dash [_ [_ [_ a]]]) (vector 1 (vector 2 (vector 3 4))))) a) => 4 + (pcase-let (((dash [a _ _ _]) (vector 1 2 3 4))) a) => 1 + (pcase-let (((dash [_ _ _ a]) (vector 1 2 3 4))) a) => 4 + (pcase-let (((dash [_ _ a _]) (vector 1 2 3 4))) a) => 3 + (pcase-let (((dash [a [_ [_ b]]]) (vector 1 (vector 2 (vector 3 4))))) (list a b)) => '(1 4) + (pcase-let (((dash [(a _ b)]) (vector (list 1 2 3 4)))) (list a b)) => '(1 3) + (pcase-let (((dash (&plist 'a a)) (list 'a 1 'b 2))) a) => 1 + (pcase-let (((dash (&plist 'a [a b])) (list 'a [1 2] 'b 3))) (list a b)) => '(1 2) + (pcase-let (((dash (&plist 'a [a b] 'c c)) (list 'a [1 2] 'c 3))) (list a b c)) => '(1 2 3) + ;; mixing dot and &alist + (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) + (pcase-let (((dash (x y &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ &alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) + (pcase-let (((dash (x y (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list x y a c)) => '(1 2 b d) + (pcase-let (((dash (_ _ (&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list a c)) => '(b d) + ;; test bindings with no explicit val + (pcase-let (((dash a) nil)) a) => nil + (pcase-let (((dash a) nil)) a) => nil + (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) + (pcase-let (((dash a) nil) ((dash b) nil)) (list a b)) => '(nil nil) + ;; auto-derived match forms for kv destructuring + ;;; test that we normalize all the supported kv stores + (pcase-let (((dash (&plist :foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&alist :foo :bar)) (list (cons :foo 1) (cons :bar 2)))) (list foo bar)) => '(1 2) + (let ((hash (make-hash-table))) + (puthash :foo 1 hash) + (puthash :bar 2 hash) + (pcase-let (((dash (&hash :foo :bar)) hash)) (list foo bar))) => '(1 2) + (pcase-let (((dash (&hash :foo (&hash? :bar))) (make-hash-table))) bar) => nil + ;; Ensure `hash?' expander evaluates its arg only once + (let* ((ht (make-hash-table :test #'equal)) + (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) + (puthash 'a nil ht) + (pcase-let (((dash (&hash? 'a)) (funcall fn ht))) a)) => '(3) + (pcase-let (((dash (_ &keys :foo :bar)) (list 'ignored :foo 1 :bar 2))) (list foo bar)) => '(1 2) + ;;; go over all the variations of match-form derivation + (pcase-let (((dash (&plist :foo foo :bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo foo :bar bar)) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist :foo x :bar y)) (list :foo 1 :bar 2))) (list x y)) => '(1 2) + (pcase-let (((dash (&plist :foo (x) :bar [y])) (list :foo (list 1) :bar (vector 2)))) (list x y)) => '(1 2) + (pcase-let (((dash (&plist 'foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo foo 'bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo foo 'bar bar)) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&plist 'foo x 'bar y)) (list 'foo 1 'bar 2))) (list x y)) => '(1 2) + (pcase-let (((dash (&alist "foo" "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list foo bar)) => '(1 2) + (pcase-let (((dash (&alist "foo" x "bar")) (list (cons "foo" 1) (cons "bar" 2)))) (list x bar)) => '(1 2) + (pcase-let (((dash (&alist "foo" x "bar" y)) (list (cons "foo" 1) (cons "bar" 2)))) (list x y)) => '(1 2) + (pcase-let (((dash (&alist :a 'b "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist 'b :a "c")) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist 'b "c" :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist "c" 'b :a)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist "c" :a 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + (pcase-let (((dash (&alist :a "c" 'b)) (list (cons :a 1) (cons 'b 2) (cons "c" 3)))) (list a b c)) => '(1 2 3) + ;; FIXME: Byte-compiler chokes on these in Emacs < 26, which were + ;; for `-let'. + (eval '(pcase-let (((dash (&plist 'foo 1)) (list 'foo 'bar))) (list foo))) !!> error + (eval '(pcase-let (((dash (&plist foo :bar)) (list :foo :bar))) (list foo))) !!> error + ;; test the &as form + (pcase-let (((dash (items &as first . rest)) (list 1 2 3))) (list first rest items)) => '(1 (2 3) (1 2 3)) + (pcase-let (((dash (all &as [vect &as a b] bar)) (list [1 2] 3))) (list a b bar vect all)) => '(1 2 3 [1 2] ([1 2] 3)) + (pcase-let (((dash (all &as (list &as a b) bar)) (list (list 1 2) 3))) (list a b bar list all)) => '(1 2 3 (1 2) ((1 2) 3)) + (pcase-let (((dash (x &as [a b])) (list (vector 1 2 3)))) (list a b x)) => '(1 2 ([1 2 3])) + (pcase-let (((dash (result &as [_ a] [_ b])) (list [1 2] [3 4]))) (list a b result)) => '(2 4 ([1 2] [3 4])) + (pcase-let (((dash (result &as [fst &as _ a] [snd &as _ b])) (list [1 2] [3 4]))) (list a b fst snd result)) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) + (pcase-let (((dash [x &as a b &rest r]) (vector 1 2 3))) (list a b r x)) => '(1 2 [3] [1 2 3]) + (pcase-let (((dash [x &as a]) (vector 1 2 3))) (list a x)) => '(1 [1 2 3]) + (pcase-let (((dash [x &as _ _ a]) (vector 1 2 3))) (list a x)) => '(3 [1 2 3]) + (pcase-let (((dash [x &as _ _ a]) (vector 1 2 (list 3 4)))) (list a x)) => '((3 4) [1 2 (3 4)]) + (pcase-let (((dash [x &as _ _ (a b)]) (vector 1 2 (list 3 4)))) (list a b x)) => '(3 4 [1 2 (3 4)]) + (pcase-let (((dash (b &as beg . end)) (cons 1 2))) (list beg end b)) => '(1 2 (1 . 2)) + (pcase-let (((dash (plist &as &plist :a a :b b)) (list :a 1 :b 2))) (list a b plist)) => '(1 2 (:a 1 :b 2)) + (pcase-let (((dash (alist &as &alist :a a :b b)) (list (cons :a 1) (cons :b 2)))) (list a b alist)) => '(1 2 ((:a . 1) (:b . 2))) + (pcase-let (((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (list 1 2 3 4 5 6 7 8 9 10 11 12))) (list a b c list)) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) + (pcase-let (((dash (x &as a b)) (list 1 2)) ((dash (y &as c d)) (list 3 4))) (list a b c d x y)) + => '(1 2 3 4 (1 2) (3 4)) + (pcase-let (((dash (&hash-or-plist :key)) (--doto (make-hash-table) (puthash :key "value" it)))) key) + => "value" + (pcase-let (((dash (&hash-or-plist :key)) '(:key "value"))) key) + => "value") + + (defexamples pcase + (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 3 4 5 6) ((dash (a b c . d)) (progn (list a b c d)))) => '(1 2 3 (4 5 6)) + (pcase (list :baz 3 :foo 1 :qux 4 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase "foo" ((dash a) (pcase "bar" ((dash b) (progn (list a b)))))) => '("foo" "bar") + (pcase (list 1 2 3) ((dash foo) (progn foo))) => '(1 2 3) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo (list 1 2) :bar 3) ((dash (&plist :foo (a b) :bar c)) (progn (list a b c)))) => '(1 2 3) + ;; nil value in plist means subsequent cons matches are nil, because + ;; (car nil) => nil + (pcase (list :bar 1) ((dash (&plist :foo (a b))) (progn (list a b)))) => '(nil nil) + (pcase (list :foo (list 1 2 :baz 2 :bar 4) :bar 3) ((dash (&plist :foo (&plist :baz baz) :bar bar)) (progn (list baz bar)))) => '(2 3) + (pcase (list 'paragraph (list :title "foo" :level 2)) ((dash (_ (&plist :level level :title title))) (progn (list level title)))) => '(2 "foo") + (pcase (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t))) ((dash (&alist :foo (&plist 'face face 'invisible inv) :bar bar)) (progn (list bar face inv)))) => '(2 foo-face t) + (pcase (list 1 (list 2 3) 4 5 6) ((dash (a (b c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase [1 2 3 4] ((dash [a _ c]) (progn (list a c)))) => '(1 3) + (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 + (pcase (vector 1 2 3 4 5) ((dash [a _ _ _ b]) (progn (list a b)))) => '(1 5) + (pcase [1 (2 3) 4] ((dash [a (b c) d]) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (string 102 111 98 97 114) ((dash [a b c]) (progn (list a b c)))) => '(?f ?o ?b) + (pcase "abcdef" ((dash [a b c]) (progn (list a b c)))) => '(?a ?b ?c) + (pcase [1 (2 [3 4]) 5 6] ((dash [a (b [c]) d]) (progn (list a b c d)))) => '(1 2 3 5) + (pcase (list 1 2 3 4 5 6) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (vector 1 2 3)) ((dash ([a b])) (progn (list a b)))) => '(1 2) + ;; d is bound to nil. I don't think we want to error in such a case. + ;; After all (car nil) => nil + (pcase (list 1 2 3) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 nil) + (pcase [1 2 3 4] ((dash [a b c]) (progn (list a b c)))) => '(1 2 3) + (pcase [1 2 3 4] ((dash [a]) (progn a))) => 1 + (pcase "abcdef" ((dash [a b &rest c]) (progn (list a b c)))) => '(?a ?b "cdef") + (pcase [1 2 3 4 5 6] ((dash [a b &rest c]) (progn (list a b c)))) => '(1 2 [3 4 5 6]) + (pcase [1 2 3 4 5 6] ((dash [a b &rest [c d]]) (progn (list a b c d)))) => '(1 2 3 4) + ;; here we error, because "vectors" are rigid, immutable structures, + ;; so we should know how many elements there are + (pcase [1 2 3] ((dash [a b c d]) (progn (+ a b c d)))) !!> args-out-of-range + (pcase (cons 1 (cons 2 3)) ((dash (a b . c)) (progn (list a b c)))) => '(1 2 3) + (pcase (cons 1 (cons 2 (vector 3 4))) ((dash (_ _ . [a b])) (progn (list a b)))) => '(3 4) + (pcase (cons 1 (cons 2 (list 3 4))) ((dash (_ _ a b)) (progn (list a b)))) => '(3 4) + (pcase (list (vector 1 2) 3 4 5) ((dash ([a b] _ _ c)) (progn (list a b c)))) => '(1 2 5) + ;; final cdr optimization + (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c) . d)) (progn (list a b c d)))) => '(1 2 (3) (4)) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) . c))) (progn (list a b c)))) => '(1 2 (3)) + ;; cdr-skip optimization + (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 + (pcase (list 1 (list 2)) ((dash (_ (a))) (progn a))) => 2 + (pcase (list 1 2 3 4 5) ((dash (_ _ _ a)) (progn a))) => 4 + (pcase (list 1 2 3 (list 4 5)) ((dash (_ _ _ (a b))) (progn (list a b)))) => '(4 5) + (pcase (list 1 2 3 4 5) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) + (pcase (list 1 2 3 4 5 6) ((dash (_ a _ b _ c)) (progn (list a b c)))) => '(2 4 6) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ b _ _ _ c)) (progn (list a b c)))) => '(2 4 8) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ a _ _ _ b _ c)) (progn (list a b c)))) => '(2 6 8) + (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (_ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c)))) => '(4 8 12) + (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ c)) (progn (list a b c)))) => '(2 3 5) + (pcase (list 1 (list 2 3) 4 5) ((dash (_ (a b) _ . c)) (progn (list a b c)))) => '(2 3 (5)) + (pcase (list 1 (list 2 3) 4 (list 5 6)) ((dash (_ (a b) _ (c d))) (progn (list a b c d)))) => '(2 3 5 6) + (pcase (list 1 (list 2 3) 4 5 6 (list 7 8)) ((dash (_ (a b) _ _ _ (c d))) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 (list 2 3) 4 5 6) ((dash (_ (a b) _ c d)) (progn (list a b c d)))) => '(2 3 5 6) + (pcase (list 1 (list 2 3) 4 5 6 (vector 7 8)) ((dash (_ (a b) _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 (vector 2 3) 4 5 6 (vector 7 8)) ((dash (_ [a b] _ _ _ [c d])) (progn (list a b c d)))) => '(2 3 7 8) + (pcase (list 1 2 3 4 5) ((dash (_ _ _ . a)) (progn a))) => '(4 5) + (pcase (list 1 2 3 4 5) ((dash (_ a _ _)) (progn a))) => 2 + (pcase (cons 1 2) ((dash (_ . b)) (progn b))) => 2 + (pcase (cons (vector 1 2 3 4) 5) ((dash ([a b c d] . e)) (progn (list a b c d e)))) => '(1 2 3 4 5) + (pcase (cons (vector 1 2 3 4) (cons 5 6)) ((dash ([a b c d] _ . e)) (progn (list a b c d e)))) => '(1 2 3 4 6) + ;; late-binding optimization + (pcase (list (list (list 1 2) 3) 4) ((dash (((a)))) (progn a))) => 1 + (pcase (list (list (list :bar 1 :foo 2) 3) 4) ((dash (((&plist :foo a :bar b)))) (progn (list a b)))) => '(2 1) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c) . d)) (progn (list a b c d)))) => '(1 2 3 (4)) + (pcase (list (list (list 1 2) 3) 4) ((dash (((a b) c))) (progn (list a b c)))) => '(1 2 3) + (pcase (list 1 2 3 4) ((dash (a b c d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 3 4) ((dash (a)) (progn (list a)))) => '(1) + (pcase (list 1 2 3 4) ((dash (_ a)) (progn (list a)))) => '(2) + (pcase (list 1 2 3 4) ((dash (_ _ a)) (progn (list a)))) => '(3) + (pcase (list 1 2 3 4) ((dash (_ _ . a)) (progn a))) => '(3 4) + (pcase (list 1 2 (vector 3 4)) ((dash (_ _ [a b])) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (a _ _ b)) (progn (list a b)))) => '(1 4) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ b)) (progn (list a b)))) => '(3 6) + (pcase (list 1 2 3 4 5 6 7 8) ((dash (_ _ a _ _ . b)) (progn (cons a b)))) => '(3 6 7 8) + (pcase (list 1 2 3 4) ((dash (_ a _ b)) (progn (list a b)))) => '(2 4) + (pcase (list 1 2 3 (list 4 5)) ((dash (a b c (d e))) (progn (list a b c d e)))) => '(1 2 3 4 5) + (pcase (list 1 2 (list 3 4 (list 5 6 7))) ((dash (_ _ (_ _ (_ _ a)))) (progn a))) => 7 + (pcase (list 1 (list 2 (list 3 4))) ((dash (_ (_ (_ a)))) (progn a))) => 4 + (pcase (list 1 2 :bar 2 :foo 1) ((dash (_ _ &plist :foo a :bar b)) (progn (list a b)))) => '(1 2) + ;; &keys support + (pcase (list 1 2 :bar 4 :foo 3) ((dash (_ _ &keys :foo a :bar b)) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 3 4) + (pcase (list 1 2 3 4 :bar 6 :foo 5) ((dash (a _ _ _ &keys :foo b :bar c)) (progn (list a b c)))) => '(1 5 6) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys :foo c :bar d)) (progn (list a b c d)))) => '(1 2 3 4) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (a b &keys)) (progn (list a b)))) => '(1 2) + (pcase (list 1 2 :bar 4 :foo 3) ((dash (&keys :foo a :bar b)) (progn (list a b)))) => '(3 4) + (pcase (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9) ((dash (a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i)) (progn (list a b c d e f g h i)))) => '(1 2 3 4 5 6 7 8 9) + ;; single-binding optimization for vectors and kv + (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [_ [_ [_ a]]]) (progn a))) => 4 + (pcase (vector 1 2 3 4) ((dash [a _ _ _]) (progn a))) => 1 + (pcase (vector 1 2 3 4) ((dash [_ _ _ a]) (progn a))) => 4 + (pcase (vector 1 2 3 4) ((dash [_ _ a _]) (progn a))) => 3 + (pcase (vector 1 (vector 2 (vector 3 4))) ((dash [a [_ [_ b]]]) (progn (list a b)))) => '(1 4) + (pcase (vector (list 1 2 3 4)) ((dash [(a _ b)]) (progn (list a b)))) => '(1 3) + (pcase (list 'a 1 'b 2) ((dash (&plist 'a a)) (progn a))) => 1 + (pcase (list 'a [1 2] 'b 3) ((dash (&plist 'a [a b])) (progn (list a b)))) => '(1 2) + (pcase (list 'a [1 2] 'c 3) ((dash (&plist 'a [a b] 'c c)) (progn (list a b c)))) => '(1 2 3) + ;; mixing dot and &alist + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (x y &alist 'a a 'c c)) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)) ((dash (_ _ &alist 'a a 'c c)) (progn (list a c)))) => '(b d) + (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (x y (&alist 'a a 'c c))) (progn (list x y a c)))) => '(1 2 b d) + (pcase (list 1 2 '((a . b) (e . f) (g . h) (c . d))) ((dash (_ _ (&alist 'a a 'c c))) (progn (list a c)))) => '(b d) + ;; test bindings with no explicit val + (pcase nil ((dash a) (progn a))) => nil + (pcase nil ((dash a) (progn a))) => nil + (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) + (pcase nil ((dash a) (pcase nil ((dash b) (progn (list a b)))))) => '(nil nil) + ;; auto-derived match forms for kv destructuring + ;;; test that we normalize all the supported kv stores + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo :bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list (cons :foo 1) (cons :bar 2)) ((dash (&alist :foo :bar)) (progn (list foo bar)))) => '(1 2) + (let ((hash (make-hash-table))) + (puthash :foo 1 hash) + (puthash :bar 2 hash) + (pcase hash ((dash (&hash :foo :bar)) (progn (list foo bar))))) => '(1 2) + (pcase (make-hash-table) ((dash (&hash :foo (&hash? :bar))) (progn bar))) => nil + ;; Ensure `hash?' expander evaluates its arg only once + (let* ((ht (make-hash-table :test #'equal)) + (fn (lambda (ht) (push 3 (gethash 'a ht)) ht))) + (puthash 'a nil ht) + (pcase (funcall fn ht) ((dash (&hash? 'a)) (progn a)))) => '(3) + (pcase (list 'ignored :foo 1 :bar 2) ((dash (_ &keys :foo :bar)) (progn (list foo bar)))) => '(1 2) + ;;; go over all the variations of match-form derivation + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo foo :bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list :foo 1 :bar 2) ((dash (&plist :foo x :bar y)) (progn (list x y)))) => '(1 2) + (pcase (list :foo (list 1) :bar (vector 2)) ((dash (&plist :foo (x) :bar [y])) (progn (list x y)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo 'bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo foo 'bar bar)) (progn (list foo bar)))) => '(1 2) + (pcase (list 'foo 1 'bar 2) ((dash (&plist 'foo x 'bar y)) (progn (list x y)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" "bar")) (progn (list foo bar)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar")) (progn (list x bar)))) => '(1 2) + (pcase (list (cons "foo" 1) (cons "bar" 2)) ((dash (&alist "foo" x "bar" y)) (progn (list x y)))) => '(1 2) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a 'b "c")) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b :a "c")) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist 'b "c" :a)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" 'b :a)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist "c" :a 'b)) (progn (list a b c)))) => '(1 2 3) + (pcase (list (cons :a 1) (cons 'b 2) (cons "c" 3)) ((dash (&alist :a "c" 'b)) (progn (list a b c)))) => '(1 2 3) + ;; FIXME: Byte-compiler chokes on these in Emacs < 26. + (eval '(pcase (list 'foo 'bar) ((dash (&plist 'foo 1)) (progn (list foo))))) !!> error + (eval '(pcase (list :foo :bar) ((dash (&plist foo :bar)) (progn (list foo)))) t) !!> error + ;; test the &as form + (pcase (list 1 2 3) ((dash (items &as first . rest)) (progn (list first rest items)))) => '(1 (2 3) (1 2 3)) + (pcase (list [1 2] 3) ((dash (all &as [vect &as a b] bar)) (progn (list a b bar vect all)))) => '(1 2 3 [1 2] ([1 2] 3)) + (pcase (list (list 1 2) 3) ((dash (all &as (list &as a b) bar)) (progn (list a b bar list all)))) => '(1 2 3 (1 2) ((1 2) 3)) + (pcase (list (vector 1 2 3)) ((dash (x &as [a b])) (progn (list a b x)))) => '(1 2 ([1 2 3])) + (pcase (list [1 2] [3 4]) ((dash (result &as [_ a] [_ b])) (progn (list a b result)))) => '(2 4 ([1 2] [3 4])) + (pcase (list [1 2] [3 4]) ((dash (result &as [fst &as _ a] [snd &as _ b])) (progn (list a b fst snd result)))) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) + (pcase (vector 1 2 3) ((dash [x &as a b &rest r]) (progn (list a b r x)))) => '(1 2 [3] [1 2 3]) + (pcase (vector 1 2 3) ((dash [x &as a]) (progn (list a x)))) => '(1 [1 2 3]) + (pcase (vector 1 2 3) ((dash [x &as _ _ a]) (progn (list a x)))) => '(3 [1 2 3]) + (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ a]) (progn (list a x)))) => '((3 4) [1 2 (3 4)]) + (pcase (vector 1 2 (list 3 4)) ((dash [x &as _ _ (a b)]) (progn (list a b x)))) => '(3 4 [1 2 (3 4)]) + (pcase (cons 1 2) ((dash (b &as beg . end)) (progn (list beg end b)))) => '(1 2 (1 . 2)) + (pcase (list :a 1 :b 2) ((dash (plist &as &plist :a a :b b)) (progn (list a b plist)))) => '(1 2 (:a 1 :b 2)) + (pcase (list (cons :a 1) (cons :b 2)) ((dash (alist &as &alist :a a :b b)) (progn (list a b alist)))) => '(1 2 ((:a . 1) (:b . 2))) + (pcase (list 1 2 3 4 5 6 7 8 9 10 11 12) ((dash (list &as _ _ _ a _ _ _ b _ _ _ c)) (progn (list a b c list)))) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) + (pcase (list 1 2) ((dash (x &as a b)) (pcase (list 3 4) ((dash (y &as c d)) (progn (list a b c d x y)))))) + => '(1 2 3 4 (1 2) (3 4)) + (pcase (--doto (make-hash-table) (puthash :key "value" it)) ((dash (&hash-or-plist :key)) (progn key))) + => "value" + (pcase '(:key "value") ((dash (&hash-or-plist :key)) (progn key))) + => "value"))) (def-example-group "Side effects" "Functions iterating over lists for side effect only."