[latex3-commits] [git/LaTeX3-latex3-latex3] master: Tighten xparse signature checks: allow "!" only on trailing optionals (9b7df82)
Bruno Le Floch
bruno at le-floch.fr
Sun Sep 23 23:24:05 CEST 2018
Repository : https://github.com/latex3/latex3
On branch : master
Link : https://github.com/latex3/latex3/commit/9b7df82f382776ae6441924c55b157c7deba92f6
>---------------------------------------------------------------
commit 9b7df82f382776ae6441924c55b157c7deba92f6
Author: Bruno Le Floch <bruno at le-floch.fr>
Date: Sun Sep 23 18:47:49 2018 +0200
Tighten xparse signature checks: allow "!" only on trailing optionals
Previously "!" only applied to trailing optional but silently did nothing
on other arguments. Now it produces an error at definition time.
>---------------------------------------------------------------
9b7df82f382776ae6441924c55b157c7deba92f6
l3packages/xparse/testfiles/xparse001.ptex.tlg | 6 +-
l3packages/xparse/testfiles/xparse001.tlg | 6 +-
l3packages/xparse/testfiles/xparse001.uptex.tlg | 6 +-
l3packages/xparse/xparse.dtx | 208 ++++++++++++++---------
4 files changed, 132 insertions(+), 94 deletions(-)
diff --git a/l3packages/xparse/testfiles/xparse001.ptex.tlg b/l3packages/xparse/testfiles/xparse001.ptex.tlg
index 0338abf..306069d 100644
--- a/l3packages/xparse/testfiles/xparse001.ptex.tlg
+++ b/l3packages/xparse/testfiles/xparse001.ptex.tlg
@@ -577,9 +577,9 @@ l. ...}
* character.
*************************************************
> \foo=\protected macro:->\__xparse_start:nNNnnn {+t\par O{default}mmm}\foo
-\foo code {\__xparse_grab_t_long:w \par \__xparse_grab_D:w
-[]\__xparse_grab_m_3:w }{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl
-\c_novalue_tl \c_novalue_tl }{}.
+\foo code {\__xparse_grab_t:w \par \__xparse_grab_D:w []\__xparse_grab_m_3:w
+}{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl \c_novalue_tl
+\c_novalue_tl }{}.
<recently read> }
l. ...}
> \foo code=\protected\long macro:#1#2#3#4#5->(#1)(#2)(#3)(#4).
diff --git a/l3packages/xparse/testfiles/xparse001.tlg b/l3packages/xparse/testfiles/xparse001.tlg
index 2b00ced..d31420a 100644
--- a/l3packages/xparse/testfiles/xparse001.tlg
+++ b/l3packages/xparse/testfiles/xparse001.tlg
@@ -577,9 +577,9 @@ l. ...}
* character.
*************************************************
> \foo=\protected macro:->\__xparse_start:nNNnnn {+t\par O{default}mmm}\foo
-\foo code {\__xparse_grab_t_long:w \par \__xparse_grab_D:w
-[]\__xparse_grab_m_3:w }{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl
-\c_novalue_tl \c_novalue_tl }{}.
+\foo code {\__xparse_grab_t:w \par \__xparse_grab_D:w []\__xparse_grab_m_3:w
+}{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl \c_novalue_tl
+\c_novalue_tl }{}.
<recently read> }
l. ...}
> \foo code=\protected\long macro:#1#2#3#4#5->(#1)(#2)(#3)(#4).
diff --git a/l3packages/xparse/testfiles/xparse001.uptex.tlg b/l3packages/xparse/testfiles/xparse001.uptex.tlg
index 0338abf..306069d 100644
--- a/l3packages/xparse/testfiles/xparse001.uptex.tlg
+++ b/l3packages/xparse/testfiles/xparse001.uptex.tlg
@@ -577,9 +577,9 @@ l. ...}
* character.
*************************************************
> \foo=\protected macro:->\__xparse_start:nNNnnn {+t\par O{default}mmm}\foo
-\foo code {\__xparse_grab_t_long:w \par \__xparse_grab_D:w
-[]\__xparse_grab_m_3:w }{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl
-\c_novalue_tl \c_novalue_tl }{}.
+\foo code {\__xparse_grab_t:w \par \__xparse_grab_D:w []\__xparse_grab_m_3:w
+}{\c_novalue_tl {\prg_do_nothing: default}\c_novalue_tl \c_novalue_tl
+\c_novalue_tl }{}.
<recently read> }
l. ...}
> \foo code=\protected\long macro:#1#2#3#4#5->(#1)(#2)(#3)(#4).
diff --git a/l3packages/xparse/xparse.dtx b/l3packages/xparse/xparse.dtx
index c9685d2..d3001e1 100644
--- a/l3packages/xparse/xparse.dtx
+++ b/l3packages/xparse/xparse.dtx
@@ -953,6 +953,13 @@
% \end{macrocode}
% \end{variable}
%
+% \begin{variable}{\l_@@_ignore_spaces_bool}
+% For trailing optionals.
+% \begin{macrocode}
+\bool_new:N \l_@@_ignore_spaces_bool
+% \end{macrocode}
+% \end{variable}
+%
% \begin{variable}{\l_@@_last_delimiters_tl}
% Holds the delimiters (first tokens) of all optional arguments since
% the previous mandatory argument, to warn about cases where it would
@@ -971,13 +978,6 @@
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{\l_@@_ignore_spaces_bool}
-% For trailing optionals.
-% \begin{macrocode}
-\bool_new:N \l_@@_ignore_spaces_bool
-% \end{macrocode}
-% \end{variable}
-%
% \begin{variable}{\l_@@_m_args_int}
% The number of \texttt{m} arguments: if this is the same as the total
% number of arguments, then a short-cut can be taken in the creation of
@@ -1028,15 +1028,20 @@
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{\l_@@_some_long_bool, \l_@@_some_short_bool}
-% Both of these flags are set while normalizing the argument
-% specification. To grab arguments expandably, all short arguments
-% must appear before long arguments, so as soon as the first long
-% argument is seen (other than \texttt{t}-type, whose long status is
-% ignored) the \texttt{some_long} flag is set. The
-% \texttt{some_short} flag is used for expandable commands, to know
-% whether to define a short auxiliary too.
-% \begin{macrocode}
+% \begin{variable}
+% {\l_@@_some_ignore_spaces_bool, \l_@@_some_long_bool, \l_@@_some_short_bool}
+% These flags are set while normalizing the argument specification.
+% The \texttt{ignore_spaces} one is used to detect when |!| is used on
+% an argument that is not a trailing optional argument.
+% The other two are used to check whether all short arguments appear
+% before long arguments: this is needed to grab arguments expandably.
+% As soon as the first long argument is seen (other than
+% \texttt{t}-type, whose long status is ignored) the
+% \texttt{some_long} flag is set. The \texttt{some_short} flag is
+% used for expandable commands, to know whether to define a short
+% auxiliary too.
+% \begin{macrocode}
+\bool_new:N \l_@@_some_ignore_spaces_bool
\bool_new:N \l_@@_some_long_bool
\bool_new:N \l_@@_some_short_bool
% \end{macrocode}
@@ -1521,50 +1526,49 @@
% specification is valid before the main parsing run. If it is not
% valid the entire set up is abandoned to avoid any strange internal
% errors. A function is provided for each argument type that will grab
-% any extra arguments and call the loop function.
-%
-% The first thing that is done in the loop is to check that the various
-% argument types have the correct number of data items associated with
-% them. The opportunity is also taken to make sure that where a single
-% character is required, one has actually been supplied.
-%
-% The second is that processors and the marker~|+| for long arguments
-% must be followed by arguments.
-%
-% The third is to check for forbidden types for expandable commands,
-% namely \texttt{G}/\texttt{v} always, and \texttt{l}/\texttt{u} after
-% optional arguments (\pkg{xparse} may have inserted braces due to a
-% failed search for an optional argument).
-%
-% The fourth is that an optional argument should not be followed by a
-% mandatory argument with the same delimiter, as otherwise the optional
-% argument could never be omitted.
-%
-% The fifth is to keep track in \cs{l_@@_some_long_bool} and
-% \cs{l_@@_some_short_bool} of whether the command has some long/short
-% arguments.
-%
-% The sixth is to keep track in \cs{l_@@_grab_expandably_bool} of
-% whether all arguments are \texttt{m}/\texttt{l}/\texttt{u} type and
-% short arguments appear before long ones, in which case they can be
-% grabbed expandably just as safely as they could be grabbed expandably.
-% Regardless of that, arguments of expandable commands will be grabbed
-% expandably and arguments of environments will not (because the list of
-% arguments built by non-expandable grabbing is used to pass them to the
-% end-environment code).
-%
-% The last is to count mandatory arguments, used later to detect which
-% optional arguments are trailing.
+% any extra data items and call the loop function after performing the
+% following checks and tasks.
+% \begin{itemize}
+% \item Check that each argument has the correct number of data items
+% associated with it, and that where a single character is required,
+% one has actually been supplied.
+% \item Check that processors and the markers~|+| and~|!| are followed
+% by an argument for which they make sense, and are not redundant.
+% \item Check the absence of forbidden types for expandable commands,
+% namely \texttt{G}/\texttt{v} always, and \texttt{l}/\texttt{u}
+% after optional arguments (\pkg{xparse} may have inserted braces
+% due to a failed search for an optional argument).
+% \item Check that no optional argument is followed by a mandatory
+% argument with the same delimiter, as otherwise the optional
+% argument could never be omitted.
+% \item Keep track in \cs{l_@@_some_long_bool} and
+% \cs{l_@@_some_short_bool} of whether the command has some
+% long/short arguments.
+% \item Keep track in \cs{l_@@_grab_expandably_bool} of whether all
+% arguments are \texttt{m}/\texttt{l}/\texttt{u} type and short
+% arguments appear before long ones, in which case they can be
+% grabbed expandably just as safely as they could be grabbed
+% nonexpandably. Regardless of that, arguments of expandable
+% commands will be grabbed expandably and arguments of environments
+% will not (because the list of arguments built by non-expandable
+% grabbing is used to pass them to the end-environment code).
+% \item Count mandatory arguments, used later to detect which optional
+% arguments are trailing.
+% \end{itemize}
+% Further checks happen at the end of the loop:
+% \begin{itemize}
+% \item that there are at most $9$ arguments;
+% \item that an expandable command does not end with an optional
+% argument (this case is detected by using the fact that
+% \cs{l_@@_last_delimiters_tl} is cleared by every mandatory argument
+% and filled by every optional argument).
+% \end{itemize}
%
% \begin{macro}{\@@_normalize_arg_spec:n}
% \begin{macro}{\@@_normalize_arg_spec_loop:n}
% Loop through the argument specification, calling an auxiliary
% specific to each argument type. If any argument is unknown stop the
-% definition. After the loop, if there are more than
-% $9$ arguments, stop. Additionally, expandable commands may not end
-% with an optional argument; this case is detected by using the fact
-% that \cs{l_@@_last_delimiters_tl} is cleared by every mandatory
-% argument and filled by every optional argument.
+% definition.
% \begin{macrocode}
\cs_new_protected:Npn \@@_normalize_arg_spec:n #1
{
@@ -1573,10 +1577,11 @@
\tl_clear:N \l_@@_last_delimiters_tl
\tl_clear:N \l_@@_arg_spec_tl
\bool_set_true:N \l_@@_grab_expandably_bool
+ \bool_set_false:N \l_@@_ignore_spaces_bool
\bool_set_false:N \l_@@_long_bool
+ \bool_set_false:N \l_@@_some_ignore_spaces_bool
\bool_set_false:N \l_@@_some_long_bool
\bool_set_false:N \l_@@_some_short_bool
- \bool_set_false:N \l_@@_ignore_spaces_bool
\@@_normalize_arg_spec_loop:n #1
\q_recursion_tail \q_recursion_tail \q_recursion_tail \q_recursion_stop
\int_compare:nNnT \l_@@_current_arg_int > 9
@@ -1691,7 +1696,11 @@
\cs_new_protected:cpn { @@_normalize_type_+:w } #1
{
\quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn }
- \tl_put_right:Nn \l_@@_arg_spec_tl { + }
+ \bool_if:NT \l_@@_long_bool
+ {
+ \__kernel_msg_error:nnxx { xparse } { two-markers }
+ { \iow_char:N \\ \l_@@_function_tl } { + }
+ }
\bool_set_true:N \l_@@_long_bool
\int_decr:N \l_@@_current_arg_int
\@@_normalize_arg_spec_loop:n {#1}
@@ -1699,7 +1708,13 @@
\cs_new_protected:cpn { @@_normalize_type_!:w } #1
{
\quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn }
- \tl_put_right:Nn \l_@@_arg_spec_tl { ! }
+ \bool_if:NT \l_@@_ignore_spaces_bool
+ {
+ \__kernel_msg_error:nnxx { xparse } { two-markers }
+ { \iow_char:N \\ \l_@@_function_tl } { ! }
+ }
+ \bool_set_true:N \l_@@_ignore_spaces_bool
+ \bool_set_true:N \l_@@_some_ignore_spaces_bool
\int_decr:N \l_@@_current_arg_int
\@@_normalize_arg_spec_loop:n {#1}
}
@@ -1770,9 +1785,14 @@
{
\quark_if_recursion_tail_stop_do:Nn #1 { \@@_bad_arg_spec:wn }
\@@_single_char_check:n {#1}
- \tl_put_right:Nn \l_@@_arg_spec_tl { t #1 }
+ \tl_put_right:Nx \l_@@_arg_spec_tl
+ {
+ \bool_if:NT \l_@@_ignore_spaces_bool { ! }
+ t \exp_not:n {#1}
+ }
\tl_put_right:Nn \l_@@_last_delimiters_tl {#1}
\bool_set_false:N \l_@@_grab_expandably_bool
+ \bool_set_false:N \l_@@_ignore_spaces_bool
\bool_set_false:N \l_@@_long_bool
\@@_normalize_arg_spec_loop:n
}
@@ -1800,17 +1820,13 @@
\cs_new_protected:Npn \@@_normalize_type_l:w
{
\@@_normalize_check_lu:N l
- \@@_add_arg_spec:n { l }
- \int_incr:N \l_@@_mandatory_args_int
- \tl_clear:N \l_@@_last_delimiters_tl
+ \@@_add_arg_spec_mandatory:n { l }
\@@_normalize_arg_spec_loop:n
}
\cs_new_protected:Npn \@@_normalize_type_m:w
{
\@@_delimiter_check:nnn { } { m } { \iow_char:N \{ }
- \@@_add_arg_spec:n { m }
- \int_incr:N \l_@@_mandatory_args_int
- \tl_clear:N \l_@@_last_delimiters_tl
+ \@@_add_arg_spec_mandatory:n { m }
\@@_normalize_arg_spec_loop:n
}
\cs_new_protected:Npn \@@_normalize_type_R:w #1#2#3
@@ -1819,27 +1835,21 @@
\@@_single_char_check:n {#1}
\@@_single_char_check:n {#2}
\@@_delimiter_check:nnn {#1} { R/r } { \tl_to_str:n {#1} }
- \@@_add_arg_spec:n { R #1 #2 {#3} }
- \int_incr:N \l_@@_mandatory_args_int
- \tl_clear:N \l_@@_last_delimiters_tl
\bool_set_false:N \l_@@_grab_expandably_bool
+ \@@_add_arg_spec_mandatory:n { R #1 #2 {#3} }
\@@_normalize_arg_spec_loop:n
}
\cs_new_protected:Npn \@@_normalize_type_u:w #1
{
\quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn }
\@@_normalize_check_lu:N u
- \@@_add_arg_spec:n { u {#1} }
- \int_incr:N \l_@@_mandatory_args_int
- \tl_clear:N \l_@@_last_delimiters_tl
+ \@@_add_arg_spec_mandatory:n { u {#1} }
\@@_normalize_arg_spec_loop:n
}
\cs_new_protected:Npn \@@_normalize_type_v:w
{
\@@_normalize_check_gv:N v
- \@@_add_arg_spec:n { v }
- \int_incr:N \l_@@_mandatory_args_int
- \tl_clear:N \l_@@_last_delimiters_tl
+ \@@_add_arg_spec_mandatory:n { v }
\@@_normalize_arg_spec_loop:n
}
% \end{macrocode}
@@ -1936,13 +1946,16 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_add_arg_spec:n}
+% \begin{macro}{\@@_add_arg_spec:n, \@@_add_arg_spec_mandatory:n}
% When adding an argument to the argument specification, set the
% \texttt{some_long} or \texttt{some_short} booleans as appropriate
-% and clear the boolean keeping track of whether the argument is long.
+% and clear the booleans keeping track of |+| and |!| markers.
% Before that, test for a short argument following some long
% arguments: this is forbidden for expandable commands and prevents
% grabbing arguments expandably.
+%
+% For mandatory arguments do some more work, in particular complain if
+% they were preceeded by~|!|.
% \begin{macrocode}
\cs_new_protected:Npn \@@_add_arg_spec:n #1
{
@@ -1961,8 +1974,26 @@
\bool_if:NTF \l_@@_long_bool
{ \bool_set_true:N \l_@@_some_long_bool }
{ \bool_set_true:N \l_@@_some_short_bool }
+ \tl_put_right:Nx \l_@@_arg_spec_tl
+ {
+ \bool_if:NT \l_@@_long_bool { + }
+ \bool_if:NT \l_@@_ignore_spaces_bool { ! }
+ \exp_not:n {#1}
+ }
\bool_set_false:N \l_@@_long_bool
- \tl_put_right:Nn \l_@@_arg_spec_tl {#1}
+ \bool_set_false:N \l_@@_ignore_spaces_bool
+ }
+\cs_new_protected:Npn \@@_add_arg_spec_mandatory:n #1
+ {
+ \bool_if:NT \l_@@_some_ignore_spaces_bool
+ {
+ \__kernel_msg_error:nnxx { xparse } { ignore-space-non-trailing }
+ { \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} }
+ \@@_bad_def:wn
+ }
+ \int_incr:N \l_@@_mandatory_args_int
+ \tl_clear:N \l_@@_last_delimiters_tl
+ \@@_add_arg_spec:n {#1}
}
% \end{macrocode}
% \end{macro}
@@ -1972,8 +2003,8 @@
% \begin{macro}{\@@_prepare_signature:n}
% \begin{macro}{\@@_prepare_signature:N}
% \begin{macro}{\@@_prepare_signature_bypass:N}
-% Actually creating the signature uses the same loop approach as counting
-% up mandatory arguments. There are first a number of variables which need
+% Actually creating the signature uses the same loop approach as
+% normalizing the signature. There are first a number of variables which need
% to be set to track what is going on. Many of these variables are unused
% when defining expandable commands.
% \begin{macrocode}
@@ -3012,19 +3043,15 @@
% \end{macro}
%
% \begin{macro}{\@@_grab_t:w}
-% \begin{macro}{\@@_grab_t_long:w}
% \begin{macro}{\@@_grab_t_ignore_spaces:w}
-% \begin{macro}{\@@_grab_t_long_ignore_spaces:w}
% \begin{macro}{\@@_grab_t_aux:NNw}
% Dealing with a token is quite easy. Check the match, remove the
% token if needed and add a flag to the output.
% \begin{macrocode}
\cs_new_protected:Npn \@@_grab_t:w
{ \@@_grab_t_aux:NNw \peek_meaning_remove_ignore_spaces:NTF }
-\cs_new_eq:NN \@@_grab_t_long:w \@@_grab_t:w
\cs_new_protected:Npn \@@_grab_t_ignore_spaces:w
{ \@@_grab_t_aux:NNw \peek_meaning_remove:NTF }
-\cs_new_eq:NN \@@_grab_t_long_ignore_spaces:w \@@_grab_t_ignore_spaces:w
\cs_new_protected:Npn \@@_grab_t_aux:NNw #1#2#3 \@@_run_code:
{
\tl_set:Nn \l_@@_signature_tl {#3}
@@ -3040,8 +3067,6 @@
% \end{macro}
% \end{macro}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
% \begin{macro}{\@@_grab_u:w}
% \begin{macro}{\@@_grab_u_long:w}
@@ -4152,6 +4177,13 @@
The~default~values~of~two~or~more~arguments~of~'#1'~depend~on~each~
other~in~a~way~that~cannot~be~resolved.
}
+\__kernel_msg_new:nnnn { xparse } { ignore-space-non-trailing }
+ { Prefix~'!'~used~before~mandatory~argument~'#2'~of~command~'#1'. }
+ {
+ The~prefix~'!'~can~only~apply~to~trailing~optional~arguments.
+ \\ \\
+ LaTeX~will~ignore~this~entire~definition.
+ }
\__kernel_msg_new:nnnn { xparse } { missing-required }
{ Failed~to~find~required~argument~starting~with~'#2'~for~command~'#1'. }
{
@@ -4219,6 +4251,12 @@
This~cannot~be~implemented. \\ \\
LaTeX~will~ignore~this~entire~definition.
}
+\__kernel_msg_new:nnnn { xparse } { two-markers }
+ { Two~'#2'~apply~to~the~same~argument~in~argument~specification~of~command~'#1'. }
+ {
+ The~argument~specification~provided~has~two~markers~'#2'~applying~
+ to~the~same~argument;~these~are~redundant.
+ }
\__kernel_msg_new:nnnn { xparse } { unknown-argument-type }
{ Unknown~argument~type~'#2'~for~the~command~'#1'. }
{
More information about the latex3-commits
mailing list