diff options
Diffstat (limited to 'macros/latex/required/l3kernel/l3prg.dtx')
-rw-r--r-- | macros/latex/required/l3kernel/l3prg.dtx | 1829 |
1 files changed, 1829 insertions, 0 deletions
diff --git a/macros/latex/required/l3kernel/l3prg.dtx b/macros/latex/required/l3kernel/l3prg.dtx new file mode 100644 index 0000000000..ea76955d82 --- /dev/null +++ b/macros/latex/required/l3kernel/l3prg.dtx @@ -0,0 +1,1829 @@ +% \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} +%</driver> +% \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-04-11} +% +% \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} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |