[latex3-commits] [latex3/latex2e] gh1189: Optimise creation of document commands with m-type args (9ba9f5c7)

github at latex-project.org github at latex-project.org
Tue Nov 28 11:04:47 CET 2023


Repository : https://github.com/latex3/latex2e
On branch  : gh1189
Link       : https://github.com/latex3/latex2e/commit/9ba9f5c72e53776007e9843a36748ceade6a29ff

>---------------------------------------------------------------

commit 9ba9f5c72e53776007e9843a36748ceade6a29ff
Author: Joseph Wright <joseph.wright at morningstar2.co.uk>
Date:   Tue Nov 28 09:31:50 2023 +0000

    Optimise creation of document commands with m-type args


>---------------------------------------------------------------

9ba9f5c72e53776007e9843a36748ceade6a29ff
 base/changes.txt                         |  4 ++
 base/doc/ltnews39.tex                    | 10 ++++
 base/ltcmd.dtx                           | 42 ++++++++++++---
 base/testfiles-ltcmd/github-1009.tlg     | 44 +++++++--------
 base/testfiles-ltcmd/ltcmd001.tlg        | 93 +++++++++++---------------------
 base/testfiles-ltcmd/ltcmd002.luatex.tlg |  2 +-
 base/testfiles-ltcmd/ltcmd002.tlg        |  2 +-
 7 files changed, 102 insertions(+), 95 deletions(-)

diff --git a/base/changes.txt b/base/changes.txt
index c1024ea9..dc5e25e1 100644
--- a/base/changes.txt
+++ b/base/changes.txt
@@ -6,6 +6,10 @@ to completeness or accuracy and it contains some references to files that are
 not part of the distribution.
 ================================================================================
 
+2023-11-28  Joseph Wright  <Joseph.Wright at latex-project.org>
+	* ltcmd.dtx (subsection{Declaring commands and environments}):
+	Optimise creation of simple document commands (gh/1189)
+
 2023-11-16  Frank Mittelbach  <Frank.Mittelbach at latex-project.org>
 	* ltpara.dtx (subsection{Providing hooks for paragraphs}):
 	Correct error message: hook left horizontal not vertical mode (gh/1182)
diff --git a/base/doc/ltnews39.tex b/base/doc/ltnews39.tex
index 3002928c..39c97a1e 100644
--- a/base/doc/ltnews39.tex
+++ b/base/doc/ltnews39.tex
@@ -187,6 +187,16 @@ defined it is not altered, but instead a warning message is displayed.
 %
 \githubissue{823}
 
+\subsection{Optimise creation of simple document commands}
+
+Creating document commands using \cs{NewDocumentCommand}, etc., provides a very
+flexible way of grabbing arguments. When the document command only takes simple
+mandatory arguments, this has to-date added an overhead that could be avoided.
+We have now refined the internal code path such that \enquote{simple} document
+commands avoid any overhead at point-of-use, making the results as efficient as
+using \cs{newcommand} or low-level \TeX{} constructs.
+%
+\githubissue{1189}
 
 
 %\section{Bug fixes}
diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx
index dcb85a1a..a611d688 100644
--- a/base/ltcmd.dtx
+++ b/base/ltcmd.dtx
@@ -34,8 +34,8 @@
 %%% From File: ltcmd.dtx
 %
 %    \begin{macrocode}
-\def\ltcmdversion{v1.2a}
-\def\ltcmddate{2023-08-19}
+\def\ltcmdversion{v1.2b}
+\def\ltcmddate{2023-11-28}
 %    \end{macrocode}
 %
 %<*driver>
@@ -437,16 +437,44 @@
 %
 % \begin{macro}{\@@_declare_cmd_code:Nnn}
 % \begin{macro}
-%   {\@@_declare_cmd_code_aux:Nnn, \@@_declare_cmd_code_expandable:Nnn}
-%   The appropriate auxiliary is called.
+%   {
+%     \@@_declare_cmd_optimised:Nnn,
+%     \@@_declare_cmd_code_aux:Nnn,
+%     \@@_declare_cmd_code_expandable:Nnn
+%   }
+% \changes{v1.2b}{2023/11/28}
+%   {Optimise cmd creation for all-\texttt{m} arguments}
+%   At this stage we can check for a short-cut possibility: if the signature
+%   is just made up of \cs{@@_expandable_grab_m:w}, then we can produce an
+%   optimised document command.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_declare_cmd_code:Nnn
   {
-    \bool_if:NTF \l_@@_grab_expandably_bool
-      { \@@_declare_cmd_code_expandable:Nnn }
-      { \@@_declare_cmd_code_aux:Nnn }
+    \str_if_eq:eeTF
+      { \exp_not:V \l_@@_signature_tl }
+      {
+        \prg_replicate:nn \l_@@_current_arg_int
+          { \exp_not:N \@@_expandable_grab_m:w }
+      }
+      { \@@_declare_cmd_optimised:Nnn }
+      {
+        \bool_if:NTF \l_@@_grab_expandably_bool
+          { \@@_declare_cmd_code_expandable:Nnn }
+          { \@@_declare_cmd_code_aux:Nnn }
+      }
    }
 %    \end{macrocode}
+%   The optimised version of commands just has to worry about whether to make
+%   them protected.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_declare_cmd_optimised:Nnn #1#2#3
+  {
+    \bool_if:NTF \l_@@_expandable_bool
+      { \cs_generate_from_arg_count:NNnn #1 \cs_set_nopar:Npn }
+      { \cs_generate_from_arg_count:NNnn #1 \cs_set_protected_nopar:Npn }
+        \l_@@_current_arg_int {#3}
+  }
+%    \end{macrocode}
 %   Standard functions call \cs{@@_start:nNNnnn}, which receives the
 %   argument specification, an auxiliary used for
 %   grabbing arguments, an auxiliary containing the code, and then the
diff --git a/base/testfiles-ltcmd/github-1009.tlg b/base/testfiles-ltcmd/github-1009.tlg
index 2e650734..a586b9ef 100644
--- a/base/testfiles-ltcmd/github-1009.tlg
+++ b/base/testfiles-ltcmd/github-1009.tlg
@@ -3,23 +3,21 @@ Don't change this file in any respect.
 ============================================================
 TEST 1: Non-expandable Document Commands with m-type arguments
 ============================================================
-> \foo=document command:
+> \foo=\protected macro:
 ->.
-<recently read> }
+<argument> \foo 
 l. ...}
-> \baz=document command:
+> \baz=\protected macro:
 ->.
-<recently read> }
+<argument> \baz 
 l. ...}
-> \foo=document command:
-  #1:m
-->.
-<recently read> }
+> \foo=\protected macro:
+#1->.
+<argument> \foo 
 l. ...}
-> \baz=document command:
-  #1:m
-->.
-<recently read> }
+> \baz=\protected macro:
+#1->.
+<argument> \baz 
 l. ...}
 > \foo=document command:
   #1:+m
@@ -45,23 +43,21 @@ l. ...}
 ============================================================
 TEST 2: Expandable Document Commands with m-type arguments
 ============================================================
-> \foo=expandable document command:
+> \foo=macro:
 ->.
-<recently read> }
+<argument> \foo 
 l. ...}
-> \baz=expandable document command:
+> \baz=macro:
 ->.
-<recently read> }
+<argument> \baz 
 l. ...}
-> \foo=expandable document command:
-  #1:m
-->.
-<recently read> }
+> \foo=macro:
+#1->.
+<argument> \foo 
 l. ...}
-> \baz=expandable document command:
-  #1:m
-->.
-<recently read> }
+> \baz=macro:
+#1->.
+<argument> \baz 
 l. ...}
 > \foo=expandable document command:
   #1:+m
diff --git a/base/testfiles-ltcmd/ltcmd001.tlg b/base/testfiles-ltcmd/ltcmd001.tlg
index 22e0c8a5..824392d2 100644
--- a/base/testfiles-ltcmd/ltcmd001.tlg
+++ b/base/testfiles-ltcmd/ltcmd001.tlg
@@ -17,97 +17,69 @@ For immediate help type H <return>.
 l. ...}
 You have used \RenewDocumentCommand with a command that was never defined.
 LaTeX will ignore this entire definition.
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {}\foo  \foo  \foo
-code ?{}.
+> \foo=\protected macro:->First definition.
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:->First definition.
+> \foo code=undefined.
 <recently read> }
 l. ...}
 ============================================================
 ============================================================
 TEST 2: Commands with simple mandatory arguments
 ============================================================
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {m}\foo  \foo  \foo
-code ?{\__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1->(#1).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1->(#1).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mm}\foo  \foo  \foo
-code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2->(#1)(#2).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1#2->(#1)(#2).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmm}\foo  \foo  \foo
-code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3->(#1)(#2)(#3).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1#2#3->(#1)(#2)(#3).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmm}\foo  \foo 
-\foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3#4->(#1)(#2)(#3)(#4).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1#2#3#4->(#1)(#2)(#3)(#4).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmmm}\foo  \foo 
-\foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3#4#5->(#1)(#2)(#3)(#4)(#5).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1#2#3#4#5->(#1)(#2)(#3)(#4)(#5).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmmmm}\foo  \foo 
-\foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3#4#5#6->(#1)(#2)(#3)(#4)(#5)(#6).
 <recently read> }
 l. ...}
-> \foo code=\protected\long macro:#1#2#3#4#5#6->(#1)(#2)(#3)(#4)(#5)(#6).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmmmmm}\foo  \foo 
-\foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3#4#5#6#7->(#1)(#2)(#3)(#4)(#5)(#6)(#7).
 <recently read> }
 l. ...}
-> \foo code=\protected\long
-macro:#1#2#3#4#5#6#7->(#1)(#2)(#3)(#4)(#5)(#6)(#7).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmmmmmm}\foo  \foo 
-\foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w }.
+> \foo=\protected macro:#1#2#3#4#5#6#7#8->(#1)(#2)(#3)(#4)(#5)(#6)(#7)(#8).
 <recently read> }
 l. ...}
-> \foo code=\protected\long
-macro:#1#2#3#4#5#6#7#8->(#1)(#2)(#3)(#4)(#5)(#6)(#7)(#8).
+> \foo code=undefined.
 <recently read> }
 l. ...}
-> \foo=\protected macro:->\__cmd_start_expandable:nNNNNn {mmmmmmmmm}\foo  \foo
- \foo code ?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w }.
+> \foo=\protected
+macro:#1#2#3#4#5#6#7#8#9->(#1)(#2)(#3)(#4)(#5)(#6)(#7)(#8)(#9).
 <recently read> }
 l. ...}
-> \foo code=\protected\long
-macro:#1#2#3#4#5#6#7#8#9->(#1)(#2)(#3)(#4)(#5)(#6)(#7)(#8)(#9).
+> \foo code=undefined.
 <recently read> }
 l. ...}
 ============================================================
@@ -349,11 +321,10 @@ l. ...}
 ============================================================
 TEST 8: Some valid expandable functions
 ============================================================
-> \foo=macro:->\__cmd_start_expandable:nNNNNn {m}\foo  \foo  \foo code
-?{\__cmd_expandable_grab_m:w }.
+> \foo=macro:#1->(#1).
 <recently read> }
 l. ...}
-> \foo code=\long macro:#1->(#1).
+> \foo code=undefined.
 <recently read> }
 l. ...}
 > \foo=macro:->\__cmd_start_expandable:nNNNNn {+m}\foo  \foo  \foo code
@@ -363,12 +334,10 @@ l. ...}
 > \foo code=\long macro:#1->(#1).
 <recently read> }
 l. ...}
-> \foo=macro:->\__cmd_start_expandable:nNNNNn {mmm}\foo  \foo  \foo code
-?{\__cmd_expandable_grab_m:w \__cmd_expandable_grab_m:w
-\__cmd_expandable_grab_m:w }.
+> \foo=macro:#1#2#3->(#1)(#2)(#3).
 <recently read> }
 l. ...}
-> \foo code=\long macro:#1#2#3->(#1)(#2)(#3).
+> \foo code=\long macro:#1->(#1).
 <recently read> }
 l. ...}
 > \foo=macro:->\__cmd_start_expandable:nNNNNn {om}\foo  \foo  \foo code
@@ -468,10 +437,10 @@ l. ...}
 You have used \RenewDocumentEnvironment with an environment that was never
 defined.
 LaTeX will ignore this entire definition.
-> \environment foo=\protected macro:->\__cmd_start_env:nnnnn {}{foo}{}{}{}.
+> \environment foo=\protected macro:->First.
 <recently read> }
 l. ...}
-> \environment foo code=\protected\long macro:->First.
+> \environment foo code=undefined.
 <recently read> }
 l. ...}
 ============================================================
@@ -700,14 +669,14 @@ TEST 17: (ab)using xparse commands in csnames
 l. ...  }
 The control sequence marked <to be read again> should
 not appear between \csname and \endcsname.
-\test- \xparse function is not expandable \bool_set_false:N \l__cmd_environment_bool \__cmd_start_aux:NNnnnn \foo code {D...}{\__cmd_grab_D:w ..}{{.}}{}....\cs_end: 
+\test- \xparse function is not expandable \bool_set_false:N \l__cmd_environment_bool \__cmd_start_aux:NNnnnn \foo  \foo code {D...}{\__cmd_grab_D:w ..}{{.}}{}....\cs_end: 
 ! Missing \endcsname inserted.
 <to be read again> 
                    \xparse function is not expandable 
 l. ...  }
 The control sequence marked <to be read again> should
 not appear between \csname and \endcsname.
-\test- \xparse function is not expandable \relax \bool_set_false:N \l__cmd_environment_bool \__cmd_start_aux:NNnnnn \foo code {D...}{\__cmd_grab_D:w .}{{.}}{}....\cs_end: 
+\test- \xparse function is not expandable \relax \bool_set_false:N \l__cmd_environment_bool \__cmd_start_aux:NNnnnn \foo  \foo code {D...}{\__cmd_grab_D:w ..}{{.}}{}....\cs_end: 
 ============================================================
 ============================================================
 TEST 18: Checking for existing expandable definitions
@@ -726,10 +695,10 @@ l. ...}
 You have used \RenewExpandableDocumentCommand with a command that was never
 defined.
 LaTeX will ignore this entire definition.
-> \foo=macro:->\__cmd_start_expandable:nNNNNn {}\foo  \foo  \foo code ?{}.
+> \foo=macro:->First definition.
 <recently read> }
 l. ...}
-> \foo code=\long macro:->First definition.
+> \foo code=undefined.
 <recently read> }
 l. ...}
 ============================================================
diff --git a/base/testfiles-ltcmd/ltcmd002.luatex.tlg b/base/testfiles-ltcmd/ltcmd002.luatex.tlg
index b3eb65ef..62d4d73c 100644
--- a/base/testfiles-ltcmd/ltcmd002.luatex.tlg
+++ b/base/testfiles-ltcmd/ltcmd002.luatex.tlg
@@ -48,7 +48,7 @@ TEST 2: Nesting optional arguments
 TEST 3: Invalid arguments (types s, o m)
 ============================================================
 Runaway argument?
-! Paragraph ended before \foo  was complete.
+! Paragraph ended before \foo was complete.
 <to be read again> 
 \par 
 l. ...}
diff --git a/base/testfiles-ltcmd/ltcmd002.tlg b/base/testfiles-ltcmd/ltcmd002.tlg
index 31f7b523..3a5fbeb9 100644
--- a/base/testfiles-ltcmd/ltcmd002.tlg
+++ b/base/testfiles-ltcmd/ltcmd002.tlg
@@ -48,7 +48,7 @@ TEST 2: Nesting optional arguments
 TEST 3: Invalid arguments (types s, o m)
 ============================================================
 Runaway argument?
-! Paragraph ended before \foo  was complete.
+! Paragraph ended before \foo was complete.
 <to be read again> 
                    \par 
 l. ...}





More information about the latex3-commits mailing list.