From 19d25b8009801aa98ea2f46b45c37c257f990491 Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Wed, 4 Mar 2020 03:02:32 +0000
Subject: CTAN sync 202003040302

---
 macros/latex/contrib/l3packages/xparse.dtx | 282 +++++++++++++++++++++++------
 1 file changed, 231 insertions(+), 51 deletions(-)

(limited to 'macros/latex/contrib/l3packages/xparse.dtx')

diff --git a/macros/latex/contrib/l3packages/xparse.dtx b/macros/latex/contrib/l3packages/xparse.dtx
index fc0164a8cd..279b2e58c0 100644
--- a/macros/latex/contrib/l3packages/xparse.dtx
+++ b/macros/latex/contrib/l3packages/xparse.dtx
@@ -67,7 +67,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2020-02-25}
+% \date{Released 2020-03-03}
 %
 % \maketitle
 %
@@ -130,12 +130,12 @@
 %     Regardless of the input, the argument will be passed to the
 %     internal code without the outer braces. This is the \pkg{xparse}
 %     type specifier for a normal \TeX{} argument.
-%   \item[r] Given as \texttt{r}\meta{char1}\meta{char2}, this denotes a
+%   \item[r] Given as \texttt{r}\meta{token1}\meta{token2}, this denotes a
 %     \enquote{required} delimited argument, where the delimiters are
-%     \meta{char1} and \meta{char2}. If the opening delimiter
-%     \meta{char1} is missing, the default marker |-NoValue-| will be
+%     \meta{token1} and \meta{token2}. If the opening delimiter
+%     \meta{token1} is missing, the default marker |-NoValue-| will be
 %     inserted after a suitable error.
-%   \item[R] Given as \texttt{R}\meta{char1}\meta{char2}\marg{default},
+%   \item[R] Given as \texttt{R}\meta{token1}\meta{token2}\marg{default},
 %     this is a \enquote{required} delimited argument as for~\texttt{r},
 %     but it has a user-definable recovery \meta{default} instead of
 %     |-NoValue-|.
@@ -158,30 +158,30 @@
 %   \item[o] A standard \LaTeX{} optional argument, surrounded with square
 %     brackets, which will supply
 %     the special |-NoValue-| marker if not given (as described later).
-%   \item[d] Given as \texttt{d}\meta{char1}\meta{char2}, an optional
-%     argument which is delimited by \meta{char1} and \meta{char2}.
+%   \item[d] Given as \texttt{d}\meta{token1}\meta{token2}, an optional
+%     argument which is delimited by \meta{token1} and \meta{token2}.
 %     As with \texttt{o}, if no
 %     value is given the special marker |-NoValue-| is returned.
 %   \item[O] Given as \texttt{O}\marg{default}, is like \texttt{o}, but
 %     returns \meta{default} if no value is given.
-%   \item[D] Given as \texttt{D}\meta{char1}\meta{char2}\marg{default},
+%   \item[D] Given as \texttt{D}\meta{token1}\meta{token2}\marg{default},
 %     it is as for \texttt{d}, but returns \meta{default} if no value is given.
 %     Internally, the \texttt{o}, \texttt{d} and \texttt{O} types are
 %     short-cuts to an appropriated-constructed \texttt{D} type argument.
 %   \item[s] An optional star, which will result in a value
 %     \cs{BooleanTrue} if a star is present and \cs{BooleanFalse}
 %     otherwise (as described later).
-%   \item[t] An optional \meta{char}, which will result in a value
-%     \cs{BooleanTrue} if \meta{char} is present and \cs{BooleanFalse}
-%     otherwise. Given as \texttt{t}\meta{char}.
-%   \item[e] Given as \texttt{e}\marg{chars}, a set of optional
+%   \item[t] An optional \meta{token}, which will result in a value
+%     \cs{BooleanTrue} if \meta{token} is present and \cs{BooleanFalse}
+%     otherwise. Given as \texttt{t}\meta{token}.
+%   \item[e] Given as \texttt{e}\marg{tokens}, a set of optional
 %     \emph{embellishments}, each of which requires a \emph{value}.
 %     If an embellishment is not present, |-NoValue-| is returned.  Each
 %     embellishment gives one argument, ordered as for the list of
-%     \meta{chars} in the argument specification.  All \meta{chars}
+%     \meta{tokens} in the argument specification.  All \meta{tokens}
 %     must be distinct.  \emph{This is an experimental type}.
 %   \item[E] As for \texttt{e} but returns one or more \meta{defaults}
-%     if values are not given: \texttt{E}\marg{chars}\marg{defaults}. See
+%     if values are not given: \texttt{E}\marg{tokens}\marg{defaults}. See
 %     Section~\ref{sec:embellishment} for more details.
 % \end{itemize}
 %
@@ -213,7 +213,9 @@
 %   \foo[[]         % Error: missing closing "]"
 % \end{verbatim}
 % Also note that |{| and |}| cannot be used as delimiters as they are used
-% by \TeX{} as grouping tokens. Arguments to be grabbed inside these tokens
+% by \TeX{} as grouping tokens. Implicit begin- or end-group tokens (\emph{e.g.},
+% |\bgroup| and |\egroup|) are not allowed for delimited argument tipes.
+% Arguments to be grabbed inside these tokens
 % must be created as either \texttt{m}- or \texttt{g}-type arguments.
 %
 % Within delimited arguments, non-balanced or otherwise awkward tokens may
@@ -371,13 +373,13 @@
 % \subsection{Default values for \enquote{embellishments}}
 % \label{sec:embellishment}
 %
-% The \texttt{E}-type argument allows one default value per test character.
+% The \texttt{E}-type argument allows one default value per test token.
 % This is achieved by giving a list of defaults for each entry in the
 % list, for example:
 % \begin{verbatim}
 %   E{^_}{{UP}{DOWN}}
 % \end{verbatim}
-% If the list of default values is \emph{shorter} than the list of test characters,
+% If the list of default values is \emph{shorter} than the list of test tokens,
 % the special |-NoValue-| marker will be returned (as for the \texttt{e}-type
 % argument). Thus for example
 % \begin{verbatim}
@@ -487,6 +489,66 @@
 %     is given: \texttt{G}\marg{default}.
 % \end{itemize}
 %
+% \subsection{Details about argument delimiters}
+%
+% In normal (non-expandable) commands, the delimited types look for the
+% initial delimiter by peeking ahead (using \pkg{expl3}'s |\peek_...|
+% functions) looking for the delimiter token.  The token has to have the
+% same meaning and \enquote{shape} of the token defined as delimiter.
+% There are three possible cases of delimiters: character tokens, control
+% sequence tokens, and active character tokens.  For all practical purposes
+% of this description, active character tokens will behave exactly as
+% control sequence tokens.
+%
+% \subsubsection{Character tokens}
+%
+% A character token is characterised by its character code, and its meaning
+% is the category code~(|\catcode|).  When a command is defined, the meaning
+% of the character token is fixed into the definition of the command and
+% cannot change.  A command will correctly see an argument delimiter if
+% the open delimiter has the same character and category codes as at the
+% time of the definition.  For example in:
+% \begin{verbatim}
+%   \NewDocumentCommand { \foobar } { D<>{default} } {(#1)}
+%   \foobar <hello> \par
+%   \char_set_catcode_letter:N <
+%   \foobar <hello>
+% \end{verbatim}
+% the output would be:
+% \begin{verbatim}
+%   (hello)
+%   (default)<hello>
+% \end{verbatim}
+% as the open-delimter |<| changed in meaning between the two calls to
+% |\foobar|, so the second one doesn't see the |<| as a valid delimiter.
+% Commands assume that if a valid open-delimiter was found, a matching
+% close-delimiter will also be there.  If it is not (either by being
+% omitted or by changing in meaning), a low-level \TeX{} error is raised
+% and the command call is aborted.
+%
+% \subsubsection{Control sequence tokens}
+%
+% A control sequence (or control character) token is characterised by is
+% its name, and its meaning is its definition.
+% A token cannot have two different meanings at the same time.
+% When a control sequence is defined as delimiter in a command,
+% it will be detected as delimiter whenever the control sequence name
+% is found in the document regardless of its current definition.
+% For example in:
+% \begin{verbatim}
+%   \cs_set:Npn \x { abc }
+%   \NewDocumentCommand { \foobar } { D\x\y{default} } {(#1)}
+%   \foobar \x hello\y \par
+%   \cs_set:Npn \x { def }
+%   \foobar \x hello\y
+% \end{verbatim}
+% the output would be:
+% \begin{verbatim}
+%   (hello)
+%   (hello)
+% \end{verbatim}
+% with both calls to the command seeing the delimiter |\x|.
+%
 % \section{Declaring commands and environments}
 %
 % With the concept of an argument specifier defined, it is now
@@ -916,7 +978,7 @@
 %    \end{macrocode}
 %
 %    \begin{macrocode}
-\ProvidesExplPackage{xparse}{2020-02-25}{}
+\ProvidesExplPackage{xparse}{2020-03-03}{}
   {L3 Experimental document command parser}
 %    \end{macrocode}
 %
@@ -1906,8 +1968,8 @@
 \cs_new_protected:Npn \@@_normalize_type_D:w #1#2#3
   {
     \quark_if_recursion_tail_stop_do:nn {#3} { \@@_bad_arg_spec:wn }
-    \@@_single_char_check:n {#1}
-    \@@_single_char_check:n {#2}
+    \@@_single_token_check:n {#1} \@@_allowed_token_check:N #1
+    \@@_single_token_check:n {#2}
     \@@_add_arg_spec:n { D #1 #2 {#3} }
     \tl_put_right:Nn \l_@@_last_delimiters_tl {#1}
     \bool_set_false:N \l_@@_grab_expandably_bool
@@ -1917,7 +1979,8 @@
   {
     \quark_if_recursion_tail_stop_do:nn {#2} { \@@_bad_arg_spec:wn }
     \tl_if_blank:nT {#1} { \@@_bad_arg_spec:wn }
-    \tl_map_function:nN {#1} \@@_single_char_check:n
+    \tl_map_function:nN {#1} \@@_single_token_check:n
+    \tl_map_function:nN {#1} \@@_allowed_token_check:N
     \@@_normalize_E_unique_check:w #1 \q_nil \q_stop
     \int_compare:nNnT { \tl_count:n {#2} } > { \tl_count:n {#1} }
       { \@@_bad_arg_spec:wn }
@@ -1946,7 +2009,7 @@
 \cs_new_protected:Npn \@@_normalize_type_t:w #1
   {
     \quark_if_recursion_tail_stop_do:Nn #1 { \@@_bad_arg_spec:wn }
-    \@@_single_char_check:n {#1}
+    \@@_single_token_check:n {#1} \@@_allowed_token_check:N #1
     \tl_put_right:Nx \l_@@_arg_spec_tl
       {
         \bool_if:NT \l_@@_obey_spaces_bool { ! }
@@ -1994,8 +2057,8 @@
 \cs_new_protected:Npn \@@_normalize_type_R:w #1#2#3
   {
     \quark_if_recursion_tail_stop_do:nn {#3} { \@@_bad_arg_spec:wn }
-    \@@_single_char_check:n {#1}
-    \@@_single_char_check:n {#2}
+    \@@_single_token_check:n {#1} \@@_allowed_token_check:N #1
+    \@@_single_token_check:n {#2}
     \@@_delimiter_check:nnn {#1} { R/r } { \tl_to_str:n {#1} }
     \bool_set_false:N \l_@@_grab_expandably_bool
     \@@_add_arg_spec_mandatory:n { R #1 #2 {#3} }
@@ -2042,27 +2105,53 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\@@_single_char_check:n}
-%   Checks that what should be single characters really are single
-%   characters (possibly surrounded by spaces).
+% \begin{macro}{\@@_single_token_check:n}
+%   Checks that the argument is a single (non-space) token (possibly
+%   surrounded by spaces), and aborts the definition otherwise.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_single_char_check:n #1
+\cs_new_protected:Npn \@@_single_token_check:n #1
   {
-    \tl_trim_spaces_apply:nN {#1} \tl_if_single_token:nTF
+    \tl_trim_spaces_apply:nN {#1} \tl_if_single_token:nF
       {
-        \group_begin:
-        \tex_escapechar:D = 92 \scan_stop:
-        \exp_args:Nx \tl_if_single_token:nF
-          { \exp_args:No \tl_to_str:n { \use:nn #1 { } } }
-          {
-            \__kernel_msg_warning:nnxx { xparse } { not-single-char }
-              { \@@_environment_or_command: } { \tl_to_str:n {#1} }
-          }
-        \group_end:
+        \__kernel_msg_error:nnxx { xparse } { not-single-token }
+          { \@@_environment_or_command: } { \tl_to_str:n {#1} }
+        \@@_bad_def:wn
       }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_allowed_token_check:N}
+%   Some tokens are now allowed as delimiters for some argument types,
+%   notably implicit begin/end-group tokens (|\bgroup|/|\egroup|).
+%   The major problem with these tokens is that for |\peek_...| functions,
+%   a literal~|{|$_1$. is virtually indistinguishable from a |\bgroup| or
+%   other token which was |\let| to a~|{|$_1$, and the same goes
+%   for~|}|$_2$.  All other tokens can be easily distingushed from their
+%   implicit counterparts by grabbing them and looking at the string
+%   length (see \cs{@@_token_if_cs:NTF}), but for begin/end group tokens
+%   that is not possible without the risk of mistakenly grabbing the
+%   entire brace group (potentially leading to a~\texttt{!~Runaway argument}
+%   error) or trying to grab a |}|$_2$, leading to a~\texttt{!~Argument
+%   of~\dots~has an extra~|}|}.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_allowed_token_check:N #1
+  {
+    \token_if_eq_meaning:NNTF #1 \c_group_begin_token
+        { \use:n }
+        {
+          \token_if_eq_meaning:NNTF #1 \c_group_end_token
+            { \use:n }
+            { \use_none:n }
+        }
       {
-        \__kernel_msg_error:nnxx { xparse } { not-single-char }
+        \__kernel_msg_error:nnxxx
+          { xparse } { forbidden-implicit-group-token }
           { \@@_environment_or_command: } { \tl_to_str:n {#1} }
+          {
+            \token_if_eq_meaning:NNTF #1 \c_group_begin_token
+              { begin } { end }
+          }
         \@@_bad_def:wn
       }
   }
@@ -2853,12 +2942,12 @@
 \cs_new_protected:Npn \@@_grab_D_obey_spaces:w #1#2#3 \@@_run_code:
   {
     \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected_nopar:Npn
-      \peek_meaning_remove:NTF
+      \@@_peek_meaning_remove:NTF
   }
 \cs_new_protected:Npn \@@_grab_D_long_obey_spaces:w #1#2#3 \@@_run_code:
   {
     \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected:Npn
-      \peek_meaning_remove:NTF
+      \@@_peek_meaning_remove:NTF
   }
 %    \end{macrocode}
 % \begin{macro}{\@@_grab_D_aux:NNnNN}
@@ -2991,6 +3080,12 @@
 %   the grabber. (This assumes that the open and close delimiters are not the
 %   same character with different category codes, but that really should not
 %   happen in any sensible document-level syntax.)
+%   An exception is when |#1| is a control sequence token, in which case the
+%   character-token treatment is no good because if hit with \cs{token_to_str:N}
+%   it would add sputios tokens to the argument. In this case a different
+%   branch is taken. The token inserted is then the same \meta{csname} as |#1|,
+%   but with a space appended, so that the grabber don't see it as another
+%   of the same delimiter.
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_grab_D_call:Nw #1
   {
@@ -3000,8 +3095,15 @@
           \l_@@_fn_tl \char_generate:nn { `#1 } { 11 }
       }
       {
-        \exp_after:wN \l_@@_fn_tl
-        \token_to_str:N #1
+        \@@_token_if_cs:NTF #1
+          {
+            \exp_after:wN \l_@@_fn_tl
+            \cs:w \cs_to_str:N #1 ~ \cs_end:
+          }
+          {
+            \exp_after:wN \l_@@_fn_tl
+            \token_to_str:N #1
+          }
       }
   }
 %    \end{macrocode}
@@ -3033,13 +3135,13 @@
   {
     \@@_grab_E:nnNN {#1} {#2}
       \cs_set_protected_nopar:Npn
-      \peek_meaning_remove:NTF
+      \@@_peek_meaning_remove:NTF
   }
 \cs_new_protected:Npn \@@_grab_E_long_obey_spaces:w #1#2 \@@_run_code:
   {
     \@@_grab_E:nnNN {#1} {#2}
       \cs_set_protected:Npn
-      \peek_meaning_remove:NTF
+      \@@_peek_meaning_remove:NTF
   }
 %    \end{macrocode}
 %   A loop is needed here to allow a random ordering of keys. These are
@@ -3294,7 +3396,7 @@
 \cs_new_protected:Npn \@@_grab_t:w
   { \@@_grab_t_aux:NNw \@@_peek_nonspace_remove:NTF }
 \cs_new_protected:Npn \@@_grab_t_obey_spaces:w
-  { \@@_grab_t_aux:NNw \peek_meaning_remove:NTF }
+  { \@@_grab_t_aux:NNw \@@_peek_meaning_remove:NTF }
 \cs_new_protected:Npn \@@_grab_t_aux:NNw #1#2#3 \@@_run_code:
   {
     \tl_set:Nn \l_@@_signature_tl {#3}
@@ -4282,6 +4384,28 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_token_if_cs:NTF}
+%   Based on the definition of \cs{@@_check_definable_aux:nN} above, but
+%   only checks for an actual control sequence (\emph{i.e.},
+%   \cs[no-index]{\meta{anything}}). \cs{tex_escapechar:D} is
+%   temporarily changed to a known value and then it checks if
+%   |\string#1| contains more than one character: if it does, it's a
+%   control sequence.  This test differs from \cs{token_if_cs:NTF} for
+%   example in \verb|\token_if_cs:NTF \c_group_begin_token {T}{F}|,
+%   where \cs{token_if_cs:NTF} returns false.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_token_if_cs:NTF #1
+  {
+    \group_begin:
+      \int_set:Nn \tex_escapechar:D { 92 }
+      \exp_args:Nx \tl_if_empty:nTF
+          { \exp_args:No \str_tail:n { \token_to_str:N #1 } }
+        { \group_end: \use_ii:nn }
+        { \group_end: \use_i:nn }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\@@_tl_mapthread_function:NNN, \@@_tl_mapthread_function:nnN}
 % \begin{macro}{\@@_tl_mapthread_loop:w}
 %   Analogue of \cs{seq_mapthread_function:NNN} for token lists.
@@ -4351,9 +4475,9 @@
 %   \cs{peek_meaning_remove:NTF}.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_peek_nonspace:NTF
-  { \@@_peek_nonspace_aux:nNNTF { } \peek_meaning:NTF }
+  { \@@_peek_nonspace_aux:nNNTF { } \@@_peek_meaning:NTF }
 \cs_new_protected:Npn \@@_peek_nonspace_remove:NTF
-  { \@@_peek_nonspace_aux:nNNTF { } \peek_meaning_remove:NTF }
+  { \@@_peek_nonspace_aux:nNNTF { } \@@_peek_meaning_remove:NTF }
 \cs_new_protected:Npn \@@_peek_nonspace_aux:nNNTF #1#2#3#4#5
   {
     \peek_meaning_remove:NTF \c_space_token
@@ -4363,6 +4487,55 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_peek_meaning:NTF, \@@_peek_meaning_remove:NTF}
+% \begin{macro}{\@@_peek_cs_check_equal:NNN, \@@_peek_meaning_aux:NNTF, \@@_peek_true_remove:NNw}
+%   Peek ahead for a token with a given meaning.  In case the search
+%   token is a control sequence, also check that the \meta{csname} is
+%   the same as the control sequence peeked at.  This extra verification
+%   is necessary when the command is delimited by control sequence tokens
+%   (as opposed to character tokens), and we want the the exact same
+%   control sequence to match.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_meaning:NTF
+  { \@@_peek_meaning_aux:NNTF \c_false_bool }
+\cs_new_protected:Npn \@@_peek_meaning_remove:NTF
+  { \@@_peek_meaning_aux:NNTF \c_true_bool }
+\cs_new_protected:Npn \@@_peek_meaning_aux:NNTF #1#2#3#4
+  {
+    \tl_set:Nn \l_@@_tmpa_tl {#3}
+    \tl_set:Nn \l_@@_tmpb_tl {#4}
+    \peek_meaning:NTF #2
+      {
+        \token_if_eq_meaning:NNTF #2 \c_group_begin_token
+          { \@@_peek_true_remove:Nw #1 }
+          {
+            \@@_token_if_cs:NTF #2
+              { \@@_peek_cs_check_equal:NNN #1 #2 }
+              { \@@_peek_true_remove:Nw #1 }
+          }
+      }
+      { \l_@@_tmpb_tl }
+  }
+\cs_new_protected:Npn \@@_peek_cs_check_equal:NNN #1#2#3
+  {
+    \str_if_eq:nnTF {#2} {#3}
+      { \@@_peek_true_remove:Nw #1 }
+      { \l_@@_tmpb_tl }
+    #3
+  }
+\cs_new_protected:Npn \@@_peek_true_remove:Nw #1
+  {
+    \bool_if:NTF #1
+      {
+        \tex_afterassignment:D \l_@@_tmpa_tl
+        \cs_set_eq:NN \@@_tmp:w
+      }
+      { \l_@@_tmpa_tl }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \subsection{Messages}
 %
 % \begin{macro}{\@@_environment_or_command:}
@@ -4493,14 +4666,21 @@
     token.~Perhaps~a~backslash~is~missing?
     \c_@@_ignore_def_tl
   }
-\__kernel_msg_new:nnnn { xparse } { not-single-char }
+\__kernel_msg_new:nnnn { xparse } { not-single-token }
   {
     Argument~delimiter~'#2'~for~the~#1~should~be~
-    a~single~character.
+    a~single~non-space~token.
   }
   {
     The~argument~specification~provided~was~not~valid:~in~a~place~
-    where~a~single~character~is~required,~LaTeX~found~'#2'.
+    where~a~single~token~is~required,~LaTeX~found~'#2'.
+    \c_@@_ignore_def_tl
+  }
+\__kernel_msg_new:nnnn { xparse } { forbidden-implicit-group-token }
+  { Argument~delimiter~'#2'~for~the~#1~is~not~allowed. }
+  {
+    The~argument~specification~provided~was~not~valid:~the~implicit~
+    #3-group~token~'#2'~is~not~allowed~as~an~argument~delimiter.
     \c_@@_ignore_def_tl
   }
 \__kernel_msg_new:nnnn { xparse } { processor-in-expandable }
-- 
cgit v1.2.3

