summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3debug.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3debug.dtx1447
1 files changed, 1447 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx b/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
new file mode 100644
index 00000000000..55b16ec0e21
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
@@ -0,0 +1,1447 @@
+% \iffalse meta-comment
+%
+%% File: l3debug.dtx
+%
+% Copyright (C) 2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \textsf{l3debug} package\\ Debugging support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-09-28}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \begin{function}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N}
+% \begin{syntax}
+% \cs{__kernel_chk_var_local:N} \meta{var}
+% \cs{__kernel_chk_var_global:N} \meta{var}
+% \end{syntax}
+% Applies \cs{__kernel_chk_var_exist:N} \meta{var} as well as
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}, where
+% \meta{scope} is |l| or~|g|.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_var_scope:NN}
+% \begin{syntax}
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}
+% \end{syntax}
+% Checks the \meta{var} has the correct \meta{scope}, and if not
+% raises a kernel-level error. This function is only created if
+% debugging is enabled. The \meta{scope} is a single letter |l|, |g|,
+% |c| denoting local variables, global variables, or constants. More
+% precisely, if the variable name starts with a letter and an
+% underscore (normal \pkg{expl3} convention) the function checks that
+% this single letter matches the \meta{scope}. Otherwise the function
+% cannot know the scope \meta{var} the first time: instead, it defines
+% |\__debug_chk_/|\meta{var name} to store that information for the
+% next call. Thus, if a given \meta{var} is subject to assignments of
+% different scopes a kernel error will result.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_chk_cs_exist:N,
+% \__kernel_chk_cs_exist:c,
+% \__kernel_chk_var_exist:N
+% }
+% \begin{syntax}
+% \cs{__kernel_chk_cs_exist:N} \meta{cs}
+% \cs{__kernel_chk_var_exist:N} \meta{var}
+% \end{syntax}
+% These functions are only created if debugging is enabled. They
+% check that their argument is defined according to the criteria for
+% \cs{cs_if_exist_p:N},
+% and if not raises a kernel-level error. Error messages are
+% different.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_chk_flag_exist:n}
+% \begin{syntax}
+% \cs{__kernel_chk_flag_exist:n} \Arg{flag}
+% \end{syntax}
+% This function is only created if debugging is enabled. It checks
+% that the \meta{flag} is defined according to the criterion for
+% \cs{flag_if_exist_p:n}, and if not raises a kernel-level error.
+% \end{function}
+%
+% \begin{function}{\__kernel_debug_log:x}
+% \begin{syntax}
+% \cs{__kernel_debug_log:x} \Arg{message text}
+% \end{syntax}
+% If the \texttt{log-functions} option is active, this function writes
+% the \meta{message text} to the log file using \cs{iow_log:x}.
+% Otherwise, the \meta{message text} is ignored using \cs{use_none:n}.
+% This function is only created if debugging is enabled.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3debug} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=debug>
+% \end{macrocode}
+%
+% Standard file identification.
+% \begin{macrocode}
+\ProvidesExplFile{l3debug.def}{2019-04-06}{}{L3 Debugging support}
+% \end{macrocode}
+%
+% \begin{macro}{\debug_on:n, \debug_off:n, \@@_all_on:, \@@_all_off:}
+% \begin{macrocode}
+\cs_set_protected:Npn \debug_on:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { @@_ ##1 _on: }
+ { \__kernel_msg_error:nnn { kernel } { debug } {##1} }
+ }
+ }
+\cs_set_protected:Npn \debug_off:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { @@_ ##1 _off: }
+ { \__kernel_msg_error:nnn { kernel } { debug } {##1} }
+ }
+ }
+\cs_new_protected:Npn \@@_all_on:
+ {
+ \debug_on:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+\cs_new_protected:Npn \@@_all_off:
+ {
+ \debug_off:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_suspend:, \debug_resume:}
+% \begin{macro}{\@@_suspended:T}
+% \begin{macro}{\l_@@_suspended_tl}
+% Suspend and resume locally all debug-related errors and logging
+% except deprecation errors. The \cs{debug_suspend:} and \cs{debug_resume:}
+% pairs can be nested. We keep track of nesting in a token list
+% containing a number of periods. At first begin with the
+% \enquote{non-suspended} version of \cs{@@_suspended:T}.
+% \begin{macrocode}
+\tl_new:N \l_@@_suspended_tl { }
+\cs_set_protected:Npn \debug_suspend:
+ {
+ \tl_put_right:Nn \l_@@_suspended_tl { . }
+ \cs_set_eq:NN \@@_suspended:T \use:n
+ }
+\cs_set_protected:Npn \debug_resume:
+ {
+ \tl_set:Nx \l_@@_suspended_tl
+ { \tl_tail:N \l_@@_suspended_tl }
+ \tl_if_empty:NT \l_@@_suspended_tl
+ {
+ \cs_set_eq:NN \@@_suspended:T \use_none:n
+ }
+ }
+\cs_new_eq:NN \@@_suspended:T \use_none:n
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_check-declarations_on:, \@@_check-declarations_off:}
+% \begin{macro}
+% {
+% \__kernel_chk_var_exist:N,
+% \__kernel_chk_cs_exist:N,
+% \__kernel_chk_cs_exist:c
+% }
+% \begin{macro}[EXP]{\__kernel_chk_flag_exist:n}
+% \begin{macro}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N}
+% \begin{macro}{\__kernel_chk_var_scope:NN}
+% When debugging is enabled these two functions set up functions that
+% test their argument (when \texttt{check-declarations} is active)
+% \begin{itemize}
+% \item \cs{__kernel_chk_var_exist:N} and \cs{__kernel_chk_cs_exist:N}, two
+% functions that test that their argument is defined;
+% \item \cs{__kernel_chk_var_scope:NN} that checks that its argument |#2|
+% has scope |#1|.
+% \item \cs{__kernel_chk_var_local:N} and \cs{__kernel_chk_var_global:N} that
+% perform both checks.
+% \end{itemize}
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_chk_var_exist:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_cs_exist:N #1 { }
+\cs_generate_variant:Nn \__kernel_chk_cs_exist:N { c }
+\cs_new:Npn \__kernel_chk_flag_exist:n #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_local:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_global:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_scope:NN #1#2 { }
+\cs_new_protected:cpn { @@_check-declarations_on: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \__kernel_msg_error:nnx { kernel } { non-declared-variable }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \__kernel_msg_error:nnx { kernel } { command-not-defined }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set:Npn \__kernel_chk_flag_exist:n ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \flag_if_exist:nF {##1}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { bad-variable } { flag~##1~ }
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN
+ {
+ \@@_suspended:T \use_none:nnn
+ \@@_chk_var_scope_aux:NN
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1
+ {
+ \@@_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \@@_chk_var_scope_aux:NN l ##1
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1
+ {
+ \@@_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \@@_chk_var_scope_aux:NN g ##1
+ }
+ }
+\cs_new_protected:cpn { @@_check-declarations_off: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1 { }
+ \cs_set:Npn \__kernel_chk_flag_exist:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN ##1##2 { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_chk_var_scope_aux:NN}
+% \begin{macro}{\@@_chk_var_scope_aux:Nn}
+% \begin{macro}{\@@_chk_var_scope_aux:NNn}
+% First check whether the name of the variable |#2| starts with
+% \meta{letter}|_|. If it does then pass that letter, the
+% \meta{scope}, and the variable name to
+% \cs{@@_chk_var_scope_aux:NNn}. That function compares the two
+% letters and triggers an error if they differ (the \cs{scan_stop:}
+% case is not reachable here). If the second character was not |_|
+% then pass the same data to the same auxiliary, except for its first
+% argument which is now a control sequence. That control sequence is
+% actually a token list (but to avoid triggering the checking code we
+% manipulate it using \cs{cs_set_nopar:Npn}) containing a single
+% letter \meta{scope} according to what the first assignment to the
+% given variable was.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_var_scope_aux:NN #1#2
+ { \exp_args:NNf \@@_chk_var_scope_aux:Nn #1 { \cs_to_str:N #2 } }
+\cs_new_protected:Npn \@@_chk_var_scope_aux:Nn #1#2
+ {
+ \if:w _ \use_i:nn \use_i_delimit_by_q_stop:nw #2 ? ? \q_stop
+ \exp_after:wN \@@_chk_var_scope_aux:NNn
+ \use_i_delimit_by_q_stop:nw #2 ? \q_stop
+ #1 {#2}
+ \else:
+ \exp_args:Nc \@@_chk_var_scope_aux:NNn
+ { @@_chk_/ #2 }
+ #1 {#2}
+ \fi:
+ }
+\cs_new_protected:Npn \@@_chk_var_scope_aux:NNn #1#2#3
+ {
+ \if:w #1 #2
+ \else:
+ \if:w #1 \scan_stop:
+ \cs_gset_nopar:Npn #1 {#2}
+ \else:
+ \__kernel_msg_error:nnxxx { kernel } { local-global }
+ {#1} {#2} { \iow_char:N \\ #3 }
+ \fi:
+ \fi:
+ }
+\use:c { @@_check-declarations_off: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_log-functions_on:, \@@_log-functions_off:}
+% \begin{macro}{\__kernel_debug_log:x}
+% These two functions (corresponding to the \pkg{expl3} option
+% \texttt{log-functions}) control whether \cs{__kernel_debug_log:x}
+% writes to the log file or not. By default, logging is off.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_log-functions_on: }
+ {
+ \cs_set_protected:Npn \__kernel_debug_log:x
+ { \@@_suspended:T \use_none:nn \iow_log:x }
+ }
+\cs_new_protected:cpn { @@_log-functions_off: }
+ { \cs_set_protected:Npn \__kernel_debug_log:x { \use_none:n } }
+\cs_new_protected:Npn \__kernel_debug_log:x { \use_none:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_check-expressions_on:, \@@_check-expressions_off:}
+% \begin{macro}{\__kernel_chk_expr:nNnN}
+% \begin{macro}{\@@_chk_expr_aux:nNnN}
+% When debugging is enabled these two functions set
+% \cs{__kernel_chk_expr:nNnN} to test or not whether the given
+% expression is valid. The idea is to evaluate the expression within
+% a brace group (to catch trailing \cs{use_none:nn} or similar), then
+% test that the result is what we expect. This is done by turning it
+% to an integer and hitting that with \cs{tex_romannumeral:D} after
+% replacing the first character by |-0|. If all goes well, that
+% primitive finds a non-positive integer and gives an empty output.
+% If the original expression evaluation stopped early it leaves a
+% trailing \cs{tex_relax:D}, which stops the second evaluation (used
+% to convert to integer) before it encounters the final
+% \cs{tex_relax:D}. Since \cs{tex_romannumeral:D} does not absorb
+% \cs{tex_relax:D} the output will be nonempty. Note that |#3| is
+% empty except for mu expressions for which it is \cs{tex_mutoglue:D}
+% to avoid an \enquote{incompatible glue units} error. Note also that
+% if we had omitted the first \cs{tex_relax:D} then for instance
+% |1+2\relax+3| would incorrectly be accepted as a valid integer
+% expression.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_check-expressions_on: }
+ {
+ \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2
+ {
+ \@@_suspended:T { ##1 \use_none:nnnnnnn }
+ \exp_after:wN \@@_chk_expr_aux:nNnN
+ \exp_after:wN { \tex_the:D ##2 ##1 \scan_stop: }
+ ##2
+ }
+ }
+\cs_new_protected:cpn { @@_check-expressions_off: }
+ { \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2##3##4 {##1} }
+\cs_new:Npn \__kernel_chk_expr:nNnN #1#2#3#4 {#1}
+\cs_new:Npn \@@_chk_expr_aux:nNnN #1#2#3#4
+ {
+ \tl_if_empty:oF
+ {
+ \tex_romannumeral:D - 0
+ \exp_after:wN \use_none:n
+ \int_value:w #3 #2 #1 \scan_stop:
+ }
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { expr } {#4} {#1}
+ }
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_deprecation_on:, \@@_deprecation_off:}
+% Some commands were more recently deprecated and not yet removed;
+% only make these into errors if the user requests it. This relies on
+% two token lists, filled up in \pkg{l3deprecation} by calls to
+% \cs{__kernel_deprecation_code:nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_deprecation_on:
+ { \g_@@_deprecation_on_tl }
+\cs_new_protected:Npn \@@_deprecation_off:
+ { \g_@@_deprecation_off_tl }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{
+% \l_@@_internal_tl,
+% \l_@@_tmpa_tl,
+% \l_@@_tmpb_tl,
+% }
+% For patching.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+\tl_new:N \l_@@_tmpa_tl
+\tl_new:N \l_@@_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+% \@@_generate_parameter_list:NNN,
+% \@@_build_parm_text:n,
+% \@@_build_arg_list:n,
+% \@@_arg_list_from_signature:nNN,
+% \@@_arg_check_invalid:N,
+% \@@_parm_terminate:w,
+% \@@_arg_if_braced:n,
+% \@@_get_base_form:N,
+% \@@_arg_return:N,
+% }
+% \begin{macro}[TF]{\@@_arg_if_braced:N}
+% Some functions don't take the arguments their signature indicates.
+% For instance, \cs{clist_concat:NNN} doesn't take (directly) any
+% argument, so patching it with something that uses |#1|, |#2|, or
+% |#3| results in ``Illegal parameter number in definition of
+% \cs{clist_concat:NNN}''.
+%
+% Instead of changing \emph{the} definition of the macros, we'll
+% create a copy of such macros, say, |__debug_clist_concat:NNN| which
+% will be defined as
+% |<debug code with #1, #2 and #3>\clist_concat:NNN#1#2#3|. For that
+% we need to identify the signature of every function and build the
+% appropriate parameter list.
+%
+% \cs{@@_generate_parameter_list:NNN} takes a function in |#1| and
+% returns teo parameter lists: |#2| contains the simple |#1#2#3| as
+% would be used in the \meta{parameter~text} of the definition and
+% |#3| contains the same parameters but with braces where necessary.
+%
+% With the current implementation the resulting |#3| is, for example
+% for |\some_function:NnNn|, |#1{#2}#3{#4}|. While this is correct,
+% it might be unnecessary. Bracing everything will usually have the
+% same outcome (unless the function was misused in the first place).
+% What should be done?
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_generate_parameter_list:NNN #1#2#3
+ {
+ \tl_set:Nx \l_@@_internal_tl
+ { \exp_last_unbraced:Nf \use_ii:nnn \cs_split_function:N #1 }
+ \tl_set:Nx #2
+ { \exp_args:NV \@@_build_parm_text:n \l_@@_internal_tl }
+ \tl_set:Nx #3
+ { \exp_args:NV \@@_build_arg_list:n \l_@@_internal_tl }
+ }
+\cs_new:Npn \@@_build_parm_text:n #1
+ {
+ \@@_arg_list_from_signature:nNN { 1 } \c_false_bool #1
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_build_arg_list:n #1
+ {
+ \@@_arg_list_from_signature:nNN { 1 } \c_true_bool #1
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_arg_list_from_signature:nNN #1 #2 #3
+ {
+ \quark_if_recursion_tail_stop:N #3
+ \@@_arg_check_invalid:N #3
+ \bool_if:NT #2 { \@@_arg_if_braced:NT #3 { \use_none:n } }
+ \use:n { \c_hash_str \int_eval:n {#1} }
+ \exp_args:Nf \@@_arg_list_from_signature:nNN
+ { \int_eval:n {#1+1} } #2
+ }
+% \end{macrocode}
+% Argument types |w|, |p|, |T|, and |F| shouldn't be included in the
+% parameter lists, so we abort the loop if either is found.
+% \begin{macrocode}
+\cs_new:Npn \@@_arg_check_invalid:N #1
+ {
+ \if:w w #1 \@@_parm_terminate:w \else:
+ \if:w p #1 \@@_parm_terminate:w \else:
+ \if:w T #1 \@@_parm_terminate:w \else:
+ \if:w F #1 \@@_parm_terminate:w \else:
+ \exp:w
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_end:
+ }
+\cs_new:Npn \@@_parm_terminate:w
+ { \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \exp:w }
+\prg_new_conditional:Npnn \@@_arg_if_braced:N #1 { T }
+ { \exp_args:Nf \@@_arg_if_braced:n { \@@_get_base_form:N #1 } }
+\cs_new:Npn \@@_arg_if_braced:n #1
+ {
+ \if:w n #1 \prg_return_true: \else:
+ \if:w N #1 \prg_return_false: \else:
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { bad-arg-type } {#1}
+ \fi:
+ \fi:
+ }
+\__kernel_msg_new:nnn { kernel } { bad-arg-type }
+ { Wrong~argument~type~#1. }
+% \end{macrocode}
+% The macro below is a modifiec copy of
+% |\__cs_generate_variant_loop_base:N| to get the base form of an
+% argument type given a variant. It serves only to differentiate
+% arguments which should be braced from ones which shouldn't. If all
+% were to be braced this would be unnecessary. I moved the |n| and |N|
+% variants to the beginning of the test as the are much more common
+% here.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_base_form:N #1
+ {
+ \if:w n #1 \@@_arg_return:N n \else:
+ \if:w N #1 \@@_arg_return:N N \else:
+ \if:w c #1 \@@_arg_return:N N \else:
+ \if:w o #1 \@@_arg_return:N n \else:
+ \if:w V #1 \@@_arg_return:N n \else:
+ \if:w v #1 \@@_arg_return:N n \else:
+ \if:w f #1 \@@_arg_return:N n \else:
+ \if:w e #1 \@@_arg_return:N n \else:
+ \if:w x #1 \@@_arg_return:N n \else:
+ \@@_arg_return:N \scan_stop:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_arg_return:N #1
+ { \exp_after:wN #1 \exp:w \exp_end_continue_f:w }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+% \__kernel_patch:nnn,
+% \__kernel_patch_aux:nnn,
+% \@@_setup_debug_code:Nnn,
+% \@@_add_to_debug_code:Nnn,
+% \@@_insert_debug_code:Nnn,
+% \__kernel_patch_weird:nnn,
+% \__kernel_patch_weird_aux:nnn,
+% \@@_patch_weird:Nnn,
+% }
+% Simple patching by adding material at the start and end of (a
+% collection of) functions is straight-forward as we know the catcode
+% set up. The approach is essentially that in \pkg{etoolbox}. Notice
+% the need to worry about spaces: those are otherwise lost as normally
+% in \pkg{expl3} code they would be~|~|.
+%
+% As discussed above, some functions don't take arguments, so we can't
+% patch something that uses an argument in them. For these functions
+% \cs{__kernel_patch:nnn} is used. It starts by creating a copy of the
+% function (say, \cs{clist_concat:NNN}) with a |__debug_| prefix in
+% the name. This copy won't be changed. The code redefines the
+% original function to take the exact same arguments as advertised in
+% its signature (see \cs{@@_generate_parameter_list:NNN} above).
+% The redefined function also contains the debug code in the proper
+% position. If a function with the same name and the |__debug_| prefix
+% was already defined, then the macro patches that definition by
+% adding more debug code to it.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ {
+ \cs_if_exist:cTF { __debug_ \cs_to_str:N ##1 }
+ { \@@_add_to_debug_code:Nnn }
+ { \@@_setup_debug_code:Nnn }
+ ##1 {#1} {#2}
+ }
+ \group_end:
+ }
+ \cs_set_protected:Npn \@@_setup_debug_code:Nnn #1#2#3
+ {
+ \cs_gset_eq:cN { __debug_ \cs_to_str:N #1 } #1
+ \@@_generate_parameter_list:NNN #1 \l_@@_tmpa_tl \l_@@_tmpb_tl
+ \exp_args:Nx \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \tl_use:N \l_@@_tmpa_tl
+ {
+ \tl_to_str:n {#2}
+ \exp_not:c { __debug_ \cs_to_str:N #1 }
+ \tl_use:N \l_@@_tmpb_tl
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ \cs_set_protected:Npn \@@_add_to_debug_code:Nnn #1#2#3
+ {
+ \use:x
+ {
+ \cs_set:Npn \exp_not:N \@@_tmp:w
+ ####1 \tl_to_str:n { macro: }
+ ####2 \tl_to_str:n { -> }
+ ####3 \c_backslash_str \tl_to_str:n { __debug_ }
+ \cs_to_str:N #1
+ ####4 \exp_not:N \q_mark
+ {
+ \exp_not:N \exp_args:Nx \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D ####1
+ \tex_def:D \exp_not:N #1 ####2
+ {
+ ####3 \tl_to_str:n {#2}
+ \c_backslash_str __debug_ \cs_to_str:N #1
+ ####4 \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+ \exp_after:wN \@@_tmp:w \cs_meaning:N #1 \q_mark
+ }
+% \end{macrocode}
+% Some functions, however, won't work with the signature reading setup
+% above because their signature contains |w|eird arguments. These
+% functions need to be patched using \cs{__kernel_patch_weird:nnn},
+% which won't make a copy of the function, rather it will patch the
+% debug code directly into it. This means that whatever argument the
+% debug code uses must be actually used by the patched function.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_weird:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_weird_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_weird_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ { \@@_patch_weird:Nnn ##1 {#1} {#2} }
+ \group_end:
+ }
+ \cs_set_protected:Npn \@@_patch_weird:Nnn #1#2#3
+ {
+ \use:x
+ {
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \cs_argument_spec:N #1
+ {
+ \tl_to_str:n {#2}
+ \cs_replacement_spec:N #1
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Patching the second argument to ensure it exists. This happens before
+% we alter |#1| so the ordering is correct. For many variable types
+% such as \texttt{int} a low-level error occurs when |#2| is unknown, so
+% adding a check is not needed.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_exist:N #2 }
+ { }
+ {
+ \bool_set_eq:NN
+ \bool_gset_eq:NN
+ \clist_set_eq:NN
+ \clist_gset_eq:NN
+ \fp_set_eq:NN
+ \fp_gset_eq:NN
+ \prop_set_eq:NN
+ \prop_gset_eq:NN
+ \seq_set_eq:NN
+ \seq_gset_eq:NN
+ \str_set_eq:NN
+ \str_gset_eq:NN
+ \tl_set_eq:NN
+ \tl_gset_eq:NN
+ }
+% \end{macrocode}
+%
+% Patching both second and third arguments.
+% \begin{macrocode}
+% \tracingall
+ \__kernel_patch:nnn
+ {
+ \__kernel_chk_var_exist:N #2
+ \__kernel_chk_var_exist:N #3
+ }
+ { }
+ {
+ \clist_concat:NNN
+ \clist_gconcat:NNN
+ \seq_concat:NNN
+ \seq_gconcat:NNN
+ \str_concat:NNN
+ \str_gconcat:NNN
+ \tl_concat:NNN
+ \tl_gconcat:NNN
+ }
+% \tracingnone
+% \end{macrocode}
+%
+% Patching where the first argument to a function needs scope-checking:
+% either local or global (so two lists).
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_local:N #1 }
+ { }
+ {
+ \bool_set:Nn
+ \bool_set_eq:NN
+ \bool_set_true:N
+ \bool_set_false:N
+ \box_set_eq:NN
+ \box_set_eq_drop:NN
+ \box_set_to_last:N
+ \clist_set_eq:NN
+ \dim_zero:N
+ \dim_set:Nn
+ \dim_set_eq:NN
+ \dim_add:Nn
+ \dim_sub:Nn
+ \fp_set_eq:NN
+ \int_zero:N
+ \int_set_eq:NN
+ \int_add:Nn
+ \int_sub:Nn
+ \int_incr:N
+ \int_decr:N
+ \int_set:Nn
+ \hbox_set:Nn
+ \hbox_set_to_wd:Nnn
+ \hbox_set:Nw
+ \hbox_set_to_wd:Nnw
+ \muskip_zero:N
+ \muskip_set:Nn
+ \muskip_add:Nn
+ \muskip_sub:Nn
+ \muskip_set_eq:NN
+ \seq_set_eq:NN
+ \skip_zero:N
+ \skip_set:Nn
+ \skip_set_eq:NN
+ \skip_add:Nn
+ \skip_sub:Nn
+ \tl_set:Nn
+ \tl_set:No
+ \tl_set:Nx
+ \tl_set_eq:NN
+ \tl_put_left:Nn
+ \tl_put_left:NV
+ \tl_put_left:No
+ \tl_put_left:Nx
+ \tl_put_right:Nn
+ \tl_put_right:NV
+ \tl_put_right:No
+ \tl_put_right:Nx
+ \tl_build_begin:N
+ \tl_build_put_right:Nn
+ \tl_build_put_right:Nx
+ \tl_build_put_left:Nn
+ \vbox_set:Nn
+ \vbox_set_top:Nn
+ \vbox_set_to_ht:Nnn
+ \vbox_set:Nw
+ \vbox_set_to_ht:Nnw
+ \vbox_set_split_to_ht:NNn
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_global:N #1 }
+ { }
+ {
+ \bool_gset:Nn
+ \bool_gset_eq:NN
+ \bool_gset_true:N
+ \bool_gset_false:N
+ \box_gset_eq:NN
+ \box_gset_eq_drop:NN
+ \box_gset_to_last:N
+ \clist_gset_eq:NN
+ \dim_gset_eq:NN
+ \dim_gzero:N
+ \dim_gset:Nn
+ \dim_gadd:Nn
+ \dim_gsub:Nn
+ \fp_gset_eq:NN
+ \int_gzero:N
+ \int_gset_eq:NN
+ \int_gadd:Nn
+ \int_gsub:Nn
+ \int_gincr:N
+ \int_gdecr:N
+ \int_gset:Nn
+ \hbox_gset:Nn
+ \hbox_gset_to_wd:Nnn
+ \hbox_gset:Nw
+ \hbox_gset_to_wd:Nnw
+ \muskip_gzero:N
+ \muskip_gset:Nn
+ \muskip_gadd:Nn
+ \muskip_gsub:Nn
+ \muskip_gset_eq:NN
+ \seq_gset_eq:NN
+ \skip_gzero:N
+ \skip_gset:Nn
+ \skip_gset_eq:NN
+ \skip_gadd:Nn
+ \skip_gsub:Nn
+ \tl_gset:Nn
+ \tl_gset:No
+ \tl_gset:Nx
+ \tl_gset_eq:NN
+ \tl_gput_left:Nn
+ \tl_gput_left:NV
+ \tl_gput_left:No
+ \tl_gput_left:Nx
+ \tl_gput_right:Nn
+ \tl_gput_right:NV
+ \tl_gput_right:No
+ \tl_gput_right:Nx
+ \tl_build_gbegin:N
+ \tl_build_gput_right:Nn
+ \tl_build_gput_right:Nx
+ \tl_build_gput_left:Nn
+ \vbox_gset:Nn
+ \vbox_gset_top:Nn
+ \vbox_gset_to_ht:Nnn
+ \vbox_gset:Nw
+ \vbox_gset_to_ht:Nnw
+ \vbox_gset_split_to_ht:NNn
+ }
+% \end{macrocode}
+%
+% Scoping for constants.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN c #1 }
+ { }
+ {
+ \bool_const:Nn
+ \dim_const:Nn
+ \int_const:Nn
+ \intarray_const_from_clist:Nn
+ \muskip_const:Nn
+ \skip_const:Nn
+ \tl_const:Nn
+ \tl_const:Nx
+ }
+% \end{macrocode}
+%
+% Flag functions.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_flag_exist:n {#1} }
+ { }
+ {
+ \flag_if_raised:nT
+ \flag_if_raised:nF
+ \flag_if_raised:nTF
+ \flag_if_raised_p:n
+ \flag_height:n
+ \flag_raise_if_clear:n
+ \flag_clear:n
+ }
+% \end{macrocode}
+%
+% Various one-offs.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_cs_exist:N #1 }
+ { }
+ { \cs_generate_variant:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN g #1 }
+ { }
+ { \intarray_new:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN q #1 }
+ { }
+ { \quark_new:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN s #1 }
+ { }
+ { \scan_new:N }
+% \end{macrocode}
+%
+% Patch various internal commands to log definitions of functions.
+% First, a kernel internal. Then internals from the \pkg{cs},
+% \pkg{keys} and \pkg{msg} modules.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:x
+ { Defining~\token_to_str:N #1~ \msg_line_context: }
+ }
+ { \__kernel_chk_if_free_cs:N }
+%<@@=cs>
+ \__kernel_patch_weird:nnn
+ {
+ \cs_if_free:NF #4
+ {
+ \__kernel_debug_log:x
+ {
+ Variant~\token_to_str:N #4~%
+ already~defined;~ not~ changing~ it~ \msg_line_context:
+ }
+ }
+ }
+ { }
+ { \@@_generate_variant:wwNN }
+%<@@=keys>
+ \__kernel_patch:nnn
+ {
+ \cs_if_exist:cF { \c_@@_code_root_tl #1 }
+ { \__kernel_debug_log:x { Defining~key~#1~\msg_line_context: } }
+ }
+ { }
+ { \@@_cmd_set:nn }
+%<@@=msg>
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:x
+ { Defining~message~ #1 / #2 ~\msg_line_context: }
+ }
+ { \@@_chk_free:nn }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+% Internal functions from \pkg{prg} module.
+% \begin{macrocode}
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 _p : #6 } }
+ { }
+ { \@@_set_eq_conditional_p_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 TF } }
+ { }
+ { \@@_set_eq_conditional_TF_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 T } }
+ { }
+ { \@@_set_eq_conditional_T_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 F } }
+ { }
+ { \@@_set_eq_conditional_F_form:wNnnnn }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=regex>
+% \end{macrocode}
+% Internal functions from \pkg{regex} module.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_escape_use:nnnn
+ \group_begin:
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_escape_use:nnnn }
+ \use_none:nnn
+ }
+ { }
+ { \@@_escape_use:nnn }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build:N }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build:N
+ }
+ { \@@_build:N }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build_for_cs:n }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build_for_cs:n
+ }
+ { \@@_build_for_cs:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace:nnx { regex } { 2 }
+ {
+ regex~new~state~
+ L=\int_use:N \l_@@_left_state_int ~ -> ~
+ R=\int_use:N \l_@@_right_state_int ~ -> ~
+ M=\int_use:N \l_@@_max_state_int ~ -> ~
+ \int_eval:n { \l_@@_max_state_int + 1 }
+ }
+ }
+ { }
+ { \@@_build_new_state: }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+ { \@@_group_aux:nnnnN }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_branch:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_branch:n }
+ { \@@_branch:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match:n
+ \@@_trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match:n }
+ { \@@_match:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match_cs:n
+ \@@_trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match_cs:n }
+ { \@@_match_cs:n }
+ \__kernel_patch:nnn
+ { \@@_trace:nnx { regex } { 1 } { initializing } }
+ { }
+ { \@@_match_init: }
+ \__kernel_patch:nnn
+ {
+ \@@_trace:nnx { regex } { 2 }
+ { state~\int_use:N \l_@@_curr_state_int }
+ }
+ { }
+ { \@@_use_state: }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_replacement:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_replacement:n }
+ { \@@_replacement:n }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=debug>
+% \end{macrocode}
+%
+% Patching arguments is a bit more involved: we do these one at a time.
+% The basic idea is the same, using a |#| token that is a string.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:Nn #1
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:Nn #1
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:Nn #1#2
+ {
+ \char_set_catcode_parameter:N \#
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_args:Nx \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1 \tex_def:D \exp_not:N #1
+ \cs_argument_spec:N #1
+ { \exp_args:No \tl_to_str:n { #1 #2 } }
+ }
+ \group_end:
+ }
+% \end{macrocode}
+%
+% The functions here can get a bit repetitive, so we define a helper
+% which can re-use the same patch code repeatedly. The main part of the
+% patch is the same, so we just have to deal with the part which varies
+% depending on the type of expression.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNx \__kernel_patch:Nn ##1
+ {
+ { \c_hash_str 1 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_set:Nn
+ \dim_gset:Nn
+ \dim_add:Nn
+ \dim_gadd:Nn
+ \dim_sub:Nn
+ \dim_gsub:Nn
+ \dim_const:Nn
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_set:Nn
+ \int_gset:Nn
+ \int_add:Nn
+ \int_gadd:Nn
+ \int_sub:Nn
+ \int_gsub:Nn
+ \int_const:Nn
+ }
+ { \@@_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_set:Nn
+ \muskip_gset:Nn
+ \muskip_add:Nn
+ \muskip_gadd:Nn
+ \muskip_sub:Nn
+ \muskip_gsub:Nn
+ \muskip_const:Nn
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_set:Nn
+ \skip_gset:Nn
+ \skip_add:Nn
+ \skip_gadd:Nn
+ \skip_sub:Nn
+ \skip_gsub:Nn
+ \skip_const:Nn
+ }
+ { \tex_glueexpr:D { } }
+% \end{macrocode}
+%
+% Patching expandable expressions, first the one-argument versions,
+% then the two-argument ones.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNx \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=box>
+ \__kernel_patch_eval:nn
+ { \@@_dim_eval:n }
+ { \@@_eval:w { } }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_eval:n
+ \dim_to_decimal:n
+ \dim_to_decimal_in_sp:n
+ \dim_abs:n
+ \dim_sign:n
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_eval:n
+ \int_abs:n
+ \int_sign:n
+ }
+ { \@@_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_eval:n
+ \skip_horizontal:n
+ \skip_vertical:n
+ }
+ { \tex_glueexpr:D { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_eval:n
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNx \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_max:nn
+ \dim_min:nn
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_max:nn
+ \int_min:nn
+ \int_div_truncate:nn
+ \int_mod:nn
+ }
+ { \@@_eval:w { } }
+% \end{macrocode}
+%
+% Conditionals: three argument ones then one argument ones
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nNnT , :nNnF , :nNnTF , _p:nNn }
+ {
+ \exp_args:Ncx \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ { \c_hash_str 2 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 3 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_cond:nn { dim_compare } { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_cond:nn { int_compare } { \@@_eval:w { } }
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nT , :nF , :nTF , _p:n }
+ {
+ \exp_args:Ncx \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+%<@@=int>
+ \__kernel_patch_cond:nn { int_if_even } { \@@_eval:w { } }
+ \__kernel_patch_cond:nn { int_if_odd } { \@@_eval:w { } }
+% \end{macrocode}
+%
+% Step functions.
+% \begin{macrocode}
+%<@@=dim>
+ \__kernel_patch:Nn \dim_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ }
+%<@@=int>
+ \__kernel_patch:Nn \int_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ }
+% \end{macrocode}
+%
+% Odds and ends
+% \begin{macrocode}
+ \__kernel_patch:Nn \dim_to_fp:n { { (#1) } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=skip>
+% \end{macrocode}
+% This one has catcode changes so must be done by hand.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \prg_set_conditional:Npnn \skip_if_finite:n ##1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_if_finite:wwNw
+ \skip_use:N \tex_glueexpr:D
+ \__kernel_chk_expr:nNnN
+ {##1} \tex_glueexpr:D { } \skip_if_finite:n
+ ; \prg_return_false:
+ #1 ; \prg_return_true: \q_stop
+ }
+ }
+\exp_args:No \@@_tmp:w { \tl_to_str:n { fil } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% Messages.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { debug }
+ { The~debugging~option~'#1'~does~not~exist~\msg_line_context:. }
+ {
+ The~functions~'\iow_char:N\\debug_on:n'~and~
+ '\iow_char:N\\debug_off:n'~only~accept~the~arguments~
+ 'all',~'check-declarations',~'check-expressions',~
+ 'deprecation',~'log-functions',~not~'#1'.
+ }
+\__kernel_msg_new:nnn { kernel } { expr } { '#2'~in~#1 }
+\__kernel_msg_new:nnnn { kernel } { local-global }
+ { Inconsistent~local/global~assignment }
+ {
+ \c_@@_coding_error_text_tl
+ \if:w l #2 Local
+ \else:
+ \if:w g #2 Global \else: Constant \fi:
+ \fi:
+ \ %
+ assignment~to~a~
+ \if:w l #1 local
+ \else:
+ \if:w g #1 global \else: constant \fi:
+ \fi:
+ \ %
+ variable~'#3'.
+ }
+\__kernel_msg_new:nnnn { kernel } { non-declared-variable }
+ { The~variable~#1~has~not~been~declared~\msg_line_context:. }
+ {
+ \c_@@_coding_error_text_tl
+ Checking~is~active,~and~you~have~tried~do~so~something~like: \\
+ \ \ \tl_set:Nn ~ #1 ~ \{ ~ ... ~ \} \\
+ without~first~having: \\
+ \ \ \tl_new:N ~ #1 \\
+ \\
+ LaTeX~will~create~the~variable~and~continue.
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\__kernel_if_debug:TF}
+% Flip the switch for deprecated code.
+% \begin{macrocode}
+\cs_set_protected:Npn \__kernel_if_debug:TF #1#2 {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex