[latex3-commits] [git/LaTeX3-latex3-latex3] master: Optimize simple conditionals automatically (11c919f)
Bruno Le Floch
bruno at le-floch.fr
Mon Apr 30 21:18:57 CEST 2018
Repository : https://github.com/latex3/latex3
On branch : master
Link : https://github.com/latex3/latex3/commit/11c919fae62da26ab77227407c6bae709f3a45c6
>---------------------------------------------------------------
commit 11c919fae62da26ab77227407c6bae709f3a45c6
Author: Bruno Le Floch <bruno at le-floch.fr>
Date: Mon Apr 30 15:17:00 2018 -0400
Optimize simple conditionals automatically
This optimizes (slightly) the case of conditionals that end with
...
\prg_return_true:
\else:
\prg_return_false:
\fi:
by replacing this code fragment (and the extra tokens that are
inserted for various conditional forms) by faster code with
\exp_after:wN and some \use_ii:nn.
>---------------------------------------------------------------
11c919fae62da26ab77227407c6bae709f3a45c6
l3kernel/l3basics.dtx | 167 +++++++++++++++++++-----------
l3kernel/l3int.dtx | 6 +-
l3kernel/l3tl.dtx | 14 +--
l3kernel/testfiles/m3token002.luatex.tlg | 8 +-
l3kernel/testfiles/m3token002.tlg | 8 +-
5 files changed, 122 insertions(+), 81 deletions(-)
diff --git a/l3kernel/l3basics.dtx b/l3kernel/l3basics.dtx
index c351720..330a149 100644
--- a/l3kernel/l3basics.dtx
+++ b/l3kernel/l3basics.dtx
@@ -2131,7 +2131,7 @@
% \prg_set_protected_conditional:Npnn ,
% \prg_new_protected_conditional:Npnn ,
% }
-% \begin{macro}{\@@_generate_conditional_parm:nnNpnn}
+% \begin{macro}{\@@_generate_conditional_parm:NNNpnn}
% The user functions for the types using parameter text from the
% programmer. The various functions only differ by which function is
% used for the assignment. For those |Npnn| type functions, we must
@@ -2141,23 +2141,24 @@
% \Arg{set~or~new} \Arg{maybe~protected} \Arg{parameters} |{TF,...}|
% \Arg{code} to the auxiliary function responsible for defining all
% conditionals.
+% Note that |e| stands for expandable and |p| for protected.
% \begin{macrocode}
\cs_set_protected:Npn \prg_set_conditional:Npnn
- { \@@_generate_conditional_parm:nnNpnn { set } { } }
+ { \@@_generate_conditional_parm:NNNpnn \cs_set:Npn e }
\cs_set_protected:Npn \prg_new_conditional:Npnn
- { \@@_generate_conditional_parm:nnNpnn { new } { } }
+ { \@@_generate_conditional_parm:NNNpnn \cs_new:Npn e }
\cs_set_protected:Npn \prg_set_protected_conditional:Npnn
- { \@@_generate_conditional_parm:nnNpnn { set } { _protected } }
+ { \@@_generate_conditional_parm:NNNpnn \cs_set_protected:Npn p }
\cs_set_protected:Npn \prg_new_protected_conditional:Npnn
- { \@@_generate_conditional_parm:nnNpnn { new } { _protected } }
-\cs_set_protected:Npn \@@_generate_conditional_parm:nnNpnn #1#2#3#4#
+ { \@@_generate_conditional_parm:NNNpnn \cs_new_protected:Npn p }
+\cs_set_protected:Npn \@@_generate_conditional_parm:NNNpnn #1#2#3#4#
{
\use:x
{
- \@@_generate_conditional:nnNnnnnn
+ \@@_generate_conditional:nnNNNnnn
\cs_split_function:N #3
}
- {#1} {#2} {#4}
+ #1 #2 {#4}
}
% \end{macrocode}
% \end{macro}
@@ -2172,8 +2173,8 @@
% }
% \begin{macro}
% {
-% \@@_generate_conditional_count:nnNnn ,
-% \@@_generate_conditional_count:nnNnnnn
+% \@@_generate_conditional_count:NNNnn ,
+% \@@_generate_conditional_count:nnNNNnn
% }
% The user functions for the types automatically inserting the correct
% parameter text based on the signature. The various functions only
@@ -2189,26 +2190,26 @@
% later.
% \begin{macrocode}
\cs_set_protected:Npn \prg_set_conditional:Nnn
- { \@@_generate_conditional_count:nnNnn { set } { } }
+ { \@@_generate_conditional_count:NNNnn \cs_set:Npn e }
\cs_set_protected:Npn \prg_new_conditional:Nnn
- { \@@_generate_conditional_count:nnNnn { new } { } }
+ { \@@_generate_conditional_count:NNNnn \cs_new:Npn e }
\cs_set_protected:Npn \prg_set_protected_conditional:Nnn
- { \@@_generate_conditional_count:nnNnn { set } { _protected } }
+ { \@@_generate_conditional_count:NNNnn \cs_set_protected:Npn p }
\cs_set_protected:Npn \prg_new_protected_conditional:Nnn
- { \@@_generate_conditional_count:nnNnn { new } { _protected } }
-\cs_set_protected:Npn \@@_generate_conditional_count:nnNnn #1#2#3
+ { \@@_generate_conditional_count:NNNnn \cs_new_protected:Npn p }
+\cs_set_protected:Npn \@@_generate_conditional_count:NNNnn #1#2#3
{
\use:x
{
- \@@_generate_conditional_count:nnNnnnn
+ \@@_generate_conditional_count:nnNNNnn
\cs_split_function:N #3
}
- {#1} {#2}
+ #1 #2
}
-\cs_set_protected:Npn \@@_generate_conditional_count:nnNnnnn #1#2#3#4#5
+\cs_set_protected:Npn \@@_generate_conditional_count:nnNNNnn #1#2#3#4#5
{
\__kernel_cs_parm_from_arg_count:nnF
- { \@@_generate_conditional:nnNnnnnn {#1} {#2} #3 {#4} {#5} }
+ { \@@_generate_conditional:nnNNNnnn {#1} {#2} #3 #4 #5 }
{ \tl_count:n {#2} }
{
\__kernel_msg_error:nnxx { kernel } { bad-number-of-arguments }
@@ -2223,8 +2224,10 @@
%
% \begin{macro}
% {
-% \@@_generate_conditional:nnNnnnnn,
-% \@@_generate_conditional:nnnnnnw
+% \@@_generate_conditional:nnNNNnnn,
+% \@@_generate_conditional:NNnnnnNw,
+% \@@_generate_conditional_test:w,
+% \@@_generate_conditional_fast:nw,
% }
% The workhorse here is going through a list of desired forms, \emph{i.e.},
% |p|, |TF|, |T| and |F|. The first three arguments come from splitting up
@@ -2236,8 +2239,15 @@
% empty), the seventh is the list of forms to define, the eighth is the
% replacement text which we will augment when defining the forms.
% The use of \cs{tl_to_str:n} makes the later loop more robust.
+%
+% A large number of our low-level conditionals look like \meta{code}
+% \cs{prg_return_true:} \cs{else:} \cs{prg_return_false:} \cs{fi:} so
+% we optimize this special case by calling
+% \cs{@@_generate_conditional_fast:nw} \Arg{code}. This passes
+% \cs{use_i:nn} instead of \cs{use_i_ii:nnn} to functions such as
+% \cs{@@_generate_p_form:wNNnnnnN}.
% \begin{macrocode}
-\cs_set_protected:Npn \@@_generate_conditional:nnNnnnnn #1#2#3#4#5#6#7#8
+\cs_set_protected:Npn \@@_generate_conditional:nnNNNnnn #1#2#3#4#5#6#7#8
{
\if_meaning:w \c_false_bool #3
\__kernel_msg_error:nnx { kernel } { missing-colon }
@@ -2246,95 +2256,126 @@
\fi:
\use:x
{
- \exp_not:N \@@_generate_conditional:nnnnnnw
- \exp_not:n { {#4} {#5} {#1} {#2} {#6} {#8} }
+ \exp_not:N \@@_generate_conditional:NNnnnnNw
+ \exp_not:n { #4 #5 {#1} {#2} {#6} }
+ \@@_generate_conditional_test:w
+ #8 \q_mark
+ \@@_generate_conditional_fast:nw
+ \prg_return_true: \else: \prg_return_false: \fi: \q_mark
+ \use_none:n
+ \exp_not:n { {#8} \use_i_ii:nnn }
\tl_to_str:n {#7}
\exp_not:n { , \q_recursion_tail , \q_recursion_stop }
}
}
+\cs_set:Npn \@@_generate_conditional_test:w
+ #1 \prg_return_true: \else: \prg_return_false: \fi: \q_mark #2
+ { #2 {#1} }
+\cs_set:Npn \@@_generate_conditional_fast:nw #1#2 \exp_not:n #3
+ { \exp_not:n { {#1} \use_i:nn } }
% \end{macrocode}
% Looping through the list of desired forms. First are six arguments
% and seventh is the form. Use the form to call the
% correct type. If the form does not exist, the \cs{use:c}
% construction results in \tn{relax}, and the error message is
% displayed (unless the form is empty, to allow for |{T, , F}|),
-% then \cs{use_none:nnnnnnn} cleans up. Otherwise, the
+% then \cs{use_none:nnnnnnnn} cleans up. Otherwise, the
% error message is removed by the variant form.
% \begin{macrocode}
-\cs_set_protected:Npn \@@_generate_conditional:nnnnnnw #1#2#3#4#5#6#7 ,
+\cs_set_protected:Npn \@@_generate_conditional:NNnnnnNw #1#2#3#4#5#6#7#8 ,
{
- \if_meaning:w \q_recursion_tail #7
+ \if_meaning:w \q_recursion_tail #8
\exp_after:wN \use_none_delimit_by_q_recursion_stop:w
\fi:
- \use:c { @@_generate_ #7 _form:wnnnnnn }
- \tl_if_empty:nF {#7}
+ \use:c { @@_generate_ #8 _form:wNNnnnnN }
+ \tl_if_empty:nF {#8}
{
\__kernel_msg_error:nnxx
{ kernel } { conditional-form-unknown }
- {#7} { \token_to_str:c { #3 : #4 } }
+ {#8} { \token_to_str:c { #3 : #4 } }
}
- \use_none:nnnnnnn
+ \use_none:nnnnnnnn
\q_stop
- {#1} {#2} {#3} {#4} {#5} {#6}
- \@@_generate_conditional:nnnnnnw {#1} {#2} {#3} {#4} {#5} {#6}
+ #1 #2 {#3} {#4} {#5} {#6} #7
+ \@@_generate_conditional:NNnnnnNw #1 #2 {#3} {#4} {#5} {#6} #7
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}
% {
-% \@@_generate_p_form:wnnnnnn,
-% \@@_generate_TF_form:wnnnnnn,
-% \@@_generate_T_form:wnnnnnn,
-% \@@_generate_F_form:wnnnnnn
+% \@@_generate_p_form:wNNnnnnN,
+% \@@_generate_TF_form:wNNnnnnN,
+% \@@_generate_T_form:wNNnnnnN,
+% \@@_generate_F_form:wNNnnnnN
% }
+% \begin{macro}[EXP]{\@@_p_true:w}
% How to generate the various forms. Those functions take the
-% following arguments: 1: \texttt{set} or \texttt{new}, 2: empty or
-% \texttt{\_protected}, 3: function name 4: signature, 5: parameter
-% text (or empty), 6: replacement. Remember that the logic-returning
-% functions expect two arguments to be present after \cs{exp_end:}:
-% notice the construction of the different variants relies on this,
-% and that the |TF| and |F| variants will be slightly faster than the |T|
-% version. The |p| form is only valid for expandable tests, we check
-% for that by making sure that the second argument is empty.
-% \begin{macrocode}
-\cs_set_protected:Npn \@@_generate_p_form:wnnnnnn
- #1 \q_stop #2#3#4#5#6#7
+% following arguments: 1: junk, 2: \cs{cs_set:Npn} or similar, 3: |p|
+% (for protected conditionals) or |e|, 4: function name, 5: signature,
+% 6: parameter text, 7: replacement (possibly trimmed by
+% \cs{@@_generate_conditional_fast:nw}), 8: \cs{use_i_ii:nnn} or
+% \cs{use_i:nn} (for \enquote{fast} conditionals). Remember that the
+% logic-returning functions expect two arguments to be present after
+% \cs{exp_end:}: notice the construction of the different variants
+% relies on this, and that the |TF| and |F| variants will be slightly
+% faster than the |T| version. The |p| form is only valid for
+% expandable tests, we check for that by making sure that the second
+% argument is empty. For \enquote{fast} conditionals, |#7| has an
+% extra \cs[no-index]{if_\ldots{}}. To optimize a bit further we
+% could replace \cs{exp_after:wN} \cs{use_ii:nnn} and similar by a
+% single macro similar to \cs{@@_p_true:w}. The drawback is that if
+% the |T| or |F| arguments are actually missing, the recovery from
+% the runaway argument would not insert \cs{fi:} back, messing up
+% nesting of conditionals.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_generate_p_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
{
- \if_meaning:w \scan_stop: #3 \scan_stop:
+ \if_meaning:w e #3
\exp_after:wN \use_i:nn
\else:
\exp_after:wN \use_ii:nn
\fi:
{
- \exp_args:cc { cs_ #2 #3 :Npn } { #4 _p: #5 } #6
- { #7 \exp_end: \c_true_bool \c_false_bool }
+ #8
+ { \exp_args:Nc #2 { #4 _p: #5 } #6 }
+ { { #7 \exp_end: \c_true_bool \c_false_bool } }
+ { #7 \@@_p_true:w \fi: \c_false_bool }
}
{
\__kernel_msg_error:nnx { kernel } { protected-predicate }
{ \token_to_str:c { #4 _p: #5 } }
}
}
-\cs_set_protected:Npn \@@_generate_T_form:wnnnnnn
- #1 \q_stop #2#3#4#5#6#7
+\cs_set_protected:Npn \@@_generate_T_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
{
- \exp_args:cc { cs_ #2 #3 :Npn } { #4 : #5 T } #6
- { #7 \exp_end: \use:n \use_none:n }
+ #8
+ { \exp_args:Nc #2 { #4 : #5 T } #6 }
+ { { #7 \exp_end: \use:n \use_none:n } }
+ { #7 \exp_after:wN \use_ii:nn \fi: \use_none:n }
}
-\cs_set_protected:Npn \@@_generate_F_form:wnnnnnn
- #1 \q_stop #2#3#4#5#6#7
+\cs_set_protected:Npn \@@_generate_F_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
{
- \exp_args:cc { cs_ #2 #3 :Npn } { #4 : #5 F } #6
- { #7 \exp_end: { } }
+ #8
+ { \exp_args:Nc #2 { #4 : #5 F } #6 }
+ { { #7 \exp_end: { } } }
+ { #7 \exp_after:wN \use_none:nn \fi: \use:n }
}
-\cs_set_protected:Npn \@@_generate_TF_form:wnnnnnn
- #1 \q_stop #2#3#4#5#6#7
+\cs_set_protected:Npn \@@_generate_TF_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
{
- \exp_args:cc { cs_ #2 #3 :Npn } { #4 : #5 TF } #6
- { #7 \exp_end: }
+ #8
+ { \exp_args:Nc #2 { #4 : #5 TF } #6 }
+ { { #7 \exp_end: } }
+ { #7 \exp_after:wN \use_ii:nnn \fi: \use_ii:nn }
}
+\cs_set:Npn \@@_p_true:w \fi: \c_false_bool { \fi: \c_true_bool }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \begin{macro}{\prg_set_eq_conditional:NNn, \prg_new_eq_conditional:NNn}
% \begin{macro}{\@@_set_eq_conditional:NNNn}
diff --git a/l3kernel/l3int.dtx b/l3kernel/l3int.dtx
index 58c13e5..dedc00d 100644
--- a/l3kernel/l3int.dtx
+++ b/l3kernel/l3int.dtx
@@ -1692,10 +1692,10 @@
{ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_if_even:n } }
\prg_new_conditional:Npnn \int_if_even:n #1 { p , T , F , TF}
{
- \if_int_odd:w \@@_eval:w #1 \@@_eval_end:
- \prg_return_false:
- \else:
+ \reverse_if:N \if_int_odd:w \@@_eval:w #1 \@@_eval_end:
\prg_return_true:
+ \else:
+ \prg_return_false:
\fi:
}
% \end{macrocode}
diff --git a/l3kernel/l3tl.dtx b/l3kernel/l3tl.dtx
index 61505a3..dbae072 100644
--- a/l3kernel/l3tl.dtx
+++ b/l3kernel/l3tl.dtx
@@ -2004,13 +2004,13 @@
\group_begin:
\tl_set:Nn \l_@@_internal_a_tl {#1}
\tl_set:Nn \l_@@_internal_b_tl {#2}
- \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl
- \group_end:
- \prg_return_true:
- \else:
- \group_end:
- \prg_return_false:
- \fi:
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
}
\tl_new:N \l_@@_internal_a_tl
\tl_new:N \l_@@_internal_b_tl
diff --git a/l3kernel/testfiles/m3token002.luatex.tlg b/l3kernel/testfiles/m3token002.luatex.tlg
index 815be14..fbfd4a8 100644
--- a/l3kernel/testfiles/m3token002.luatex.tlg
+++ b/l3kernel/testfiles/m3token002.luatex.tlg
@@ -157,7 +157,7 @@ FALSE
============================================================
TEST 15: token if space
============================================================
-! Argument of \use_i:nn has an extra }.
+! Argument of \use_ii:nnn has an extra }.
<inserted text>
\par
l. ...}
@@ -168,7 +168,7 @@ I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.
Runaway argument?
-! Paragraph ended before \use_i:nn was complete.
+! Paragraph ended before \use_ii:nnn was complete.
<to be read again>
\par
l. ...}
@@ -176,7 +176,7 @@ I suspect you've forgotten a `}', causing me to apply this
control sequence to too much text. How can we recover?
My plan is to forget the whole thing and hope for the best.
UE\c_space_token \par
-! Argument of \use_i:nn has an extra }.
+! Argument of \use_ii:nnn has an extra }.
<inserted text>
\par
l. ...}
@@ -187,7 +187,7 @@ I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.
Runaway argument?
-! Paragraph ended before \use_i:nn was complete.
+! Paragraph ended before \use_ii:nnn was complete.
<to be read again>
\par
l. ...}
diff --git a/l3kernel/testfiles/m3token002.tlg b/l3kernel/testfiles/m3token002.tlg
index a1e9ef9..fbee0af 100644
--- a/l3kernel/testfiles/m3token002.tlg
+++ b/l3kernel/testfiles/m3token002.tlg
@@ -157,7 +157,7 @@ FALSE
============================================================
TEST 15: token if space
============================================================
-! Argument of \use_i:nn has an extra }.
+! Argument of \use_ii:nnn has an extra }.
<inserted text>
\par
l. ...}
@@ -168,7 +168,7 @@ I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.
Runaway argument?
-! Paragraph ended before \use_i:nn was complete.
+! Paragraph ended before \use_ii:nnn was complete.
<to be read again>
\par
l. ...}
@@ -176,7 +176,7 @@ I suspect you've forgotten a `}', causing me to apply this
control sequence to too much text. How can we recover?
My plan is to forget the whole thing and hope for the best.
UE\c_space_token \par
-! Argument of \use_i:nn has an extra }.
+! Argument of \use_ii:nnn has an extra }.
<inserted text>
\par
l. ...}
@@ -187,7 +187,7 @@ I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.
Runaway argument?
-! Paragraph ended before \use_i:nn was complete.
+! Paragraph ended before \use_ii:nnn was complete.
<to be read again>
\par
l. ...}
More information about the latex3-commits
mailing list