[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.