% \iffalse meta-comment % %% File: l3prg.dtx % % Copyright (C) 2005-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} % % \fi % % \title{^^A % The \pkg{l3prg} module\\ Control structures^^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-03-14} % % \maketitle % % \begin{documentation} % % Conditional processing in \LaTeX3 is defined as something that % performs a series of tests, possibly involving assignments and % calling other functions that do not read further ahead in the input % stream. After processing the input, a \emph{state} is returned. The % states returned are \meta{true} and \meta{false}. % % \LaTeX3 has two forms of conditional flow processing based % on these states. The first form is predicate functions that turn the % returned state into a boolean \meta{true} or \meta{false}. For % example, the function \cs{cs_if_free_p:N} checks whether the control % sequence given as its argument is free and then returns the boolean % \meta{true} or \meta{false} values to be used in testing with % \cs{if_predicate:w} or in functions to be described below. The second form % is the kind of functions choosing a particular argument from the % input stream based on the result of the testing as in % \cs{cs_if_free:NTF} which also takes one argument (the |N|) and then % executes either \texttt{true} or \texttt{false} depending on the % result. % % \begin{texnote} % The arguments are executed after exiting the underlying % |\if...\fi:| structure. % \end{texnote} % % \section{Defining a set of conditional functions} % \label{sec:l3prg:new-conditional-functions} % % \begin{function}[updated = 2022-11-01] % { % \prg_new_conditional:Npnn, \prg_set_conditional:Npnn, \prg_gset_conditional:Npnn, % \prg_new_conditional:Nnn, \prg_set_conditional:Nnn, \prg_gset_conditional:Nnn % } % \begin{syntax} % \cs{prg_new_conditional:Npnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \meta{parameters} \Arg{conditions} \Arg{code} \\ % \cs{prg_new_conditional:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{conditions} \Arg{code} % \end{syntax} % These functions create a family of conditionals using the same % \Arg{code} to perform the test created. Those conditionals are % expandable if \meta{code} is. The \texttt{new} versions check % for existing definitions and perform assignments globally % (\emph{cf.}~\cs{cs_new:Npn}) whereas the \texttt{set} versions do no % check and perform assignments locally (\emph{cf.}~\cs{cs_set:Npn}). % The conditionals created are dependent on the comma-separated list % of \meta{conditions}, which should be one or more of \texttt{p}, % \texttt{T}, \texttt{F} and \texttt{TF}. % \end{function} % % \begin{function}[updated = 2012-02-06] % { % \prg_new_protected_conditional:Npnn, \prg_set_protected_conditional:Npnn, \prg_gset_protected_conditional:Npnn, % \prg_new_protected_conditional:Nnn, \prg_set_protected_conditional:Nnn, \prg_gset_protected_conditional:Nnn % } % \begin{syntax} % \cs{prg_new_protected_conditional:Npnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \meta{parameters} \Arg{conditions} \Arg{code} \\ % \cs{prg_new_protected_conditional:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{conditions} \Arg{code} % \end{syntax} % These functions create a family of protected conditionals using the % same \Arg{code} to perform the test created. The \meta{code} does % not need to be expandable. The \texttt{new} version check for % existing definitions and perform assignments globally % (\emph{cf.}~\cs{cs_new:Npn}) whereas the \texttt{set} version do % not (\emph{cf.}~\cs{cs_set:Npn}). The conditionals created are % depended on the comma-separated list of \meta{conditions}, which % should be one or more of \texttt{T}, \texttt{F} and \texttt{TF} (not % \texttt{p}). % \end{function} % % The conditionals are defined by \cs{prg_new_conditional:Npnn} and % friends as: % \begin{itemize} % \item \cs[no-index]{\meta{name}_p:\meta{arg spec}} --- a predicate function % which will supply either a logical \texttt{true} or logical % \texttt{false}. This function is intended for use in cases where % one or more logical tests are combined to lead to a final outcome. % This function cannot be defined for \texttt{protected} % conditionals. % \item \cs[no-index]{\meta{name}:\meta{arg spec}T} --- a function with one more % argument than the original \meta{arg spec} demands. The \meta{true % branch} code in this additional argument will be left on the % input stream only if the test is \texttt{true}. % \item \cs[no-index]{\meta{name}:\meta{arg spec}F} --- a function with one more % argument than the original \meta{arg spec} demands. The % \meta{false branch} code in this additional argument will be left % on the input stream only if the test is \texttt{false}. % \item \cs[no-index]{\meta{name}:\meta{arg spec}TF} --- a function with two % more argument than the original \meta{arg spec} demands. The % \meta{true branch} code in the first additional argument will be % left on the input stream if the test is \texttt{true}, while the % \meta{false branch} code in the second argument will be left on % the input stream if the test is \texttt{false}. % \end{itemize} % The \meta{code} of the test may use \meta{parameters} as specified by % the second argument to \cs{prg_set_conditional:Npnn}: this should % match the \meta{argument specification} but this is not enforced. The % |Nnn| versions infer the number of arguments from the argument % specification given (\emph{cf.}~\cs{cs_new:Nn}, \emph{etc.}). Within % the \meta{code}, the functions \cs{prg_return_true:} and % \cs{prg_return_false:} are used to indicate the logical outcomes of % the test. % % An example can easily clarify matters here: % \begin{verbatim} % \prg_set_conditional:Npnn \foo_if_bar:NN #1#2 { p , T , TF } % { % \if_meaning:w \l_tmpa_tl #1 % \prg_return_true: % \else: % \if_meaning:w \l_tmpa_tl #2 % \prg_return_true: % \else: % \prg_return_false: % \fi: % \fi: % } % \end{verbatim} % This defines the function |\foo_if_bar_p:NN|, |\foo_if_bar:NNTF| and % |\foo_if_bar:NNT| but not |\foo_if_bar:NNF| (because |F| is missing % from the \meta{conditions} list). The return statements take care of % resolving the remaining \cs{else:} and \cs{fi:} before returning the % state. There must be a return statement for each branch; failing to do % so will result in erroneous output if that branch is executed. % % The special case where the code of a conditional ends with % \cs{prg_return_true:} \cs{else:} \cs{prg_return_false:} \cs{fi:} is % optimized. % % \begin{function}[updated = 2023-05-26] % { % \prg_new_eq_conditional:NNn, % \prg_set_eq_conditional:NNn, % \prg_gset_eq_conditional:NNn % } % \begin{syntax} % \cs{prg_new_eq_conditional:NNn} \cs[no-index]{\meta{name_1}:\meta{arg spec_1}} \cs[no-index]{\meta{name_2}:\meta{arg spec_2}} \Arg{conditions} % \end{syntax} % These functions copy a family of conditionals. The \texttt{new} version % checks for existing definitions (\emph{cf.}~\cs{cs_new_eq:NN}) whereas % the \texttt{set} version does not (\emph{cf.}~\cs{cs_set_eq:NN}). The % conditionals copied are depended on the comma-separated list of % \meta{conditions}, which should be one or more of \texttt{p}, \texttt{T}, % \texttt{F} and \texttt{TF}. % \end{function} % % \begin{function}[EXP]{\prg_return_true:, \prg_return_false:} % \begin{syntax} % \cs{prg_return_true:} % \cs{prg_return_false:} % \end{syntax} % These \enquote{return} functions define the logical state of a conditional statement. % They appear within the code for a conditional % function generated by \cs{prg_set_conditional:Npnn}, \emph{etc}, to indicate % when a true or false branch should be taken. % While they may appear multiple times each within the code of such conditionals, % the execution of the conditional must result in the expansion of one of these % two functions \emph{exactly once}. % % The return functions trigger what is internally an \texttt{f}-expansion process to complete % the evaluation of the conditional. Therefore, after \cs{prg_return_true:} or \cs{prg_return_false:} % there must be no non-expandable material in the input stream for the remainder of % the expansion of the conditional code. This includes other instances of either of these functions. % \end{function} % % \begin{function}[added = 2017-12-12]{\prg_generate_conditional_variant:Nnn} % \begin{syntax} % \cs{prg_generate_conditional_variant:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{variant argument specifiers} \Arg{condition specifiers} % \end{syntax} % Defines argument-specifier variants of conditionals. This is % equivalent to running \cs{cs_generate_variant:Nn} \meta{conditional} % \Arg{variant argument specifiers} on each \meta{conditional} % described by the \meta{condition specifiers}. These base-form % \meta{conditionals} are obtained from the \meta{name} and \meta{arg % spec} as described for \cs{prg_new_conditional:Npnn}, and they % should be defined. % \end{function} % % \section{The boolean data type} % % This section describes a boolean data type which is closely % connected to conditional processing as sometimes you want to % execute some code depending on the value of a switch % (\emph{e.g.},~draft/final) and other times you perhaps want to use it as a % predicate function in an \cs{if_predicate:w} test. The problem of the % primitive \cs{if_false:} and \cs{if_true:} tokens is that it is not % always safe to pass them around as they may interfere with scanning % for termination of primitive conditional processing. Therefore, we % employ two canonical booleans: \cs{c_true_bool} or % \cs{c_false_bool}. Besides preventing problems as described above, it % also allows us to implement a simple boolean parser supporting the % logical operations And, Or, Not, \emph{etc.}\ which can then be used on % both the boolean type and predicate functions. % % All conditional |\bool_| functions except assignments are expandable % and expect the input to also be fully expandable (which generally % means being constructed from predicate functions and booleans, possibly nested). % % \begin{texnote} % The \texttt{bool} data type is not implemented using the % \tn{iffalse}/\tn{iftrue} primitives, in contrast to \tn{newif}, % \emph{etc.}, in plain \TeX{}, \LaTeXe{} and so on. Programmers should % not base use of \texttt{bool} switches on any particular expectation % of the implementation. % \end{texnote} % % \begin{function}{\bool_new:N, \bool_new:c} % \begin{syntax} % \cs{bool_new:N} \meta{boolean} % \end{syntax} % Creates a new \meta{boolean} or raises an error if the % name is already taken. The declaration is global. The % \meta{boolean} is initially \texttt{false}. % \end{function} % % \begin{function}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn} % \begin{syntax} % \cs{bool_const:Nn} \meta{boolean} \Arg{boolexpr} % \end{syntax} % Creates a new constant \meta{boolean} or raises an error if the name % is already taken. The value of the \meta{boolean} is set globally to % the result of evaluating the \meta{boolexpr}. % \end{function} % % \begin{function} % { % \bool_set_false:N , \bool_set_false:c , % \bool_gset_false:N, \bool_gset_false:c % } % \begin{syntax} % \cs{bool_set_false:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{false}. % \end{function} % % \begin{function} % { % \bool_set_true:N , \bool_set_true:c , % \bool_gset_true:N , \bool_gset_true:c % } % \begin{syntax} % \cs{bool_set_true:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{true}. % \end{function} % % \begin{function} % { % \bool_set_eq:NN , \bool_set_eq:cN , \bool_set_eq:Nc , \bool_set_eq:cc , % \bool_gset_eq:NN, \bool_gset_eq:cN, \bool_gset_eq:Nc, \bool_gset_eq:cc % } % \begin{syntax} % \cs{bool_set_eq:NN} \meta{boolean_1} \meta{boolean_2} % \end{syntax} % Sets \meta{boolean_1} to the current value of \meta{boolean_2}. % \end{function} % % \begin{function}[updated = 2017-07-15] % {\bool_set:Nn, \bool_set:cn, \bool_gset:Nn, \bool_gset:cn} % \begin{syntax} % \cs{bool_set:Nn} \meta{boolean} \Arg{boolexpr} % \end{syntax} % Evaluates the \meta{boolean expression} as described for % \cs{bool_if:nTF}, and sets the \meta{boolean} variable to % the logical truth of this evaluation. % \end{function} % % \begin{function}[added = 2018-05-10] % { % \bool_set_inverse:N , \bool_set_inverse:c , % \bool_gset_inverse:N, \bool_gset_inverse:c % } % \begin{syntax} % \cs{bool_set_inverse:N} \meta{boolean} % \end{syntax} % Toggles the \meta{boolean} from \texttt{true} to \texttt{false} and % conversely: sets it to the inverse of its current value. % \end{function} % % \begin{function}[EXP,pTF, updated = 2017-07-15]{\bool_if:N, \bool_if:c} % \begin{syntax} % \cs{bool_if_p:N} \meta{boolean} % \cs{bool_if:NTF} \meta{boolean} \Arg{true code} \Arg{false code} % \end{syntax} % Tests the current truth of \meta{boolean}, and continues expansion % based on this result. % \end{function} % % \begin{function}[EXP, added = 2021-11-01, updated = 2023-11-14] % {\bool_to_str:N, \bool_to_str:c, \bool_to_str:n} % \begin{syntax} % \cs{bool_to_str:N} \meta{boolean} % \cs{bool_to_str:n} \meta{boolean expression} % \end{syntax} % Expands to the string \texttt{true} or \texttt{false} depending on % the logical truth of the \meta{boolean} or \meta{boolean % expression}. % \end{function} % % \begin{function}[added = 2012-02-09, updated = 2021-04-29]{\bool_show:N, \bool_show:c} % \begin{syntax} % \cs{bool_show:N} \meta{boolean} % \end{syntax} % Displays the logical truth of the \meta{boolean} on the terminal. % \end{function} % % \begin{function}[added = 2012-02-09, updated = 2017-07-15]{\bool_show:n} % \begin{syntax} % \cs{bool_show:n} \Arg{boolean expression} % \end{syntax} % Displays the logical truth of the \meta{boolean expression} on the % terminal. % \end{function} % % \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\bool_log:N, \bool_log:c} % \begin{syntax} % \cs{bool_log:N} \meta{boolean} % \end{syntax} % Writes the logical truth of the \meta{boolean} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22, updated = 2017-07-15]{\bool_log:n} % \begin{syntax} % \cs{bool_log:n} \Arg{boolean expression} % \end{syntax} % Writes the logical truth of the \meta{boolean expression} in the log % file. % \end{function} % % \begin{function}[EXP, pTF, added=2012-03-03] % {\bool_if_exist:N, \bool_if_exist:c} % \begin{syntax} % \cs{bool_if_exist_p:N} \meta{boolean} % \cs{bool_if_exist:NTF} \meta{boolean} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{boolean} is currently defined. This does not % check that the \meta{boolean} really is a boolean variable. % \end{function} % % \subsection{Constant and scratch booleans} % % \begin{variable}{\c_true_bool, \c_false_bool} % Constants that represent |true| and |false|, respectively. Used to % implement predicates. % \end{variable} % % \begin{variable}{\l_tmpa_bool, \l_tmpb_bool} % A scratch boolean for local assignment. It is never used by % the kernel code, and so is safe for use with any \LaTeX3-defined % function. However, it may be overwritten by other non-kernel % code and so should only be used for short-term storage. % \end{variable} % % \begin{variable}{\g_tmpa_bool, \g_tmpb_bool} % A scratch boolean for global assignment. It is never used by % the kernel code, and so is safe for use with any \LaTeX3-defined % function. However, it may be overwritten by other non-kernel % code and so should only be used for short-term storage. % \end{variable} % % \section{Boolean expressions} % % As we have a boolean datatype and predicate functions returning % boolean \meta{true} or \meta{false} values, it seems only fitting % that we also provide a parser for \meta{boolean expressions}. % % A boolean expression is an expression which given input in the form % of predicate functions and boolean variables, return boolean % \meta{true} or \meta{false}. It supports the logical operations And, % Or and Not as the well-known infix operators |&&| and \verb"||" and prefix~|!| % with their usual precedences (namely, |&&| binds more tightly than % \verb"||"). In addition to this, parentheses can be used to isolate % sub-expressions. For example, % \begin{verbatim} % \int_compare_p:n { 1 = 1 } && % ( % \int_compare_p:n { 2 = 3 } || % \int_compare_p:n { 4 <= 4 } || % \str_if_eq_p:nn { abc } { def } % ) && % ! \int_compare_p:n { 2 = 4 } % \end{verbatim} % is a valid boolean expression. % % Contrarily to some other programming languages, the operators |&&| and % \verb"||" evaluate both operands in all cases, even when the first % operand is enough to determine the result. This \enquote{eager} % evaluation should be contrasted with the \enquote{lazy} evaluation of % \cs[no-index]{bool_lazy_\ldots{}} functions. % % \begin{texnote} % The eager evaluation of boolean expressions is unfortunately % necessary in \TeX{}. Indeed, a lazy parser can get confused if |&&| % or \verb"||" or parentheses appear as (unbraced) arguments of some % predicates. For instance, the innocuous-looking expression below % would break (in a lazy parser) if |#1| were a closing parenthesis % and \cs[no-index]{l_tmpa_bool} were \texttt{true}. % \begin{verbatim} % ( \l_tmpa_bool || \token_if_eq_meaning_p:NN X #1 ) % \end{verbatim} % \end{texnote} % % Minimal (lazy) evaluation can be obtained using the conditionals % \cs{bool_lazy_all:nTF}, \cs{bool_lazy_and:nnTF}, \cs{bool_lazy_any:nTF}, or % \cs{bool_lazy_or:nnTF}, which only evaluate their boolean expression % arguments when they are needed to determine the resulting truth % value. For example, when evaluating the boolean expression % \begin{verbatim} % \bool_lazy_and_p:nn % { % \bool_lazy_any_p:n % { % { \int_compare_p:n { 2 = 3 } } % { \int_compare_p:n { 4 <= 4 } } % { \int_compare_p:n { 1 = \error } } % skipped % } % } % { ! \int_compare_p:n { 2 = 4 } } % \end{verbatim} % the line marked with |skipped| is not expanded because the result % of \cs{bool_lazy_any_p:n} is known once the second boolean expression is % found to be logically \texttt{true}. On the other hand, the last % line is expanded because its logical value is needed to determine the % result of \cs{bool_lazy_and_p:nn}. % % \begin{function}[EXP, pTF, updated = 2017-07-15]{\bool_if:n} % \begin{syntax} % \cs{bool_if_p:n} \Arg{boolean expression} % \cs{bool_if:nTF} \Arg{boolean expression} \Arg{true code} \Arg{false code} % \end{syntax} % Tests the current truth of \meta{boolean expression}, and % continues expansion based on this result. The % \meta{boolean expression} should consist of a series of predicates % or boolean variables with the logical relationship between these % defined using |&&| (\enquote{And}), \verb"||" (\enquote{Or}), % |!| (\enquote{Not}) and parentheses. The logical Not applies to % the next predicate or group. % \end{function} % % \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_all:n} % \begin{syntax} % \cs{bool_lazy_all_p:n} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} % \cs{bool_lazy_all:nTF} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} \Arg{true code} \Arg{false code} % \end{syntax} % Implements the \enquote{And} operation on the \meta{boolean % expressions}, hence is \texttt{true} if all of them are % \texttt{true} and \texttt{false} if any of them is \texttt{false}. % Contrarily to the infix operator |&&|, only the \meta{boolean % expressions} which are needed to determine the result of % \cs{bool_lazy_all:nTF} are evaluated. See also \cs{bool_lazy_and:nnTF} % when there are only two \meta{boolean expressions}. % \end{function} % % \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_and:nn} % \begin{syntax} % \cs{bool_lazy_and_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2} % \cs{bool_lazy_and:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code} % \end{syntax} % Implements the \enquote{And} operation between two boolean % expressions, hence is \texttt{true} if both are \texttt{true}. % Contrarily to the infix operator |&&|, the \meta{boolexpr_2} is only % evaluated if it is needed to determine the result of % \cs{bool_lazy_and:nnTF}. See also \cs{bool_lazy_all:nTF} when there are more % than two \meta{boolean expressions}. % \end{function} % % \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_any:n} % \begin{syntax} % \cs{bool_lazy_any_p:n} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} % \cs{bool_lazy_any:nTF} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} \Arg{true code} \Arg{false code} % \end{syntax} % Implements the \enquote{Or} operation on the \meta{boolean % expressions}, hence is \texttt{true} if any of them is % \texttt{true} and \texttt{false} if all of them are \texttt{false}. % Contrarily to the infix operator \verb"||", only the \meta{boolean % expressions} which are needed to determine the result of % \cs{bool_lazy_any:nTF} are evaluated. See also \cs{bool_lazy_or:nnTF} % when there are only two \meta{boolean expressions}. % \end{function} % % \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_or:nn} % \begin{syntax} % \cs{bool_lazy_or_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2} % \cs{bool_lazy_or:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code} % \end{syntax} % Implements the \enquote{Or} operation between two boolean % expressions, hence is \texttt{true} if either one is \texttt{true}. % Contrarily to the infix operator \verb"||", the \meta{boolexpr_2} % is only evaluated if it is needed to determine the result of % \cs{bool_lazy_or:nnTF}. See also \cs{bool_lazy_any:nTF} when there are more % than two \meta{boolean expressions}. % \end{function} % % \begin{function}[EXP, updated = 2017-07-15]{\bool_not_p:n} % \begin{syntax} % \cs{bool_not_p:n} \Arg{boolean expression} % \end{syntax} % Function version of |!(|\meta{boolean expression}|)| within a boolean % expression. % \end{function} % % \begin{function}[EXP, pTF, added = 2018-05-09]{\bool_xor:nn} % \begin{syntax} % \cs{bool_xor_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2} % \cs{bool_xor:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code} % \end{syntax} % Implements an \enquote{exclusive or} operation between two boolean % expressions. There is no infix operation for this logical operation. % \end{function} % % \section{Logical loops} % % Loops using either boolean expressions or stored boolean values. % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_until:Nn, \bool_do_until:cn} % \begin{syntax} % \cs{bool_do_until:Nn} \meta{boolean} \Arg{code} % \end{syntax} % Places the \meta{code} in the input stream for \TeX{} to process, % and then checks the logical value of the \meta{boolean}. If it is % \texttt{false} then the \meta{code} is inserted into the input % stream again and the process loops until the \meta{boolean} is % \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_while:Nn, \bool_do_while:cn} % \begin{syntax} % \cs{bool_do_while:Nn} \meta{boolean} \Arg{code} % \end{syntax} % Places the \meta{code} in the input stream for \TeX{} to process, % and then checks the logical value of the \meta{boolean}. If it is % \texttt{true} then the \meta{code} is inserted into the input % stream again and the process loops until the \meta{boolean} is % \texttt{false}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_until_do:Nn, \bool_until_do:cn} % \begin{syntax} % \cs{bool_until_do:Nn} \meta{boolean} \Arg{code} % \end{syntax} % This function first checks the logical value of the \meta{boolean}. % If it is \texttt{false} the \meta{code} is placed in the input stream % and expanded. After the completion of the \meta{code} the truth % of the \meta{boolean} is re-evaluated. The process then loops % until the \meta{boolean} is \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_while_do:Nn, \bool_while_do:cn} % \begin{syntax} % \cs{bool_while_do:Nn} \meta{boolean} \Arg{code} % \end{syntax} % This function first checks the logical value of the \meta{boolean}. % If it is \texttt{true} the \meta{code} is placed in the input stream % and expanded. After the completion of the \meta{code} the truth % of the \meta{boolean} is re-evaluated. The process then loops % until the \meta{boolean} is \texttt{false}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_until:nn} % \begin{syntax} % \cs{bool_do_until:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % Places the \meta{code} in the input stream for \TeX{} to process, % and then checks the logical value of the \meta{boolean expression} % as described for \cs{bool_if:nTF}. If it is \texttt{false} then the % \meta{code} is inserted into the input stream again and the % process loops until the \meta{boolean expression} evaluates to % \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_while:nn} % \begin{syntax} % \cs{bool_do_while:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % Places the \meta{code} in the input stream for \TeX{} to process, % and then checks the logical value of the \meta{boolean expression} % as described for \cs{bool_if:nTF}. If it is \texttt{true} then the % \meta{code} is inserted into the input stream again and the % process loops until the \meta{boolean expression} evaluates to % \texttt{false}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_until_do:nn} % \begin{syntax} % \cs{bool_until_do:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % This function first checks the logical value of the % \meta{boolean expression} (as described for \cs{bool_if:nTF}). % If it is \texttt{false} the \meta{code} is placed in the input stream % and expanded. After the completion of the \meta{code} the truth % of the \meta{boolean expression} is re-evaluated. The process % then loops until the \meta{boolean expression} is \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2017-07-15]{\bool_while_do:nn} % \begin{syntax} % \cs{bool_while_do:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % This function first checks the logical value of the % \meta{boolean expression} (as described for \cs{bool_if:nTF}). % If it is \texttt{true} the \meta{code} is placed in the input stream % and expanded. After the completion of the \meta{code} the truth % of the \meta{boolean expression} is re-evaluated. The process % then loops until the \meta{boolean expression} is \texttt{false}. % \end{function} % % \begin{function}[added = 2023-05-03, EXP, noTF]{\bool_case:n} % \begin{syntax} % \cs{bool_case:nTF} \\ % ~~|{| \\ % ~~~~\Arg{boolexpr case_1} \Arg{code case_1} \\ % ~~~~\Arg{boolexpr case_2} \Arg{code case_2} \\ % ~~~~\ldots \\ % ~~~~\Arg{boolexpr case_n} \Arg{code case_n} \\ % ~~|}| \\ % ~~\Arg{true code} % ~~\Arg{false code} % \end{syntax} % Evaluates in turn each of the \meta{boolean expression cases} until % the first one that evaluates to \texttt{true}. % The \meta{code} associated to this first case is left in the input % stream, followed by the \meta{true code}, and other cases are % discarded. If none of the cases match then only the \meta{false % code} is inserted. The function \cs{bool_case:n}, % which does nothing if there is no match, is % also available. For example % \begin{verbatim} % \bool_case:nF % { % { \dim_compare_p:n { \l__mypkg_wd_dim <= 10pt } } % { Fits } % { \int_compare_p:n { \l__mypkg_total_int >= 10 } } % { Many } % { \l__mypkg_special_bool } % { Special } % } % { No idea! } % \end{verbatim} % leaves \enquote{\texttt{Fits}} or \enquote{\texttt{Many}} or % \enquote{\texttt{Special}} or \enquote{\texttt{No idea!}} in the % input stream, in a way similar to some other language's % \enquote{\texttt{if} \ldots\ \texttt{elseif} \ldots\ \texttt{elseif} \ldots\ % \texttt{else} \ldots}. % \end{function} % % \section{Producing multiple copies} % % \begin{function}[updated = 2011-07-04, EXP]{\prg_replicate:nn} % \begin{syntax} % \cs{prg_replicate:nn} \Arg{integer expression} \Arg{tokens} % \end{syntax} % Evaluates the \meta{integer expression} (which should be % zero or positive) and creates the resulting number of copies % of the \meta{tokens}. The function is both expandable and safe for % nesting. It yields its result after two expansion steps. % \end{function} % % \section{Detecting \TeX{}'s mode} % % \begin{function}[EXP,pTF]{\mode_if_horizontal:} % \begin{syntax} % \cs{mode_if_horizontal_p:} % \cs{mode_if_horizontal:TF} \Arg{true code} \Arg{false code} % \end{syntax} % Detects if \TeX{} is currently in horizontal mode. % \end{function} % % \begin{function}[EXP,pTF]{\mode_if_inner:} % \begin{syntax} % \cs{mode_if_inner_p:} % \cs{mode_if_inner:TF} \Arg{true code} \Arg{false code} % \end{syntax} % Detects if \TeX{} is currently in inner mode. % \end{function} % % \begin{function}[updated = 2011-09-05, EXP,pTF]{\mode_if_math:} % \begin{syntax} % \cs{mode_if_math_p:} % \cs{mode_if_math:TF} \Arg{true code} \Arg{false code} % \end{syntax} % Detects if \TeX{} is currently in maths mode. % \end{function} % % \begin{function}[EXP,pTF]{\mode_if_vertical:} % \begin{syntax} % \cs{mode_if_vertical_p:} % \cs{mode_if_vertical:TF} \Arg{true code} \Arg{false code} % \end{syntax} % Detects if \TeX{} is currently in vertical mode. % \end{function} % % \section{Primitive conditionals} % % \begin{function}[EXP]{\if_predicate:w} % \begin{syntax} % \cs{if_predicate:w} \meta{predicate} \meta{true code} \cs{else:} \meta{false code} \cs{fi:} % \end{syntax} % This function takes a predicate function and % branches according to the result. (In practice this function would also % accept a single boolean variable in place of the \meta{predicate} but to make the % coding clearer this should be done through \cs{if_bool:N}.) % \end{function} % % \begin{function}[EXP]{\if_bool:N} % \begin{syntax} % \cs{if_bool:N} \meta{boolean} \meta{true code} \cs{else:} \meta{false code} \cs{fi:} % \end{syntax} % This function takes a boolean variable and % branches according to the result. % \end{function} % % \section{Nestable recursions and mappings} % % There are a number of places where recursion or mapping constructs are used in % \pkg{expl3}. At a low-level, these typically require insertion of tokens % at the end of the content to allow \enquote{clean up}. To support such % mappings in a nestable form, the following functions are provided. % % \begin{function}[EXP, added = 2018-03-26]{\prg_break_point:Nn} % \begin{syntax} % \cs{prg_break_point:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{code} % \end{syntax} % Used to mark the end of a recursion or mapping: the functions % \cs[no-index]{\meta{type}_map_break:} and % \cs[no-index]{\meta{type}_map_break:n} use this to break out of the loop % (see \cs{prg_map_break:Nn} for how to set these up). % After the loop ends, the \meta{code} is inserted into the input stream. This % occurs even if the break functions are \emph{not} applied: % \cs{prg_break_point:Nn} is functionally-equivalent in these cases % to \cs{use_ii:nn}. % \end{function} % % \begin{function}[EXP, added = 2018-03-26]{\prg_map_break:Nn} % \begin{syntax} % \cs{prg_map_break:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{user code} % \ldots{} % \cs{prg_break_point:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{ending code} % \end{syntax} % Breaks a recursion in mapping contexts, inserting in the input % stream the \meta{user code} after the \meta{ending code} for the % loop. The function breaks loops, inserting their \meta{ending % code}, until reaching a loop with the same \meta{type} as its % first argument. This \cs[no-index]{\meta{type}_map_break:} argument % must be defined; it is simply used as a recognizable marker for the \meta{type}. % % For types with mappings defined in the kernel, % \cs[no-index]{\meta{type}_map_break:} and \cs[no-index]{\meta{type}_map_break:n} % are defined as \cs{prg_map_break:Nn} \cs[no-index]{\meta{type}_map_break:} |{}| % and the same with |{}| omitted. % \end{function} % % \subsection{Simple mappings} % % In addition to the more complex mappings above, non-nestable mappings are % used in a number of locations and support is provided for these. % % \begin{function}[EXP, added = 2018-03-27]{\prg_break_point:} % This copy of \cs{prg_do_nothing:} is used to mark the end of a fast % short-term recursion: the function \cs{prg_break:n} uses this to % break out of the loop. % \end{function} % % \begin{function}[EXP, added = 2018-03-27]{\prg_break:, \prg_break:n} % \begin{syntax} % \cs{prg_break:n} \Arg{code} \ldots{} \cs{prg_break_point:} % \end{syntax} % Breaks a recursion which has no \meta{ending code} and which is not % a user-breakable mapping (see for instance % implementation of \cs{int_step_function:nnnN}), and % inserts the \meta{code} in the input stream. % \end{function} % % \section{Internal programming functions} % % \begin{function}[updated = 2011-08-11, EXP] % {\group_align_safe_begin:, \group_align_safe_end:} % \begin{syntax} % \cs{group_align_safe_begin:} % \ldots % \cs{group_align_safe_end:} % \end{syntax} % These functions are used to enclose material in a \TeX{} alignment % environment within a specially-constructed group. This group is % designed in such a way that it does not add brace groups to the % output but does act as a group for the |&| token inside % \tn{halign}. This is necessary to allow grabbing of tokens % for testing purposes, as \TeX{} uses group level to determine the % effect of alignment tokens. Without the special grouping, the use of % a function such as \cs{peek_after:Nw} would result in a forbidden % comparison of the internal \tn{endtemplate} token, yielding a % fatal error. Each \cs{group_align_safe_begin:} must be matched by a % \cs{group_align_safe_end:}, although this does not have to occur % within the same function. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3prg} implementation} % % \TestFiles{m3prg001.lvt,m3prg002.lvt,m3prg003.lvt} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Primitive conditionals} % % \begin{macro}{\if_bool:N} % \begin{macro}{\if_predicate:w} % Those two primitive \TeX{} conditionals are synonyms. % \cs{if_bool:N} is defined in \pkg{l3basics}, as it's needed % earlier to define quark test functions. % \begin{macrocode} \cs_new_eq:NN \if_predicate:w \tex_ifodd:D % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Defining a set of conditional functions} % % \begin{macro} % { % \prg_set_conditional:Npnn, % \prg_new_conditional:Npnn, % \prg_set_protected_conditional:Npnn, % \prg_new_protected_conditional:Npnn % } % \begin{macro} % { % \prg_set_conditional:Nnn, % \prg_new_conditional:Nnn, % \prg_set_protected_conditional:Nnn, % \prg_new_protected_conditional:Nnn % } % \begin{macro}{\prg_set_eq_conditional:NNn, \prg_new_eq_conditional:NNn} % \begin{macro}{\prg_return_true:} % \TestMissing % {This function is implicitly tested with all other conditionals!} % \begin{macro}{\prg_return_false:} % \TestMissing % {This function is also implicitly tested with all other conditionals!} % These are all defined in \pkg{l3basics}, as they are needed % \enquote{early}. This is just a reminder! % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{The boolean data type} % % \begin{macrocode} %<@@=bool> % \end{macrocode} % % \begin{macro}{\bool_new:N, \bool_new:c} % \UnitTested % Boolean variables have to be initiated when they are created. Other % than that there is not much to say here. % \begin{macrocode} \cs_new_protected:Npn \bool_new:N #1 { \cs_new_eq:NN #1 \c_false_bool } \cs_generate_variant:Nn \bool_new:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn} % A merger between \cs{tl_const:Nn} and \cs{bool_set:Nn}. % \begin{macrocode} \cs_new_protected:Npn \bool_const:Nn #1#2 { \__kernel_chk_if_free_cs:N #1 \tex_global:D \tex_chardef:D #1 = \bool_if_p:n {#2} } \cs_generate_variant:Nn \bool_const:Nn { c } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \bool_set_true:N, \bool_set_true:c, % \bool_gset_true:N, \bool_gset_true:c, % \bool_set_false:N, \bool_set_false:c, % \bool_gset_false:N, \bool_gset_false:c % } % \UnitTested % Setting is already pretty easy. % When \texttt{check-declarations} is active, the definitions are patched to % make sure the boolean exists. This is needed because booleans are % not based on token lists nor on \TeX{} registers. % \begin{macrocode} \cs_new_protected:Npn \bool_set_true:N #1 { \cs_set_eq:NN #1 \c_true_bool } \cs_new_protected:Npn \bool_set_false:N #1 { \cs_set_eq:NN #1 \c_false_bool } \cs_new_protected:Npn \bool_gset_true:N #1 { \cs_gset_eq:NN #1 \c_true_bool } \cs_new_protected:Npn \bool_gset_false:N #1 { \cs_gset_eq:NN #1 \c_false_bool } \cs_generate_variant:Nn \bool_set_true:N { c } \cs_generate_variant:Nn \bool_set_false:N { c } \cs_generate_variant:Nn \bool_gset_true:N { c } \cs_generate_variant:Nn \bool_gset_false:N { c } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \bool_set_eq:NN, \bool_set_eq:cN, % \bool_set_eq:Nc, \bool_set_eq:cc, % \bool_gset_eq:NN, \bool_gset_eq:cN, % \bool_gset_eq:Nc, \bool_gset_eq:cc % } % \UnitTested % The usual copy code. While it would be cleaner semantically to copy % the \cs{cs_set_eq:NN} family of functions, we copy \cs{tl_set_eq:NN} % because that has the correct checking code. % \begin{macrocode} \cs_new_eq:NN \bool_set_eq:NN \tl_set_eq:NN \cs_new_eq:NN \bool_gset_eq:NN \tl_gset_eq:NN \cs_generate_variant:Nn \bool_set_eq:NN { Nc, cN, cc } \cs_generate_variant:Nn \bool_gset_eq:NN { Nc, cN, cc } % \end{macrocode} % \end{macro} % % \begin{macro}{\bool_set:Nn, \bool_set:cn} % \begin{macro}{\bool_gset:Nn, \bool_gset:cn} % This function evaluates a boolean expression and assigns the first % argument the meaning \cs{c_true_bool} or \cs{c_false_bool}. Again, % we include some checking code. It is important to evaluate the % expression before applying the \tn{chardef} primitive, because that % primitive sets the left-hand side to \cs{scan_stop:} before looking % for the right-hand side. % \begin{macrocode} \cs_new_protected:Npn \bool_set:Nn #1#2 { \exp_last_unbraced:NNNf \tex_chardef:D #1 = { \bool_if_p:n {#2} } } \cs_new_protected:Npn \bool_gset:Nn #1#2 { \exp_last_unbraced:NNNNf \tex_global:D \tex_chardef:D #1 = { \bool_if_p:n {#2} } } \cs_generate_variant:Nn \bool_set:Nn { c } \cs_generate_variant:Nn \bool_gset:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[added = 2018-05-10] % {\bool_set_inverse:N, \bool_set_inverse:c, \bool_gset_inverse:N, \bool_gset_inverse:c} % Set to \texttt{false} or \texttt{true} locally or globally. % \begin{macrocode} \cs_new_protected:Npn \bool_set_inverse:N #1 { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 } \cs_generate_variant:Nn \bool_set_inverse:N { c } \cs_new_protected:Npn \bool_gset_inverse:N #1 { \bool_if:NTF #1 { \bool_gset_false:N } { \bool_gset_true:N } #1 } \cs_generate_variant:Nn \bool_gset_inverse:N { c } % \end{macrocode} % \end{macro} % % \subsection{Internal auxiliaries} % % \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop} % Internal recursion quarks. % \begin{macrocode} \quark_new:N \q_@@_recursion_tail \quark_new:N \q_@@_recursion_stop % \end{macrocode} % \end{variable} % % \begin{macro}[EXP]{\@@_use_i_delimit_by_q_recursion_stop:nw} % Functions to gobble up to a quark. % \begin{macrocode} \cs_new:Npn \@@_use_i_delimit_by_q_recursion_stop:nw #1 #2 \q_@@_recursion_stop {#1} % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_if_recursion_tail_stop_do:nn} % Functions to query recursion quarks. % \begin{macrocode} \__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:nn % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\bool_if:N, \bool_if:c} % \UnitTested % Straight forward here. We could optimize here if we wanted to as % the boolean can just be input directly. % \begin{macrocode} \prg_new_conditional:Npnn \bool_if:N #1 { p , T , F , TF } { \if_bool:N #1 \prg_return_true: \else: \prg_return_false: \fi: } \prg_generate_conditional_variant:Nnn \bool_if:N { c } { p , T , F , TF } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\bool_to_str:N, \bool_to_str:c, \bool_to_str:n} % Expands to string literal \texttt{true} or \texttt{false}. % \begin{macrocode} \cs_new:Npe \bool_to_str:N #1 { \exp_not:N \bool_if:NTF #1 { \tl_to_str:n { true } } { \tl_to_str:n { false } } } \cs_generate_variant:Nn \bool_to_str:N { c } \cs_new:Npe \bool_to_str:n #1 { \exp_not:N \bool_if:nTF {#1} { \tl_to_str:n { true } } { \tl_to_str:n { false } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\bool_show:n, \bool_log:n} % Show the truth value of the boolean. % \begin{macrocode} \cs_new_protected:Npn \bool_show:n { \__kernel_msg_show_eval:Nn \bool_to_str:n } \cs_new_protected:Npn \bool_log:n { \__kernel_msg_log_eval:Nn \bool_to_str:n } % \end{macrocode} % \end{macro} % % \begin{macro}{\bool_show:N, \bool_show:c, \bool_log:N, \bool_log:c, \@@_show:NN} % Show the truth value of the boolean, as \texttt{true} or % \texttt{false}. % \begin{macrocode} \cs_new_protected:Npn \bool_show:N { \@@_show:NN \tl_show:n } \cs_generate_variant:Nn \bool_show:N { c } \cs_new_protected:Npn \bool_log:N { \@@_show:NN \tl_log:n } \cs_generate_variant:Nn \bool_log:N { c } \cs_new_protected:Npn \@@_show:NN #1#2 { \__kernel_chk_defined:NT #2 { \token_case_meaning:NnF #2 { \c_true_bool { \exp_args:Ne #1 { \token_to_str:N #2 = true } } \c_false_bool { \exp_args:Ne #1 { \token_to_str:N #2 = false } } } { \msg_error:nneee { kernel } { bad-type } { \token_to_str:N #2 } { \token_to_meaning:N #2 } { bool } } } } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_tmpa_bool, \l_tmpb_bool, \g_tmpa_bool, \g_tmpb_bool} % A few booleans just if you need them. % \begin{macrocode} \bool_new:N \l_tmpa_bool \bool_new:N \l_tmpb_bool \bool_new:N \g_tmpa_bool \bool_new:N \g_tmpb_bool % \end{macrocode} % \end{variable} % % \begin{macro}[pTF]{\bool_if_exist:N, \bool_if_exist:c} % Copies of the \texttt{cs} functions defined in \pkg{l3basics}. % \begin{macrocode} \prg_new_eq_conditional:NNn \bool_if_exist:N \cs_if_exist:N { TF , T , F , p } \prg_new_eq_conditional:NNn \bool_if_exist:c \cs_if_exist:c { TF , T , F , p } % \end{macrocode} % \end{macro} % % \subsection{Boolean expressions} % % \begin{macro}[pTF]{\bool_if:n} % \UnitTested % Evaluating the truth value of a list of predicates is done using an % input syntax somewhat similar to the one found in other programming % languages with |(| and |)| for grouping, |!| for logical % \enquote{Not}, |&&| for logical \enquote{And} and \verb"||" for % logical \enquote{Or}. However, they perform eager evaluation. % We shall use the terms Not, And, Or, Open and % Close for these operations. % % Any expression is terminated by a Close operation. Evaluation % happens from left to right in the following manner using a GetNext % function: % \begin{itemize} % \item If an Open is seen, start evaluating a new expression using % the Eval function and call GetNext again. % \item If a Not is seen, remove the |!| and call a GetNext % function with the logic reversed. % \item If none of the above, reinsert the token found (this is % supposed to be a predicate function) in front of an Eval % function, which evaluates it to the boolean value \meta{true} or % \meta{false}. % \end{itemize} % The Eval function then contains a post-processing operation which % grabs the instruction following the predicate. This is either And, % Or or Close. In each case the truth value is used to determine % where to go next. The following situations can arise: % \begin{description} % \item[\meta{true}And] Current truth value is true, logical And % seen, continue with GetNext to examine truth value of next % boolean (sub-)expression. % \item[\meta{false}And] Current truth value is false, logical And % seen, stop using the values of predicates within this % sub-expression until the next Close. Then return \meta{false}. % \item[\meta{true}Or] Current truth value is true, logical Or seen, % stop using the values of predicates within this sub-expression % until the nearest Close. Then return \meta{true}. % \item[\meta{false}Or] Current truth value is false, logical Or % seen, continue with GetNext to examine truth value of next % boolean (sub-)expression. % \item[\meta{true}Close] Current truth value is true, Close % seen, return \meta{true}. % \item[\meta{false}Close] Current truth value is false, Close % seen, return \meta{false}. % \end{description} % \begin{macrocode} \prg_new_conditional:Npnn \bool_if:n #1 { T , F , TF } { \if_predicate:w \bool_if_p:n {#1} \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\bool_if_p:n, \@@_if_p:n, \@@_if_p_aux:w} % To speed up the case of a single predicate, \texttt{f}-expand and % check whether the result is one token (possibly surrounded by % spaces), which must be \cs{c_true_bool} or \cs{c_false_bool}. We % use a version of \cs{tl_if_single:nTF} optimized for speed since we % know that an empty~|#1| is an error. The auxiliary % \cs{@@_if_p_aux:w} removes the trailing parenthesis and gets rid of % any space, then returns \cs{c_true_bool} or \cs{c_false_bool} as % appropriate. This extra work around is because in a % \cs{bool_set:Nn}, the underlying \tn{chardef} turns the bool being % set temporarily equal to \tn{relax}, thus assigning a boolean to % itself would fail (gh/1055). For the general case, first issue a % \cs{group_align_safe_begin:} as we are using |&&| as % syntax shorthand for the And operation and we need to hide it for % \TeX{}. This group is closed after \cs{@@_get_next:NN} returns % \cs{c_true_bool} or \cs{c_false_bool}. That function requires the % trailing parenthesis to know where the expression ends. % \begin{macrocode} \cs_new:Npn \bool_if_p:n { \exp_args:Nf \@@_if_p:n } \cs_new:Npn \@@_if_p:n #1 { \tl_if_empty:oT { \use_none:nn #1 . } { \@@_if_p_aux:w } \group_align_safe_begin: \exp_after:wN \group_align_safe_end: \exp:w \exp_end_continue_f:w % ( \@@_get_next:NN \use_i:nnnn #1 ) } \cs_new:Npn \@@_if_p_aux:w #1 \use_i:nnnn #2#3 { \bool_if:NTF #2 \c_true_bool \c_false_bool } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_next:NN} % The GetNext operation. Its first argument is \cs{use_i:nnnn}, % \cs{use_ii:nnnn}, \cs{use_iii:nnnn}, or \cs{use_iv:nnnn} (we call % these \enquote{states}). In the first state, this function % eventually expand to the truth value \cs{c_true_bool} or % \cs{c_false_bool} of the expression which follows until the next % unmatched closing parenthesis. For instance % \enquote{\cs{@@_get_next:NN} \cs{use_i:nnnn} \cs{c_true_bool} % \texttt{\&\&} \cs{c_true_bool} \texttt{)}} (including the closing % parenthesis) expands to \cs{c_true_bool}. In the second state % (after a |!|) the logic is reversed. We call these two states % \enquote{normal} and the next two \enquote{skipping}. In the third % state (after \cs{c_true_bool}\verb"||") it always returns % \cs{c_true_bool}. In the fourth state (after \cs{c_false_bool}|&&|) % it always returns \cs{c_false_bool} and also stops when encountering % \verb"||", not only parentheses. This code itself is a switch: if % what follows is neither |!| nor |(|, we assume it is a predicate. % \begin{macrocode} \cs_new:Npn \@@_get_next:NN #1#2 { \use:c { @@_ \if_meaning:w !#2 ! \else: \if_meaning:w (#2 ( \else: p \fi: \fi: :Nw } #1 #2 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_!:Nw} % The Not operation reverses the logic: it discards the |!|~token and % calls the GetNext operation with the appropriate first argument. % Namely the first and second states are interchanged, but after % \cs{c_true_bool}\verb"||" or \cs{c_false_bool}|&&| the |!|~is % ignored. % \begin{macrocode} \cs_new:cpn { @@_!:Nw } #1#2 { \exp_after:wN \@@_get_next:NN #1 \use_ii:nnnn \use_i:nnnn \use_iii:nnnn \use_iv:nnnn } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_(:Nw} % The Open operation starts a sub-expression after discarding the open % parenthesis. This is done by calling GetNext (which eventually % discards the corresponding closing parenthesis), with a % post-processing step which looks for And, Or or Close after the % group. % \begin{macrocode} \cs_new:cpn { @@_(:Nw } #1#2 { \exp_after:wN \@@_choose:NNN \exp_after:wN #1 \int_value:w \@@_get_next:NN \use_i:nnnn } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_p:Nw} % If what follows GetNext is neither |!| nor |(|, evaluate the % predicate using the primitive \cs{int_value:w}. The canonical % \texttt{true} and \texttt{false} values have numerical values $1$ % and $0$ respectively. Look for And, Or or Close afterwards. % \begin{macrocode} \cs_new:cpn { @@_p:Nw } #1 { \exp_after:wN \@@_choose:NNN \exp_after:wN #1 \int_value:w } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_choose:NNN} % \begin{macro}+\@@_)_0:+ % \begin{macro}+\@@_)_1:+ % \begin{macro}+\@@_)_2:+ % \begin{macro}+\@@_&_0:+ % \begin{macro}+\@@_&_1:+ % \begin{macro}+\@@_&_2:+ % \begin{macro}+\@@_|_0:+ % \begin{macro}+\@@_|_1:+ % \begin{macro}+\@@_|_2:+ % The arguments are |#1|: a function such as \cs{use_i:nnnn}, |#2|: % $0$ or $1$ encoding the current truth value, |#3|: the next % operation, And, Or or Close. We distinguish three cases according % to a combination of |#1| and |#2|. Case~2 is when |#1| is % \cs{use_iii:nnnn} (state~3), namely after % \cs{c_true_bool}~\verb"||". Case~1 is when |#1| is \cs{use_i:nnnn} % and |#2| is \texttt{true} or when |#1| is \cs{use_ii:nnnn} and |#2| % is \texttt{false}, for instance for |!|\cs{c_false_bool}. Case~0 % includes the same with \texttt{true}/\texttt{false} interchanged and % the case where |#1| is \cs{use_iv:nnnn} namely after % \cs{c_false_bool}~|&&|. % % When seeing~|)| the current subexpression is done, leave the % appropriate boolean. When seeing~|&| in case~0 go into state~4, % equivalent to having seen \cs{c_false_bool}~|&&|. In case~1, namely % when the argument is \texttt{true} and we are in a normal state % continue in the normal state~1. In case~2, namely when skipping % alternatives in an Or, continue in the same state. When % seeing~\verb"|" in case~0, continue in a normal state; in particular % stop skipping for \cs{c_false_bool}~|&&| because that binds more % tightly than~\verb"||". In the other two cases start skipping for % \cs{c_true_bool}~\verb"||". % \begin{macrocode} \cs_new:Npn \@@_choose:NNN #1#2#3 { \use:c { @@_ \token_to_str:N #3 _ #1 #2 { \if_meaning:w 0 #2 1 \else: 0 \fi: } 2 0 : } } \cs_new:cpn { @@_)_0: } { \c_false_bool } \cs_new:cpn { @@_)_1: } { \c_true_bool } \cs_new:cpn { @@_)_2: } { \c_true_bool } \cs_new:cpn { @@_&_0: } & { \@@_get_next:NN \use_iv:nnnn } \cs_new:cpn { @@_&_1: } & { \@@_get_next:NN \use_i:nnnn } \cs_new:cpn { @@_&_2: } & { \@@_get_next:NN \use_iii:nnnn } \cs_new:cpn { @@_|_0: } | { \@@_get_next:NN \use_i:nnnn } \cs_new:cpn { @@_|_1: } | { \@@_get_next:NN \use_iii:nnnn } \cs_new:cpn { @@_|_2: } | { \@@_get_next:NN \use_iii:nnnn } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\bool_lazy_all:n} % \begin{macro}{\@@_lazy_all:n} % \UnitTested % Go through the list of expressions, stopping whenever an expression % is \texttt{false}. If the end is reached without finding any % \texttt{false} expression, then the result is \texttt{true}. % \begin{macrocode} \cs_new:Npn \bool_lazy_all_p:n #1 { \@@_lazy_all:n #1 \q_@@_recursion_tail \q_@@_recursion_stop } \prg_new_conditional:Npnn \bool_lazy_all:n #1 { T , F , TF } { \if_predicate:w \bool_lazy_all_p:n {#1} \prg_return_true: \else: \prg_return_false: \fi: } \cs_new:Npn \@@_lazy_all:n #1 { \@@_if_recursion_tail_stop_do:nn {#1} { \c_true_bool } \bool_if:nF {#1} { \@@_use_i_delimit_by_q_recursion_stop:nw { \c_false_bool } } \@@_lazy_all:n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\bool_lazy_and:nn} % \UnitTested % Only evaluate the second expression if the first is \texttt{true}. % Note that |#2| must be removed as an argument, not just by skipping % to the \cs{else:} branch of the conditional since |#2| may contain % unbalanced \TeX{} conditionals. % \begin{macrocode} \prg_new_conditional:Npnn \bool_lazy_and:nn #1#2 { p , T , F , TF } { \if_predicate:w \bool_if:nTF {#1} { \bool_if_p:n {#2} } { \c_false_bool } \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\bool_lazy_any:n} % \begin{macro}{\@@_lazy_any:n} % \UnitTested % Go through the list of expressions, stopping whenever an expression % is \texttt{true}. If the end is reached without finding any % \texttt{true} expression, then the result is \texttt{false}. % \begin{macrocode} \cs_new:Npn \bool_lazy_any_p:n #1 { \@@_lazy_any:n #1 \q_@@_recursion_tail \q_@@_recursion_stop } \prg_new_conditional:Npnn \bool_lazy_any:n #1 { T , F , TF } { \if_predicate:w \bool_lazy_any_p:n {#1} \prg_return_true: \else: \prg_return_false: \fi: } \cs_new:Npn \@@_lazy_any:n #1 { \@@_if_recursion_tail_stop_do:nn {#1} { \c_false_bool } \bool_if:nT {#1} { \@@_use_i_delimit_by_q_recursion_stop:nw { \c_true_bool } } \@@_lazy_any:n } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\bool_lazy_or:nn} % \UnitTested % Only evaluate the second expression if the first is \texttt{false}. % \begin{macrocode} \prg_new_conditional:Npnn \bool_lazy_or:nn #1#2 { p , T , F , TF } { \if_predicate:w \bool_if:nTF {#1} { \c_true_bool } { \bool_if_p:n {#2} } \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}{\bool_not_p:n} % \UnitTested % The Not variant just reverses the outcome of \cs{bool_if_p:n}. Can % be optimized but this is nice and simple and according to the % implementation plan. Not even particularly useful to have it when % the infix notation is easier to use. % \begin{macrocode} \cs_new:Npn \bool_not_p:n #1 { \bool_if_p:n { ! ( #1 ) } } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\bool_xor:nn} % \UnitTested % Exclusive or. If the boolean expressions have same truth value, % return \texttt{false}, otherwise return \texttt{true}. % \begin{macrocode} \prg_new_conditional:Npnn \bool_xor:nn #1#2 { p , T , F , TF } { \bool_if:nT {#1} \reverse_if:N \if_predicate:w \bool_if_p:n {#2} \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \subsection{Logical loops} % % \begin{macro}{\bool_while_do:Nn, \bool_while_do:cn} % \UnitTested % \begin{macro}{\bool_until_do:Nn, \bool_until_do:cn} % \UnitTested % A |while| loop where the boolean is tested before executing the % statement. The \enquote{while} version executes the code as long as the % boolean is true; the \enquote{until} version executes the code as % long as the boolean is false. % \begin{macrocode} \cs_new:Npn \bool_while_do:Nn #1#2 { \bool_if:NT #1 { #2 \bool_while_do:Nn #1 {#2} } } \cs_new:Npn \bool_until_do:Nn #1#2 { \bool_if:NF #1 { #2 \bool_until_do:Nn #1 {#2} } } \cs_generate_variant:Nn \bool_while_do:Nn { c } \cs_generate_variant:Nn \bool_until_do:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\bool_do_while:Nn, \bool_do_while:cn} % \UnitTested % \begin{macro}{\bool_do_until:Nn, \bool_do_until:cn} % \UnitTested % A |do-while| loop where the body is performed at least once and the % boolean is tested after executing the body. Otherwise identical to % the above functions. % \begin{macrocode} \cs_new:Npn \bool_do_while:Nn #1#2 { #2 \bool_if:NT #1 { \bool_do_while:Nn #1 {#2} } } \cs_new:Npn \bool_do_until:Nn #1#2 { #2 \bool_if:NF #1 { \bool_do_until:Nn #1 {#2} } } \cs_generate_variant:Nn \bool_do_while:Nn { c } \cs_generate_variant:Nn \bool_do_until:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \bool_while_do:nn, \bool_do_while:nn , % \bool_until_do:nn, \bool_do_until:nn % } % \UnitTested % Loop functions with the test either before or after the first body % expansion. % \begin{macrocode} \cs_new:Npn \bool_while_do:nn #1#2 { \bool_if:nT {#1} { #2 \bool_while_do:nn {#1} {#2} } } \cs_new:Npn \bool_do_while:nn #1#2 { #2 \bool_if:nT {#1} { \bool_do_while:nn {#1} {#2} } } \cs_new:Npn \bool_until_do:nn #1#2 { \bool_if:nF {#1} { #2 \bool_until_do:nn {#1} {#2} } } \cs_new:Npn \bool_do_until:nn #1#2 { #2 \bool_if:nF {#1} { \bool_do_until:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % \begin{variable}{\s_@@_mark,\s_@@_stop} % Internal scan marks. % \begin{macrocode} \scan_new:N \s_@@_mark \scan_new:N \s_@@_stop % \end{macrocode} % \end{variable} % % \begin{macro}[EXP, noTF]{\bool_case:n} % \begin{macro}{\@@_case:NnTF} % \begin{macro}{\@@_case:w,a\@@_case_end:nw} % For boolean cases the overall idea is the same as for % \cs{str_case:nnTF} as described in \pkg{l3str}. % \begin{macrocode} \cs_new:Npn \bool_case:nTF { \exp:w \@@_case:nTF } \cs_new:Npn \bool_case:nT #1#2 { \exp:w \@@_case:nTF {#1} {#2} { } } \cs_new:Npn \bool_case:nF #1 { \exp:w \@@_case:nTF {#1} { } } \cs_new:Npn \bool_case:n #1 { \exp:w \@@_case:nTF {#1} { } { } } \cs_new:Npn \@@_case:nTF #1#2#3 { \@@_case:w #1 \c_true_bool { } \s_@@_mark {#2} \s_@@_mark {#3} \s_@@_stop } \cs_new:Npn \@@_case:w #1#2 { \bool_if:nTF {#1} { \@@_case_end:nw {#2} } { \@@_case:w } } \cs_new:Npn \@@_case_end:nw #1#2#3 \s_@@_mark #4#5 \s_@@_stop { \exp_end: #1 #4 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Producing multiple copies} % % \begin{macrocode} %<@@=prg> % \end{macrocode} % % \begin{macro}{\prg_replicate:nn} % \UnitTested % \begin{macro}{\@@_replicate:N, \@@_replicate_first:N} % \begin{macro}{\@@_replicate_} % \begin{macro} % { % \@@_replicate_0:n, % \@@_replicate_1:n, % \@@_replicate_2:n, % \@@_replicate_3:n, % \@@_replicate_4:n, % \@@_replicate_5:n, % \@@_replicate_6:n, % \@@_replicate_7:n, % \@@_replicate_8:n, % \@@_replicate_9:n % } % \begin{macro} % { % \@@_replicate_first_-:n, % \@@_replicate_first_0:n, % \@@_replicate_first_1:n, % \@@_replicate_first_2:n, % \@@_replicate_first_3:n, % \@@_replicate_first_4:n, % \@@_replicate_first_5:n, % \@@_replicate_first_6:n, % \@@_replicate_first_7:n, % \@@_replicate_first_8:n, % \@@_replicate_first_9:n % } % This function uses a cascading csname technique by David Kastrup % (who else :-) % % The idea is to make the input |25| result in first adding five, and % then 20 copies of the code to be replicated. The technique uses % cascading csnames which means that we start building several csnames % so we end up with a list of functions to be called in reverse % order. This is important here (and other places) because it means % that we can for instance make the function that inserts five copies % of something to also hand down ten to the next function in % line. This is exactly what happens here: in the example with |25| % then the next function is the one that inserts two copies but it % sees the ten copies handed down by the previous function. In order % to avoid the last function to insert say, $100$ copies of the original % argument just to gobble them again we define separate functions to % be inserted first. These functions also close the expansion of % \cs{exp:w}, which ensures that \cs{prg_replicate:nn} only % requires two steps of expansion. % % This function has one flaw though: Since it constantly passes down % ten copies of its previous argument it severely affects the main % memory once you start demanding hundreds of thousands of copies. Now % I don't think this is a real limitation for any ordinary use, and if % necessary, it is possible to write \cs{prg_replicate:nn} |{1000}| % |{| \cs{prg_replicate:nn} |{1000}| \Arg{code}~|}|. An % alternative approach is to create a string of |m|'s with % \cs{exp:w} which can be done with just four macros but that % method has its own problems since it can exhaust the string % pool. Also, it is considerably slower than what we use here so the % few extra csnames are well spent I would say. % \begin{macrocode} \cs_new:Npn \prg_replicate:nn #1 { \exp:w \exp_after:wN \@@_replicate_first:N \int_value:w \int_eval:n {#1} \cs_end: } \cs_new:Npn \@@_replicate:N #1 { \cs:w @@_replicate_#1 :n \@@_replicate:N } \cs_new:Npn \@@_replicate_first:N #1 { \cs:w @@_replicate_first_ #1 :n \@@_replicate:N } % \end{macrocode} % Then comes all the functions that do the hard work of inserting all % the copies. The first function takes |:n| as a parameter. % \begin{macrocode} \cs_new:Npn \@@_replicate_ :n #1 { \cs_end: } \cs_new:cpn { @@_replicate_0:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} } \cs_new:cpn { @@_replicate_1:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1 } \cs_new:cpn { @@_replicate_2:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1 } \cs_new:cpn { @@_replicate_3:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1 } \cs_new:cpn { @@_replicate_4:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1 } \cs_new:cpn { @@_replicate_5:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1 } \cs_new:cpn { @@_replicate_6:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_7:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_8:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_9:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1#1 } % \end{macrocode} % Users shouldn't ask for something to be replicated once or even % not at all but\dots % \begin{macrocode} \cs_new:cpn { @@_replicate_first_-:n } #1 { \exp_end: \msg_expandable_error:nn { prg } { negative-replication } } \cs_new:cpn { @@_replicate_first_0:n } #1 { \exp_end: } \cs_new:cpn { @@_replicate_first_1:n } #1 { \exp_end: #1 } \cs_new:cpn { @@_replicate_first_2:n } #1 { \exp_end: #1#1 } \cs_new:cpn { @@_replicate_first_3:n } #1 { \exp_end: #1#1#1 } \cs_new:cpn { @@_replicate_first_4:n } #1 { \exp_end: #1#1#1#1 } \cs_new:cpn { @@_replicate_first_5:n } #1 { \exp_end: #1#1#1#1#1 } \cs_new:cpn { @@_replicate_first_6:n } #1 { \exp_end: #1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_first_7:n } #1 { \exp_end: #1#1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_first_8:n } #1 { \exp_end: #1#1#1#1#1#1#1#1 } \cs_new:cpn { @@_replicate_first_9:n } #1 { \exp_end: #1#1#1#1#1#1#1#1#1 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Detecting \TeX{}'s mode} % % \begin{macro}[pTF]{\mode_if_vertical:} % \UnitTested % For testing vertical mode. Strikes me here on the bus with David, % that as long as we are just talking about returning true and % false states, we can just use the primitive conditionals for this % and gobbling the \cs{exp_end:} in the input stream. However this % requires knowledge of the implementation so we keep things nice % and clean and use the return statements. % \begin{macrocode} \prg_new_conditional:Npnn \mode_if_vertical: { p , T , F , TF } { \if_mode_vertical: \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\mode_if_horizontal:} % \UnitTested % For testing horizontal mode. % \begin{macrocode} \prg_new_conditional:Npnn \mode_if_horizontal: { p , T , F , TF } { \if_mode_horizontal: \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\mode_if_inner:} % \UnitTested % For testing inner mode. % \begin{macrocode} \prg_new_conditional:Npnn \mode_if_inner: { p , T , F , TF } { \if_mode_inner: \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\mode_if_math:} % \UnitTested % For testing math mode. At the beginning of an alignment cell, % this should be used only inside a non-expandable function. % \begin{macrocode} \prg_new_conditional:Npnn \mode_if_math: { p , T , F , TF } { \if_mode_math: \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % % \subsection{Internal programming functions} % % \begin{macro}{\group_align_safe_begin:, \group_align_safe_end:} % \TeX{}'s alignment structures present many problems. As Knuth says % himself in \emph{\TeX : The Program}: \enquote{It's sort of a miracle % whenever \tn{halign} or \tn{valign} work, [\ldots]} One problem relates % to commands that internally issue a \tn{cr} but also peek ahead for % the next character for use in, say, an optional argument. If the % next token happens to be a |&| with category code~4 we get some % sort of weird error message because the underlying % \tn{futurelet} stores the token at the end of the alignment % template. This could be a |&|$_4$ giving a message like % |! Misplaced \cr.| or even worse: it could be the \tn{endtemplate} % token causing even more trouble! To solve this we have to open a % special group so that \TeX{} still thinks it's on safe ground but at % the same time we don't want to introduce any brace group that may % find its way to the output. The following functions help with this % by using behaviour documented only in Appendix~D of % \emph{The \TeX{}book}\dots % In short evaluating |`{| and |`}| as numbers will not change the counter % \TeX{} uses to keep track of its state in an alignment, whereas gobbling a % brace using \cs{if_false:} will affect \TeX's state without producing any % real group. % We place the \cs{if_false:} |{| \cs{fi:} part at that place so % that the successive expansions of \cs{group_align_safe_begin/end:} % are always brace balanced. % \begin{macrocode} \group_begin: \tex_catcode:D `\^^@ = 2 \exp_stop_f: \cs_new:Npn \group_align_safe_begin: { \exp:w \if_false: { \fi: `^^@ \exp_stop_f: } \tex_catcode:D `\^^@ = 1 \exp_stop_f: \cs_new:Npn \group_align_safe_end: { \exp:w `^^@ \if_false: } \fi: \exp_stop_f: } \group_end: % \end{macrocode} % \end{macro} % % \begin{variable}{\g__kernel_prg_map_int} % A nesting counter for mapping. % \begin{macrocode} \int_new:N \g__kernel_prg_map_int % \end{macrocode} % \end{variable} % % \begin{macro}{\prg_break_point:Nn} % \begin{macro}{\prg_map_break:Nn} % These are defined in \pkg{l3basics}, as they are needed % \enquote{early}. This is just a reminder that is the case! % \end{macro} % \end{macro} % % \begin{macro}{\prg_break_point:} % \begin{macro}{\prg_break:, \prg_break:n} % Also done in \pkg{l3basics}. % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex