diff options
Diffstat (limited to 'macros/latex/required/l3kernel/l3flag.dtx')
-rw-r--r-- | macros/latex/required/l3kernel/l3flag.dtx | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/macros/latex/required/l3kernel/l3flag.dtx b/macros/latex/required/l3kernel/l3flag.dtx new file mode 100644 index 0000000000..c3bd4a5085 --- /dev/null +++ b/macros/latex/required/l3kernel/l3flag.dtx @@ -0,0 +1,396 @@ +% \iffalse meta-comment +% +%% File: l3flag.dtx +% +% Copyright (C) 2011-2024 The LaTeX 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 \pkg{l3flag} module\\ Expandable flags^^A +% } +% +% \author{^^A +% The \LaTeX{} Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2024-04-11} +% +% \maketitle +% +% \begin{documentation} +% +% Flags are the only data-type that can be modified in expansion-only +% contexts. This module is meant mostly for kernel use: in almost all +% cases, booleans or integers should be preferred to flags because they +% are very significantly faster. +% +% A flag can hold any (small) non-negative value, which we call its +% \meta{height}. In expansion-only contexts, a flag can only be +% \enquote{raised}: this increases the \meta{height} by $1$. The \meta{height} +% can also be queried expandably. However, decreasing it, or setting it +% to zero requires non-expandable assignments. +% +% Flag variables are always local. +% +% A typical use case of flags would be to keep track of whether an +% exceptional condition has occurred during expandable processing, and +% produce a meaningful (non-expandable) message after the end of the +% expandable processing. This is exemplified by \pkg{l3str-convert}, +% which for performance reasons performs conversions of individual +% characters expandably and for readability reasons produces a single +% error message describing incorrect inputs that were encountered. +% +% Flags should not be used without carefully considering the fact that +% raising a flag takes a time and memory proportional to its height and +% that the memory cannot be reclaimed even if the flag is cleared. +% Flags should not be used unless it is unavoidable. +% +% In earlier versions, flags were referenced by an \texttt{n}-type +% \meta{flag name} such as \texttt{fp_overflow}, used as part of +% \cs{use:c} constructions. All of the commands described below have +% \texttt{n}-type analogues that can still appear in old code, but the +% \texttt{N}-type commands are to be preferred moving forward. The +% \texttt{n}-type \meta{flag name} is simply mapped to +% \cs[no-index]{l_\meta{flag name}_flag}, which makes it easier for +% packages using public flags (such as \pkg{l3fp}) to retain backwards +% compatibility. +% +% \section{Setting up flags} +% +% \begin{function}[added = 2024-01-12]{\flag_new:N, \flag_new:c} +% \begin{syntax} +% \cs{flag_new:N} \meta{flag~var} +% \end{syntax} +% Creates a new \meta{flag~var}, or raises an error if the name is +% already taken. The declaration is global, but flags are always local +% variables. The \meta{flag~var} initially has zero height. +% \end{function} +% +% \begin{function}[added = 2024-01-12]{\flag_clear:N, \flag_clear:c} +% \begin{syntax} +% \cs{flag_clear:N} \meta{flag~var} +% \end{syntax} +% Sets the height of the \meta{flag~var} to zero. The assignment is local. +% \end{function} +% +% \begin{function}[added = 2024-01-12]{\flag_clear_new:N, \flag_clear_new:c} +% \begin{syntax} +% \cs{flag_clear_new:N} \meta{flag~var} +% \end{syntax} +% Ensures that the \meta{flag~var} exists globally by applying +% \cs{flag_new:N} if necessary, then applies \cs{flag_clear:N}, setting +% the height to zero locally. +% \end{function} +% +% \begin{function}[added = 2024-01-12]{\flag_show:N, \flag_show:c} +% \begin{syntax} +% \cs{flag_show:N} \meta{flag~var} +% \end{syntax} +% Displays the height of the \meta{flag~var} in the terminal. +% \end{function} +% +% \begin{function}[added = 2024-01-12]{\flag_log:N, \flag_log:c} +% \begin{syntax} +% \cs{flag_log:N} \meta{flag~var} +% \end{syntax} +% Writes the height of the \meta{flag~var} in the log file. +% \end{function} +% +% \section{Expandable flag commands} +% +% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_exist:N, \flag_if_exist:c} +% \begin{syntax} +% \cs{flag_if_exist_p:N} \meta{flag~var} +% \cs{flag_if_exist:NTF} \meta{flag~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% This function returns \texttt{true} if the \meta{flag~var} is +% currently defined, and \texttt{false} otherwise. This does not check +% that the \meta{flag~var} really is a flag variable. +% \end{function} +% +% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_raised:N, \flag_if_raised:c} +% \begin{syntax} +% \cs{flag_if_raised_p:N} \meta{flag~var} +% \cs{flag_if_raised:NTF} \meta{flag~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% This function returns \texttt{true} if the \meta{flag~var} has non-zero +% height, and \texttt{false} if the \meta{flag~var} has zero height. +% \end{function} +% +% \begin{function}[EXP, added = 2024-01-12]{\flag_height:N, \flag_height:c} +% \begin{syntax} +% \cs{flag_height:N} \meta{flag~var} +% \end{syntax} +% Expands to the height of the \meta{flag~var} as an integer denotation. +% \end{function} +% +% \begin{function}[EXP, added = 2024-01-12]{\flag_raise:N, \flag_raise:c} +% \begin{syntax} +% \cs{flag_raise:N} \meta{flag~var} +% \end{syntax} +% The height of \meta{flag~var} is increased by $1$ locally. +% \end{function} +% +% \begin{function}[EXP, added = 2024-01-12]{\flag_ensure_raised:N, \flag_ensure_raised:c} +% \begin{syntax} +% \cs{flag_ensure_raised:N} \meta{flag~var} +% \end{syntax} +% Ensures the \meta{flag~var} is raised by making its height at least~$1$, +% locally. +% \end{function} +% +% \begin{variable}[added = 2024-01-12]{\l_tmpa_flag, \l_tmpb_flag} +% Scratch flag for local assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3flag} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=flag> +% \end{macrocode} +% +% \TestFiles{m3flag001} +% +% \subsection{Protected flag commands} +% +% The height $h$ of a flag (which is initially zero) is stored by +% setting control sequences of the form \cs[no-index]{\meta{flag +% name}\meta{integer}} to \tn{relax} for $0\leq\meta{integer}<h$. These +% control sequences are produced by \cs{cs:w} \meta{flag~var} +% \meta{integer} \cs{cs_end:}, namely the \meta{flag~var} is actually a +% (protected) macro expanding to its own csname. +% +% \begin{macro}{\flag_new:N, \flag_new:c} +% Evaluate the csname of~|#1| for use in constructing the various +% indexed macros. +% \begin{macrocode} +\cs_new_protected:Npn \flag_new:N #1 + { \cs_new_protected:Npe #1 { \cs_to_str:N #1 } } +\cs_generate_variant:Nn \flag_new:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_tmpa_flag, \l_tmpb_flag} +% Two flag variables for scratch use. +% \begin{macrocode} +\flag_new:N \l_tmpa_flag +\flag_new:N \l_tmpb_flag +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\flag_clear:N, \flag_clear:c} +% \begin{macro}{\@@_clear:wN} +% Undefine control sequences, starting from the |0| flag, upwards, +% until reaching an undefined control sequence. We don't use +% \cs{cs_undefine:c} because that would act globally. +% \begin{macrocode} +\cs_new_protected:Npn \flag_clear:N #1 + { + \@@_clear:wN 0 ; #1 + \prg_break_point: + } +\cs_generate_variant:Nn \flag_clear:N { c } +\cs_new_protected:Npn \@@_clear:wN #1 ; #2 + { + \if_cs_exist:w #2 #1 \cs_end: \else: + \prg_break:n + \fi: + \cs_set_eq:cN { #2 #1 } \tex_undefined:D + \exp_after:wN \@@_clear:wN + \int_value:w \int_eval:w \c_one_int + #1 ; #2 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\flag_clear_new:N, \flag_clear_new:c} +% As for other datatypes, clear the \meta{flag~var} or create a new one, +% as appropriate. +% \begin{macrocode} +\cs_new_protected:Npn \flag_clear_new:N #1 + { \flag_if_exist:NTF #1 { \flag_clear:N } { \flag_new:N } #1 } +\cs_generate_variant:Nn \flag_clear_new:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\flag_show:N, \flag_show:c, \flag_log:N, \flag_log:c, \@@_show:NN} +% Show the height (terminal or log file) using appropriate \pkg{l3msg} +% auxiliaries. +% \begin{macrocode} +\cs_new_protected:Npn \flag_show:N { \@@_show:NN \tl_show:n } +\cs_generate_variant:Nn \flag_show:N { c } +\cs_new_protected:Npn \flag_log:N { \@@_show:NN \tl_log:n } +\cs_generate_variant:Nn \flag_log:N { c } +\cs_new_protected:Npn \@@_show:NN #1#2 + { + \__kernel_chk_defined:NT #2 + { \exp_args:Ne #1 { \tl_to_str:n { #2 height } = \flag_height:N #2 } } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Expandable flag commands} +% +% \begin{macro}[EXP, pTF]{\flag_if_exist:N, \flag_if_exist:c} +% Copies of the \texttt{cs} functions defined in \pkg{l3basics}. +% \begin{macrocode} +\prg_new_eq_conditional:NNn \flag_if_exist:N \cs_if_exist:N + { TF , T , F , p } +\prg_new_eq_conditional:NNn \flag_if_exist:c \cs_if_exist:c + { TF , T , F , p } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP, pTF]{\flag_if_raised:N, \flag_if_raised:c} +% Test if the flag has a non-zero height, by checking the |0| control sequence. +% \begin{macrocode} +\prg_new_conditional:Npnn \flag_if_raised:N #1 { p , T , F , TF } + { + \if_cs_exist:w #1 0 \cs_end: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \flag_if_raised:N + { c } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\flag_height:N, \flag_height:c} +% \begin{macro}[EXP]{\@@_height_loop:wN, \@@_height_end:wN} +% Extract the value of the flag by going through all of the +% control sequences starting from |0|. +% \begin{macrocode} +\cs_new:Npn \flag_height:N #1 { \@@_height_loop:wN 0; #1 } +\cs_new:Npn \@@_height_loop:wN #1 ; #2 + { + \if_cs_exist:w #2 #1 \cs_end: \else: + \exp_after:wN \@@_height_end:wN + \fi: + \exp_after:wN \@@_height_loop:wN + \int_value:w \int_eval:w \c_one_int + #1 ; #2 + } +\cs_new:Npn \@@_height_end:wN #1 + #2 ; #3 {#2} +\cs_generate_variant:Nn \flag_height:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\flag_raise:N, \flag_raise:c} +% Change the appropriate control sequence to \tn{relax} by expanding a +% \cs{cs:w} \ldots{} \cs{cs_end:} construction, then pass it to +% \cs{use_none:n} to avoid leaving anything in the input stream. +% \begin{macrocode} +\cs_new:Npn \flag_raise:N #1 + { \exp_after:wN \use_none:n \cs:w #1 \flag_height:N #1 \cs_end: } +\cs_generate_variant:Nn \flag_raise:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\flag_ensure_raised:N, \flag_ensure_raised:c} +% Pass the control sequence with name \meta{flag name}\texttt{0} to +% \cs{use_none:n}. Constructing the control sequence ensures that it +% changes from being undefined (if it was so) to being \tn{relax}. +% \begin{macrocode} +\cs_new:Npn \flag_ensure_raised:N #1 + { \exp_after:wN \use_none:n \cs:w #1 0 \cs_end: } +\cs_generate_variant:Nn \flag_ensure_raised:N { c } +% \end{macrocode} +% \end{macro} +% +% \subsection{Old \texttt{n}-type flag commands} +% +% Here we keep the old flag commands since our policy is to no longer +% delete deprecated functions. The idea is to simply map \meta{flag +% name} to \cs[no-index]{l_\meta{flag name}_flag}. When the debugging +% code is activated, it checks existence of the \texttt{N}-type flag +% variables that result. +% +% \begin{macro}[no-user-doc]{\flag_new:n, \flag_clear:n, \flag_clear_new:n} +% \begin{macro}[EXP, pTF, no-user-doc]{\flag_if_exist:n, \flag_if_raised:n} +% \begin{macro}[EXP, no-user-doc]{\flag_height:n, \flag_raise:n, \flag_ensure_raised:n} +% \begin{macrocode} +\cs_new_protected:Npn \flag_new:n #1 { \flag_new:c { l_#1_flag } } +\cs_new_protected:Npn \flag_clear:n #1 { \flag_clear:c { l_#1_flag } } +\cs_new_protected:Npn \flag_clear_new:n #1 { \flag_clear_new:c { l_#1_flag } } +\cs_new:Npn \flag_if_exist_p:n #1 { \flag_if_exist_p:c { l_#1_flag } } +\cs_new:Npn \flag_if_exist:nT #1 { \flag_if_exist:cT { l_#1_flag } } +\cs_new:Npn \flag_if_exist:nF #1 { \flag_if_exist:cF { l_#1_flag } } +\cs_new:Npn \flag_if_exist:nTF #1 { \flag_if_exist:cTF { l_#1_flag } } +\cs_new:Npn \flag_if_raised_p:n #1 { \flag_if_raised_p:c { l_#1_flag } } +\cs_new:Npn \flag_if_raised:nT #1 { \flag_if_raised:cT { l_#1_flag } } +\cs_new:Npn \flag_if_raised:nF #1 { \flag_if_raised:cF { l_#1_flag } } +\cs_new:Npn \flag_if_raised:nTF #1 { \flag_if_raised:cTF { l_#1_flag } } +\cs_new:Npn \flag_height:n #1 { \flag_height:c { l_#1_flag } } +\cs_new:Npn \flag_raise:n #1 { \flag_raise:c { l_#1_flag } } +\cs_new:Npn \flag_ensure_raised:n #1 { \flag_ensure_raised:c { l_#1_flag } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[no-user-doc]{\flag_show:n, \flag_log:n, \@@_show:Nn} +% To avoid changing the output here we mostly keep the old code. +% \begin{macrocode} +\cs_new_protected:Npn \flag_show:n { \@@_show:Nn \tl_show:n } +\cs_new_protected:Npn \flag_log:n { \@@_show:Nn \tl_log:n } +\cs_new_protected:Npn \@@_show:Nn #1#2 + { + \exp_args:Nc \__kernel_chk_defined:NT { l_#2_flag } + { + \exp_args:Ne #1 + { \tl_to_str:n { flag~#2~height } = \flag_height:n {#2} } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |