% \iffalse %% File: l3chk.dtx Copyright (C) 1990-1998 LaTeX3 project % %<*dtx> \ProvidesFile{l3chk.dtx} % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{l3chk} % \ProvidesFile{l3chk.drv} % \fi % \ProvidesFile{l3chk.dtx} [1998/04/20 v1.0e L3 Experimental check module] % % \iffalse %<*driver> \documentclass{l3doc} \begin{document} \DocInput{l3chk.dtx} \end{document} % % \fi % % \GetFileInfo{l3chk.dtx} % \title{The \textsf{l3chk} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Checking things\ldots} % \author{\Team} % \date{\filedate} % \maketitle % % \section{The chk module} % % To ensure that functions and variables are properly used certain % checking functions are implemented that may or may not be compiled % into the final format. % % \subsection{Functions} % % \begin{function}{% % \chk_var_or_const:N | % } % \begin{syntax} % "\chk_var_or_const:N" % \end{syntax} % Checks that is a proper variable or constant which means that its % name starts out with "\L", "\l", "\G", "\g", "\R", "\C", "\c", or "\q". % \end{function} % % \begin{function}{% % \chk_local:N | % \chk_global:N | % } % \begin{syntax} % "\chk_local:N" % \end{syntax} % Checks that is a proper local or global variable. This means that % its name starts out with "\L", "\l", or "\G", "\g" respectively. % \end{function} % % \begin{function}{% % \chk_local_or_pref_global:N | % \pref_global_chk: | % } % To allow implementations where we precede some function with % "\pref_global:D" without loosing the possibility to check for the % correct variable type the following helper functions can be used: % "\chk_local_or_pref_global:N" is the variable check which is % usually let to "\chk_local:N", i.e.\ it will check that its argument % is a local variable. This behavior will be changed by % "\pref_global_chk:". This function first changes % "\chk_local_or_pref_global:N" to check for global variables then it % issues a "\pref_global:D". After use "\chk_local_or_pref_global:N" % will restore itself to "\chk_local:N". So, if we use % "\chk_local_or_pref_global:N" inside some function "\foo_bar:n" we can % implement a global version "\foo_gbar:n" by defining % \begin{quote} % "\def_new:Npn \foo_gbar:n {\pref_global_chk: \foo_bar:n }" % \end{quote} % provided that "\foo_bar:n" is built in a way that prefixing it with % "\pref_global:D" turns its operation into a global one. See % implementation for details. % \end{function} % % \subsection{Constants} % % \begin{variable}{\c_undefined} % This constant is always undefined and therefore can be used to check % for free function names. % \end{variable} % % \subsection{Internal functions} % % \begin{function}{% % \chk_global_aux:w | % \chk_local_aux:w | % \chk_var_or_const_aux:w | % } % Helper functions that implement the checking. % \end{function} % % \StopEventually{} % % \section{The code} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %\RequirePackage{l3basics} %\RequirePackage{l3int} %<*package> % \end{macrocode} % % The |\chk| module contains those functions that are used primarily % during the development of \LaTeX3 for checking that things are not mixed up % too badly. All these functions are of type `{\tt e}' since they % issue error messages if certain conditions are violated. % % \subsection{Checking variable assignments} % % \begin{macro}{\chk_local:N} % \begin{macro}{\chk_local_aux:w} % This function checks that its argument is a proper local % variable, i.e.\ its name starts with |\l| or |\L|. % It is not allowed that the name starts with |\g| or |\G|, % which means that we do not allow to update global % variables locally. But see |\pref_global_chk:| below for % the encoding of functions that might accept global variables % in certain situations. Not checked is the case that the % argument isn't a variable at all, i.e.\ it doesn't have % a |_| as second letter. Maybe we should add this for safety % during the implementation since it will find certain errors % involving wrong expansion in earlier stage. % \begin{macrocode} \def_new:Npn \chk_local:N #1{\exp_after:NN \chk_local_aux:w \token_to_string:N#1\q_stop} \def_new:Npn \chk_local_aux:w #1#2#3\q_stop{ \if_num:w\tex_uccode:D`#2=`G\scan_stop: \err_latex_bug:n{Local~mismatch:~local~function~called~with~ global~variable:^^J\text_put_four_sp: #1#2#3~ on~line~\tex_the:D\tex_inputlineno:D} \else: \if_num:w\tex_uccode:D`#2=`L\scan_stop: \else: \err_latex_bug:n{Variable~mismatch:~function~not~called~with~ proper~variable:^^J\text_put_four_sp: #1#2#3~ on~line~\tex_the:D\tex_inputlineno:D}\fi: \fi:} % \end{macrocode} % We set the |\l_iow_new_line_code| at this point, just in case we % run into errors. % \begin{macrocode} \tex_newlinechar:D=`\^^J % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\chk_global:N} % \begin{macro}{\chk_global_aux:w} % |\chk_global:N| is similar to |\chk_local:N| but looks only % for global variables. % \begin{macrocode} \def_new:Npn \chk_global:N #1{\exp_after:NN \chk_global_aux:w \token_to_string:N#1\q_stop} \def_new:Npn \chk_global_aux:w #1#2#3\q_stop{ \if_num:w\tex_uccode:D`#2=`L\scan_stop: \err_latex_bug:n{Global~mismatch:~global~function~called~with~ local~variable:~#1#2#3~ on~line~\tex_the:D\tex_inputlineno:D} \else: \if_num:w\tex_uccode:D`#2=`G\scan_stop: \else: \err_latex_bug:n{Variable~mismatch:~function~not~called~with~ proper~variable:~#1#2#3~ on~line~\tex_the:D\tex_inputlineno:D}\fi:\fi:} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\pref_global_chk:} % \begin{macro}{\chk_local_or_pref_global:N} % To allow implementations where we precede some function with % |\pref_global:D| without loosing the possibility to check for the % correct type of a variable, the following helper functions can be % used: |\chk_local_or_pref_global:N| \m{variable} is the variable % check which is usually |\let:NN| to |\chk_local:N|, i.e.\ it will % check for local variables. This behavior will be changed by % |\pref_global_chk:|. This function first changes % |\chk_local_or_pref_global:N| to check for global variables, then % issues a |\pref_global:D|. After being used, % |\chk_local_or_pref_global:N| will restore itself to % |\chk_local:N|. So, if we use |\chk_local_or_pref_global:N| % inside some function |\foo_bar:n| we can implement a global % version |\foo_gbar:n| by defining % \begin{verbatim} % \def_new:Npn \foo_gbar:n {\pref_global_chk: \foo_bar:n } %\end{verbatim} % provided of course, that |\foo_bar:n| is defined in a way that a % |\pref_global:D| does work. Such a scheme has to be used % carefully, but its advantage is that the checking version has the % same structure as a streamlined version, we only have to change % |\pref_global_chk:| into |\pref_global:D| and omit the % |\chk_local_or_pref_global:N| function in the body. % \begin{macrocode} \def_new:Npn \pref_global_chk: { \gdef:Npn \chk_local_or_pref_global:N ##1{ \chk_global:N ##1 \glet:NN \chk_local_or_pref_global:N \chk_local:N} \pref_global:D} \let_new:NN \chk_local_or_pref_global:N \chk_local:N % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\chk_var_or_const:N} % \begin{macro}{\chk_var_or_const_aux:w} % |\chk_var_or_const:N| is used in situations where we want to check % that we have a variable (or a constant) but do not care whether % or not it is global (for example, in allocation routines). % \begin{macrocode} \def_new:Npn \chk_var_or_const:N #1{\exp_after:NN \chk_var_or_const_aux:w \token_to_string:N#1\q_stop } \def_new:Npn \chk_var_or_const_aux:w #1#2#3\q_stop { \if_num:w\tex_uccode:D`#2=`L\scan_stop: \else: \if_num:w\tex_uccode:D`#2=`G\scan_stop: \else: \if_num:w\tex_uccode:D`#2=`C\scan_stop: \else: % \end{macrocode} % We also allow the beast to be a quark, i.e.\ to start with |\q|. % \begin{macrocode} \if_char_code:w#2q\scan_stop: \else: % \end{macrocode} % We might also want to allow that it is a user definable % variable which means that its name consists of letters only. We % could check this by testing that there is no |_|, but this is not % implemented so far. % \begin{macrocode} \err_latex_bug:n{Variable~mismatch:~function~not~called~with~ proper~variable:^^J\text_put_four_sp: #1#2#3~ on~line~\tex_the:D\tex_inputlineno:D}\fi:\fi:\fi: \fi:} % \end{macrocode} % \end{macro} % \end{macro} % % % \subsection{Doing some tracing} % % \begin{macro}{\tracingall} % \begin{macro}{\absolutelytracingall} % \begin{macro}{\donotcheck} % During |\tracingall| we don't want to see all this code coming % from the checking functions since something more substantial is % probably wrong. Therefore this definition of it turns the % functions of as far as possible. % \begin{macrocode} \def_new:Npn\donotcheck{ \let:NN \chk_global:N \use_none:n \let:NN \chk_local:N \use_none:n \let:NN \chk_local_or_pref_global:N \use_none:n \let:NN \pref_global_chk: \pref_global:D \let:NN \chk_new_cs:N \use_none:n \let:NN \chk_exist_cs:N \use_none:n \let:NN \chk_var_or_const:N \use_none:n \let:NN \cs_record_name:N \use_none:n \let:NN \cs_record_name:c \use_none:n \let:NN \cs_record_meaning:N \use_none:n \let:NN \register_record_name:N \use_none:n } \def_new:Npn\absolutelytracingall{ % \end{macrocode} % \end{macro} % % We do the settings by hand to avoid uninteresting lines in the % log file as much as possible. % \begin{macrocode} \pref_global:D\g_trace_commands_status\c_two \pref_global:D\g_trace_statistics_status\c_two \pref_global:D\g_trace_pages_status\c_one \pref_global:D\g_trace_output_status\c_one \pref_global:D\g_trace_chars_status\c_one \pref_global:D\g_trace_macros_status\c_two \pref_global:D\g_trace_paragraphs_status\c_one \pref_global:D\g_trace_restores_status\c_one \pref_global:D\g_trace_box_breadth_int\c_ten_thousand \pref_global:D\g_trace_box_depth_int\c_ten_thousand \pref_global:D\g_trace_online_status\c_one \mode_errorstop:D} % % Use LaTeX2e definition for now. %\def_new:Npn\tracingall{ % \donotcheck % \absolutelytracingall %} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tracingoff} % This macro turns off all tracing. % \begin{macrocode} \def_new:Npn\tracingoff{ % \end{macrocode} % First we turn off |\g_trace_online_status| so that we get minimal % rubbish on the terminal. Of course, in the log file all % assignments are shown. % \begin{macrocode} \pref_global:D\g_trace_online_status\c_zero \pref_global:D\g_trace_commands_status\c_zero \pref_global:D\g_trace_statistics_status\c_zero \pref_global:D\g_trace_pages_status\c_zero \pref_global:D\g_trace_output_status\c_zero \pref_global:D\g_trace_chars_status\c_zero \pref_global:D\g_trace_macros_status\c_zero \pref_global:D\g_trace_paragraphs_status\c_zero \pref_global:D\g_trace_restores_status\c_zero \pref_global:D\g_trace_box_breadth_int\c_zero \pref_global:D\g_trace_box_depth_int\c_zero } % \end{macrocode} % \end{macro} % % \subsection{Tracing modules} % % \begin{macro}{\traceon} % \begin{macro}{\traceoff} % Turning the tracing of modules on or off. (primitive version). % \begin{macrocode} %<*trace> \def_new:Npn\traceon#1{\for_clist:Nnn\l_tmpa_tlp{#1} { \cs_free:cF{g_trace_\l_tmpa_tlp _status} {\int_gincr:c{g_trace_\l_tmpa_tlp _status}} }} \def_new:Npn\traceoff#1{\for_clist:Nnn\l_tmpa_tlp{#1} { \cs_free:cF{g_trace_\l_tmpa_tlp _status} {\int_gdecr:c{g_trace_\l_tmpa_tlp _status}} }} % %<-trace>\let_new:NN\traceon\use_none:n %<-trace>\let_new:NN\traceoff\use_none:n % % \end{macrocode} % \end{macro} % \end{macro} % % % Show token usage: % \begin{macrocode} %<*showmemory> \showMemUsage % % \end{macrocode} %