[latex3-commits] [git/LaTeX3-latex3-latex2e] hook-args: Normalise labeled hook code (d92c54e4)

PhelypeOleinik phelype.oleinik at latex-project.org
Fri Apr 14 04:47:18 CEST 2023


Repository : https://github.com/latex3/latex2e
On branch  : hook-args
Link       : https://github.com/latex3/latex2e/commit/d92c54e40b581444170c46f55a7ff05ff1f7513d

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

commit d92c54e40b581444170c46f55a7ff05ff1f7513d
Author: PhelypeOleinik <phelype.oleinik at latex-project.org>
Date:   Thu Apr 13 23:47:18 2023 -0300

    Normalise labeled hook code


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

d92c54e40b581444170c46f55a7ff05ff1f7513d
 base/lthooks.dtx                                 | 156 +++++++++++++++++++++++
 base/testfiles-lthooks/lthooks-033.lvt           |   8 +-
 base/testfiles-lthooks/lthooks-033.tlg           |  19 +--
 base/testfiles-lthooks/lthooks-rollback-args.tlg |   2 +
 4 files changed, 165 insertions(+), 20 deletions(-)

diff --git a/base/lthooks.dtx b/base/lthooks.dtx
index 7764947a..6316a8c4 100644
--- a/base/lthooks.dtx
+++ b/base/lthooks.dtx
@@ -2382,6 +2382,8 @@
 %     \tl_gremove_once:Nx,
 %     \tl_show:x,
 %     \tl_log:x,
+%     \tl_set:Ne,
+%     \prop_put:Nne,
 %     \cs_replacement_spec:c
 %   }
 %   Some variants of \pkg{expl3} functions.
@@ -2390,7 +2392,9 @@
 \cs_generate_variant:Nn \tl_gremove_once:Nn { Nx }
 \cs_generate_variant:Nn \tl_show:n { x }
 \cs_generate_variant:Nn \tl_log:n { x }
+\cs_generate_variant:Nn \tl_set:Nn { Ne }
 \cs_generate_variant:Nn \cs_replacement_spec:N { c }
