[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