[latex3-commits] [git/LaTeX3-latex3-latex2e] gh569: First attempt at copying commands (ab4f4231)

PhelypeOleinik phelype.oleinik at latex-project.org
Sun Aug 1 14:39:44 CEST 2021


Repository : https://github.com/latex3/latex2e
On branch  : gh569
Link       : https://github.com/latex3/latex2e/commit/ab4f4231886d85b3291478eb18153b90e7484df8

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

commit ab4f4231886d85b3291478eb18153b90e7484df8
Author: PhelypeOleinik <phelype.oleinik at latex-project.org>
Date:   Sun Aug 1 09:39:44 2021 -0300

    First attempt at copying commands


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

ab4f4231886d85b3291478eb18153b90e7484df8
 base/ltcmd.dtx | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 239 insertions(+)

diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx
index 6907c6d7..4e47ba83 100644
--- a/base/ltcmd.dtx
+++ b/base/ltcmd.dtx
@@ -856,6 +856,245 @@
 % \end{macro}
 % \end{macro}
 %
+% \subsubsection{Copying a command and its internal structure}
+%
+%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_copy:NN}%
+%<latexrelease>  {Support \NewCommandCopy and \ShowCommand in ltcmd}
+% Since the 2020-10-01 \LaTeXe{} release, support for copying and
+% showing the definition of robust commands is available, but the
+% specifics of each command is implemented separately.  Here we'll add
+% support for copying and showing \pkg{ltcmd} definitions.
+%
+% To fully support copying, we need two commands:  a conditional to test
+% if a command is in fact an \pkg{ltcmd} command, and another to
+% actually copy the command.  The conditional is defined later as
+% \cs{__kernel_cmd_if_xparse:NTF}, so now to the copying:
+%
+% \begin{macro}{\@@_copy:NN}
+% \begin{macro}{\@@_set_eq_if_exist:NN,\@@_set_eq_if_exist:cc}
+%   This macro just branches to the proper copying command by using
+%   \cs{@@_cmd_type_cases:NnnnF}.  The copying command takes the names
+%   of the commands to be copied to and from, and the actual commands
+%   as its four arguments.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy:NN #1 #2
+  {
+    \use:x
+      {
+        \int_set:Nn \tex_escapechar:D { 92 }
+        \exp_not:N \@@_cmd_type_cases:NnnnF \exp_not:N #2
+          { \@@_copy_command:nnNN }
+          { \@@_copy_expandable:nnNN }
+          { \@@_copy_environment:nnNN }
+          { \exp_not:N \ERROR }
+            { \cs_to_str:N #1 } { \cs_to_str:N #2 }
+            \exp_not:N #1 \exp_not:N #2
+        \int_set:Nn \tex_escapechar:D { \int_use:N \tex_escapechar:D }
+      }
+  }
+\cs_new_protected:Npn \@@_set_eq_if_exist:NN #1 #2
+  { \cs_if_exist:NTF #2 { \cs_set_eq:NN } { \use_none:nn } #1 #2 }
+\cs_generate_variant:Nn \@@_set_eq_if_exist:NN { cc }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_copy_command:nnNN,\@@_copy_command:NnNNnnnn}
+%   A normal (non-expandable) command has a pretty straightforward
+%   structure.  Its definition is stored in
+%   \cs{\meta{cmd}\textvisiblespace code}, its defaults (if any) are
+%   stored in \cs{\meta{cmd}\textvisiblespace defaults}, and its
+%   top-level definition contains its signature, which can just be
+%   copied over.  \cs{@@_copy_command:nnNN} copies the command code and
+%   defaults, and then defines the top-level command using the auxiliary
+%   \cs{@@_copy_command:NnNNnnnn}.  This macro takes the signature of
+%   the command being copied from its top-level definition, and replaces
+%   the named bits with the new name.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_command:nnNN #1 #2 #3 #4
+  {
+    \cs_set_eq:cc { #1 ~ code } { #2 ~ code }
+    \@@_set_eq_if_exist:cc { #1 ~ defaults } { #2 ~ defaults }
+    \cs_set_protected_nopar:Npx #3
+      { \exp_after:wN \@@_copy_command:NnNNnnnn #4 {#1} }
+  }
+\cs_new:Npn \@@_copy_command:NnNNnnnn #1 #2 #3 #4 #5 #6 #7 #8
+  {
+    #1 \exp_not:n { {#2} }
+    \exp_not:c { #8 ~ } \exp_not:c { #8 ~ code }
+    \exp_not:n { {#5} {#6} {#7} }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_copy_expandable:nnNN,\@@_copy_expandable:NnNNNNnnn}
+%   An expandable command is slightly more complicated.  Besides the
+%   \cs{\meta{cmd}\textvisiblespace code}, and
+%   \cs{\meta{cmd}\textvisiblespace defaults}, it also has an auxiliary
+%   \cs{\meta{cmd}\textvisiblespace} for grabbing delimited arguments,
+%   and possibly another auxiliary
+%   \cs{\meta{cmd}\textvisiblespace\textvisiblespace}, if the command
+%   has both long and short arguments.  Then, its signature has also
+%   several specific bits that are unique to that command, contrary to
+%   non-expandable commands which use a common set of parsing functions.
+%
+%   We'll start by copying the basics, then call
+%   \cs{@@_copy_expandable_signature:NnNNNNnnn} to parse the signature
+%   of the command and make the modified copy in a temporary token list,
+%   then we call \cs{@@_copy_expandable:NnNNNNnnn} that will copy the
+%   top-level definition of the command, with the proper internal
+%   renames.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_expandable:nnNN #1 #2 #3 #4
+  {
+    \cs_set_eq:cc { #1 ~ code } { #2 ~ code }
+    \@@_set_eq_if_exist:cc { #1 ~ } { #2 ~ }
+    \@@_set_eq_if_exist:cc { #1 ~ \c_space_tl } { #2 ~ \c_space_tl }
+    \@@_set_eq_if_exist:cc { #1 ~ defaults } { #2 ~ defaults }
+    \exp_after:wN \@@_copy_expandable_signature:NnNNNNnnn #4 {#1} {#2}
+    \cs_set_nopar:Npx #3
+      { \exp_after:wN \@@_copy_expandable:NnNNNNnnn #4 {#1} {#2} }
+  }
+\cs_new:Npn \@@_copy_expandable:NnNNNNnnn #1 #2 #3 #4 #5 #6 #7 #8 #9
+  {
+    \exp_not:N #1 \exp_not:n { {#2} }
+    \exp_not:c { #8 ~ }
+    \exp_not:c
+      {
+        #8 ~
+        \str_if_eq:eeT
+            { \exp_not:c { #9 ~ \c_space_tl } } { \exp_not:N #4 }
+          { \c_space_tl }
+      }
+    \exp_not:c { #8 ~ code }
+    \str_if_eq:eeTF { \exp_not:N #6 } { ? }
+      { ? }
+      { \exp_not:c { #8 ~ defaults } }
+    { \exp_not:V \l_@@_tmpa_tl }
+  }
+%    \end{macrocode}
+%
+% \begin{macro}{
+%     \@@_copy_expandable_signature:NnNNNNnnn,
+%     \@@_copy_expandable:nnN,
+%     \@@_copy_parse_grabber:w,
+%   }
+%   A signature for an expandable command contains as many
+%   \cs{expandable_grab_\meta{type}:w} as there are arguments, and what
+%   follows this macro depends on the \meta{type}.  We'll start a loop
+%   through the signature, and at each argument grabber, we'll step the
+%   argument count, and look for the \meta{type} with
+%   \cs{@@_copy_parse_grabber:w} so we know which
+%   \cs{@@_copy_grabber_\meta{type}:w} to call next.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_expandable_signature:NnNNNNnnn
+    #1 #2 #3 #4 #5 #6 #7 #8 #9
+  {
+    \int_zero:N \l_@@_current_arg_int
+    \tl_clear:N \l_@@_tmpa_tl
+    \@@_copy_expandable:nnN {#8} {#9} #7
+      \q_recursion_tail \q_recursion_stop
+  }
+\cs_new_protected:Npn \@@_copy_expandable:nnN #1 #2 #3
+  {
+    \quark_if_recursion_tail_stop:n {#3}
+    \int_incr:N \l_@@_current_arg_int
+    \exp_after:wN \@@_copy_parse_grabber:w \token_to_str:N #3 {#1} {#2}
+  }
+\use:x
+  {
+    \cs_new_protected:Npn \exp_not:N \@@_copy_parse_grabber:w ##1
+        \tl_to_str:n { expandable_grab_ } ##2 \tl_to_str:n { :w }
+      {
+        \tl_put_right:Nx \exp_not:N \l_@@_tmpa_tl
+          { \exp_not:N \exp_not:c { @@_expandable_grab_##2:w } }
+        \exp_not:N \cs_if_exist_use:cF { @@_copy_grabber_##2:w }
+          { \exp_not:N \ERROR }
+      }
+  }
+%    \end{macrocode}
+%
+% \begin{macro}{
+%     \@@_copy_grabber_D:w,\@@_copy_grabber_D_alt:w,
+%     \@@_copy_grabber_R:w,\@@_copy_grabber_R_alt:w,
+%     \@@_copy_grabber_E:w,\@@_copy_grabber_E_long:w,
+%     \@@_copy_grabber_t:w,
+%     \@@_copy_grabber_m:w,\@@_copy_grabber_m_long:w,
+%   }
+%   The most complicated is the |D|elimited argument:  each argument has
+%   a dedicated grabbing function named after the command that has to be
+%   copied over (of the form
+%   \cs{\meta{cmd}\textvisiblespace(arg\textvisiblespace\meta{num})}).
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_grabber_D:w #1 #2 #3 #4 #5
+  {
+    \tl_put_right:Nx \l_@@_tmpa_tl
+      {
+        \exp_not:c { #1 ~ (arg ~ \int_use:N \l_@@_current_arg_int ) }
+        \exp_not:n { #4 #5 }
+      }
+    \cs_set_eq:cc
+      { #1 ~ (arg ~ \int_use:N \l_@@_current_arg_int ) }
+      { #2 ~ (arg ~ \int_use:N \l_@@_current_arg_int ) }
+    \@@_copy_expandable:nnN {#1} {#2}
+  }
+%    \end{macrocode}
+%
+%   |D_alt| is just a special case of |D| that uses a single delimiter
+%   (used when both delimiters of the argument are identical):
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_grabber_D_alt:w #1 #2 #3 #4
+  { \@@_copy_grabber_D:w {#1} {#2} {#3} {#4} { } }
+%    \end{macrocode}
+%
+%   |R|, as far as copying is concerned, is identical to |D|:
+%    \begin{macrocode}
+\cs_new_eq:NN \@@_copy_grabber_R:w \@@_copy_grabber_D:w
+\cs_new_eq:NN \@@_copy_grabber_R_alt:w \@@_copy_grabber_D_alt:w
+%    \end{macrocode}
+%
+%   |E| is also straightforward: we just copy the embellishments over:
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_grabber_E:w #1 #2 #3 #4
+  {
+    \tl_put_right:Nn \l_@@_tmpa_tl { {#3} {#4} }
+    \@@_copy_expandable:nnN {#1} {#2}
+  }
+\cs_new_eq:NN \@@_copy_grabber_E_long:w \@@_copy_grabber_E:w
+%    \end{macrocode}
+%
+%   |t| just needs copying the token to be tested for:
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_grabber_t:w #1 #2 #3 #4
+  {
+    \tl_put_right:Nn \l_@@_tmpa_tl { #3 #4 }
+    \@@_copy_expandable:nnN {#1} {#2}
+  }
+%    \end{macrocode}
+%
+%   And last but not least, |m| is the simplest;  the grabber is just
+%   \cs{@@_expandable_grab_m:w}, which is already added to the new
+%   command so here we just resume the loop:
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_grabber_m:w { \@@_copy_expandable:nnN }
+\cs_new_eq:NN \@@_copy_grabber_m_long:w \@@_copy_grabber_m:w
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_copy_environment:nnNN}
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_environment:nnNN #1 #2 #3 #4 { \ERROR }
+%    \end{macrocode}
+%
+%<latexrelease>\EndIncludeInRelease
+%
+%<latexrelease>\IncludeInRelease{0000/00/00}{\@@_copy:NN}%
+%<latexrelease>  {Support \NewCommandCopy and \ShowCommand in ltcmd}
+%
+%<latexrelease>\EndIncludeInRelease
+%
 % \subsection{Normalizing the argument specifications}
 %
 % The goal here is to expand aliases and check that the argument





More information about the latex3-commits mailing list.