+\cs_generate_variant:Nn \prop_put:Nnn { Nne }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -2797,6 +2801,14 @@
 %    \begin{macrocode}
         \@@_init_structure:n {#1}
 %    \end{macrocode}
+%    The call to \cs{@@_normalise_code_pool:n} will correct any improper
+%    reference to arguments that don't exist in the hook, raising a
+%    low-level \TeX{} error and doubling the offending parameter tokens.
+%    It has to be done after \cs{@@_init_structure:n} because it
+%    operates on \cs[no-index]{g_@@_\meta{hook}_code_prop}.
+%    \begin{macrocode}
+        \@@_normalise_code_pool:n {#1}
+%    \end{macrocode}
 %    The \cs{g_@@_\meta{hook}_labels_clist} holds the sorted list of
 %    labels (once it got sorted). This is used only for debugging.
 %    \begin{macrocode}
@@ -4322,6 +4334,8 @@
 %   \cs[no-index]{c_@@_\meta{hook}_parameter_tl} exists, if the hook is
 %   declared, and if it's a generic hook.
 %    \begin{macrocode}
+    \cs_if_exist:cF { @@#1~#2 }
+      { \@@_code_gset_aux:nnn {#1} {#2} { } }
     \@@_code_gset_auxi:veen
       {
         c_@@_
@@ -4462,6 +4476,148 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_normalise_code_pool:n}
+%   This one's a bit of a hack.  It takes a hook, and iterates over its
+%   code pool (\cs[no-index]{g_@@_\meta{hook}_code_prop}), redefining
+%   each code label to use only valid arguments.  This is used when, for
+%   example, a code is added referencing arguments \verb|#1| and
+%   \verb|#2|, but the hook has only \verb|#1|.  In this example, every
+%   reference to \verb|#2| is changed to \verb|##2|.  This is done
+%   because otherwise \TeX{} will throw a low-level error every time
+%   some change happens to the hook (code is added, a rule is set, etc).
+% \changes{v1.1a}{2023/04/06}
+%         {Macro added (hook-args).}
+%    \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_normalise_code_pool:n}
+%<latexrelease>                 {Hooks~with~args}
+\cs_new_protected:Npn \@@_normalise_code_pool:n #1
+  {
+%    \end{macrocode}
+%   This macro does everything in a group to avoid leaking weird
+%   definitions.  To start, we define two auxiliary token lists.
+%   \cs[no-index]{l_@@_tmpb_tl} contains:
+%\begin{verbatim}
+%   {\c__hook_hashes_tl 1}
+%   {\c__hook_hashes_tl 2}
+%   ...
+%   {\c__hook_hashes_tl 9}
+%\end{verbatim}
+%    \begin{macrocode}
+    \group_begin:
+      \cs_set:Npn \@@_tmp:w ##1##2##3##4##5##6##7##8##9 { }
+      \tl_set:Ne \l_@@_tmpb_tl
+        { \@@_braced_cs_parameter:n { @@_tmp:w } }
+      \group_begin:
+        \@@_tl_set:cn { c_@@_hash_tl } { \exp_not:N \c_@@_hashes_tl }
+        \use:e
+          {
+      \group_end:
+      \tl_set:Nn \exp_not:N \l_@@_tmpb_tl { \l_@@_tmpb_tl }
+          }
+%    \end{macrocode}
+%   And \cs[no-index]{l_@@_tmpa_tl} contains:
+%\begin{verbatim}
+%   {\c__hook_hash_tl 1}
+%   {\c__hook_hash_tl 2}
+%   ...
+%   {\c__hook_hash_tl <n>}
+%\end{verbatim}
+%   with \meta{n} being the number of arguments declared for the hook.
+%    \begin{macrocode}
+      \exp_last_unbraced:NNf
+      \cs_set:Npn \@@_tmp:w { \@@_parameter:n {#1} } { }
+      \tl_set:Ne \l_@@_tmpa_tl { \@@_braced_cs_parameter:n { @@_tmp:w } }
+%    \end{macrocode}
+%   Now this function does the fun part.  It is meant to be used with
+%   \cs{prop_map_function:NN}, taking a label name in \verb|##1| and the
+%   code stored in that label in \verb|##2|.
+%    \begin{macrocode}
+      \cs_set_protected:Npx \@@_normalise_fn:nn ##1 ##2
+        {
+%    \end{macrocode}
+%   Here we'll define two auxiliary macros:  the first one throws an
+%   error when it detects an invalid argument reference.  It piggybacks
+%   on \TeX's low-level \enquote{Illegal parameter number} error, but it
+%   defines a weirdly-named control sequence so that the error comes out
+%   nicely formatted.  For example, if the label \enquote{badpkg} adds
+%   some code that references argument \verb|#3| in the hook
+%   \enquote{foo}, which takes only two arguments, the error will be:
+%\begin{verbatim}
+%   ! Illegal parameter number in definition of hook 'foo'.
+%   (hooks)             Offending label: 'badpkg'.
+%   <to be read again> 
+%                      3
+%\end{verbatim}
+%   At the point of this definition, the error is raised if the code
+%   happens to reference an invalid argument.  If it was possible to
+%   detect that this definition raised no error, the next step would be
+%   unnecessary.
+%    \begin{macrocode}
+          \cs_set:cpn
+              {
+                hook~'#1'. ^^J
+                (hooks) \prg_replicate:nn { 13 } { ~ }
+                Offending~label:~'##1'
+              }
+              \exp_not:v { c_@@_#1_parameter_tl }
+            {##2}
+%    \end{macrocode}
+%   This next macro, with a much less fabulous name, takes always nine
+%   arguments, and it just transfers the code \verb|##2| under the label
+%   \verb|##1| to the temporary property list.  The first \meta{n}
+%   arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
+%   $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
+%   twice as many \verb|#| tokens as the former).  Then,
+%   \cs{@@_double_hashes:n} is used to double non-argument hashes, and
+%   expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
+%   parameter tokens.
+%    \begin{macrocode}
+          \cs_set:Npn \exp_not:N \@@_tmp:w
+              \exp_not:V \c_@@_nine_parameters_tl
+            {
+              \prop_put:Nne \exp_not:N \l_@@_work_prop
+                {##1} { \exp_not:N \@@_double_hashes:n {##2} }
+            }
+%    \end{macrocode}
+%   This next macro, with a much less fabulous name, takes always nine
+%   arguments, and it just transfers the code \verb|##2| under the label
+%   \verb|##1| to the temporary property list.  The first \meta{n}
+%   arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
+%   $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
+%   twice as many \verb|#| tokens as the former).  Then,
+%   \cs{@@_double_hashes:n} is used to double non-argument hashes, and
+%   expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
+%   parameter tokens.
+%    \begin{macrocode}
+          \exp_not:N \@@_tmp:w
+            \exp_not:V \l_@@_tmpa_tl
+            \exp_args:No \exp_not:o
+              { \exp_after:wN \@@_tmp:w \l_@@_tmpb_tl }
+        }
+%    \end{macrocode}
+%   Now we'll set \cs{tex_escapechar:D} to $-1$ so the hack above shows
+%   up extra nice in the case of an error, then we'll loop over the
+%   hook's code pool applying the normalisation above.  After that's
+%   done, copy the temporary property list back to the hook's.
+%    \begin{macrocode}
+      \int_set:Nn \tex_escapechar:D { -1 }
+      \prop_clear:N \l_@@_work_prop
+      \prop_map_function:cN { g_@@_#1_code_prop } \@@_normalise_fn:nn
+      \prop_gset_eq:cN { g_@@_#1_code_prop } \l_@@_work_prop
+    \group_end:
+  }
+\cs_new_eq:NN \@@_normalise_fn:nn ?
+%<latexrelease>\EndIncludeInRelease
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_normalise_code_pool:n}
+%<latexrelease>                 {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_normalise_code_pool:n
+%<latexrelease>\EndIncludeInRelease
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[pTF]{\@@_cs_if_empty:c}
 %   Check if the expansion of a control sequence is empty by looking at
 %   its replacement text.
diff --git a/base/testfiles-lthooks/lthooks-033.lvt b/base/testfiles-lthooks/lthooks-033.lvt
index e8662047..79f3ee3b 100644
--- a/base/testfiles-lthooks/lthooks-033.lvt
+++ b/base/testfiles-lthooks/lthooks-033.lvt
@@ -1,8 +1,8 @@
 
-% \RequirePackage[enable-debug]{expl3}
-% \ExplSyntaxOn
-% \debug_on:n { check-declarations , deprecation }
-% \ExplSyntaxOff
+\RequirePackage[enable-debug]{expl3}
+\ExplSyntaxOn
+\debug_on:n { check-declarations , deprecation }
+\ExplSyntaxOff
 
 \input{regression-test}
 
diff --git a/base/testfiles-lthooks/lthooks-033.tlg b/base/testfiles-lthooks/lthooks-033.tlg
index 41c76202..ffe7f490 100644
--- a/base/testfiles-lthooks/lthooks-033.tlg
+++ b/base/testfiles-lthooks/lthooks-033.tlg
@@ -421,21 +421,8 @@ l. ...  }
 You meant to type ## instead of #, right?
 Or maybe a } was forgotten somewhere earlier, and things
 are all screwed up? I'm going to assume that you meant ##.
-! Illegal parameter number in definition of \__hook liar.
-<to be read again> 
-                   1
-l. ...  }
-You meant to type ## instead of #, right?
-Or maybe a } was forgotten somewhere earlier, and things
-are all screwed up? I'm going to assume that you meant ##.
-! Illegal parameter number in definition of \__hook liar.
-<to be read again> 
-                   1
-l. ...  }
-You meant to type ## instead of #, right?
-Or maybe a } was forgotten somewhere earlier, and things
-are all screwed up? I'm going to assume that you meant ##.
-! Illegal parameter number in definition of \__hook liar.
+! Illegal parameter number in definition of hook 'liar'.
+(hooks)             Offending label: 'label'.
 <to be read again> 
                    1
 l. ...  }
@@ -444,7 +431,7 @@ Or maybe a } was forgotten somewhere earlier, and things
 are all screwed up? I'm going to assume that you meant ##.
 -> The hook 'liar':
 > Code chunks:
->     label -> \typeout {label-arg(#1)}\typeout {label-hash(##1)}\typeout {more-label-hash(##1)}
+>     label -> \typeout {label-arg(##1)}\typeout {label-hash(##1)}\typeout {more-label-hash(##1)}
 > Document-level (top-level) code (executed last):
 >     -> \typeout {top-arg(##1)}\typeout {top-hash(##1)}\typeout {more-top-hash(##1)}
 > Extra code for next invocation:
diff --git a/base/testfiles-lthooks/lthooks-rollback-args.tlg b/base/testfiles-lthooks/lthooks-rollback-args.tlg
index 2259b728..af6ec45e 100644
--- a/base/testfiles-lthooks/lthooks-rollback-args.tlg
+++ b/base/testfiles-lthooks/lthooks-rollback-args.tlg
@@ -170,6 +170,8 @@ Skipping: [....-..-..] Hooks with args on input line ...
 Applying: [....-..-..] Hooks with args on input line ....
 Skipping: [....-..-..] Hooks with args on input line ....
 Applying: [....-..-..] Hooks with args on input line ....
+Skipping: [....-..-..] Hooks with args on input line ....
+Applying: [....-..-..] Hooks with args on input line ....
 Skipping: [....-..-..] Refuse setting rule for one-time hooks on input line ....
 Applying: [....-..-..] Refuse setting rule for one-time hooks on input line ....
 Skipping: [....-..-..] Hooks with args on input line ....





More information about the latex3-commits mailing list.