From cde95caf86c78ec9b7641198abfd0a7768f5100e Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 29 Sep 2019 21:16:44 +0000 Subject: l3 (29sep19) git-svn-id: svn://tug.org/texlive/trunk@52214 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/source/latex/l3kernel/l3debug.dtx | 1447 ++++++++++++++++++++ 1 file changed, 1447 insertions(+) create mode 100644 Master/texmf-dist/source/latex/l3kernel/l3debug.dtx (limited to 'Master/texmf-dist/source/latex/l3kernel/l3debug.dtx') 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} +% +% \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 +% |\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} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex -- cgit v1.2.3