% \iffalse meta-comment % %% File: l3prg.dtx Copyright (C) 2005-2017 The LaTeX3 Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % http://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]{l3doc} \def\ExplFileDate{2017/03/18} \def\ExplFileVersion{7019} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3prg} package\\ Control structures^^A % \thanks{This file describes v\ExplFileVersion, % last revised \ExplFileDate.}^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released \ExplFileDate} % % \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 = 2012-02-06] % { % \prg_new_conditional:Npnn, \prg_set_conditional:Npnn, % \prg_new_conditional:Nnn, \prg_set_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 will 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_new_protected_conditional:Nnn, \prg_set_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 will check for % existing definitions and perform assignments globally % (\emph{cf.}~\cs{cs_new:Npn}) whereas the \texttt{set} version will % 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. % % \begin{function}{\prg_new_eq_conditional:NNn, \prg_set_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 % will check for existing definitions (\emph{cf.}~\cs{cs_new:Npn}) whereas % the \texttt{set} version will not (\emph{cf.}~\cs{cs_set:Npn}). 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 `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 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} % % \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 will generally % mean being constructed from predicate functions, 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} will initially be \texttt{false}. % \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 the content of \meta{boolean_1} equal to that of \meta{boolean_2}. % \end{function} % % \begin{function}[updated = 2012-07-08] % {\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}[EXP,pTF]{\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}[added = 2012-02-09, updated = 2015-08-01]{\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 = 2015-08-07]{\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 = 2015-08-03]{\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 = 2015-08-07]{\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} % % \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. % % At present, the infix operators |&&| and \verb"||" perform lazy % evaluation as well, but this will change in a future release. % % \begin{function}[EXP, pTF, updated = 2012-07-08]{\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, updated = 2012-07-08]{\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, updated = 2012-07-08]{\bool_xor_p:nn} % \begin{syntax} % \cs{bool_xor_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2} % \end{syntax} % Implements an \enquote{exclusive or} operation between two boolean % expressions. There is no infix operation for this logical % operator. % \end{function} % % \section{Logical loops} % % Loops using either boolean expressions or stored boolean values. % % \begin{function}[rEXP]{\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} will be inserted into the input % stream again and the process will loop until the \meta{boolean} is % \texttt{true}. % \end{function} % % \begin{function}[rEXP]{\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} will be inserted into the input % stream again and the process will loop until the \meta{boolean} is % \texttt{false}. % \end{function} % % \begin{function}[rEXP]{\bool_until_do:Nn, \bool_until_do:cn} % \begin{syntax} % \cs{bool_until_do:Nn} \meta{boolean} \Arg{code} % \end{syntax} % This function firsts 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 will then loop % until the \meta{boolean} is \texttt{true}. % \end{function} % % \begin{function}[rEXP]{\bool_while_do:Nn, \bool_while_do:cn} % \begin{syntax} % \cs{bool_while_do:Nn} \meta{boolean} \Arg{code} % \end{syntax} % This function firsts 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 will then loop % until the \meta{boolean} is \texttt{false}. % \end{function} % % \begin{function}[rEXP, updated = 2012-07-08]{\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} will be inserted into the input stream again and the % process will loop until the \meta{boolean expression} evaluates to % \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2012-07-08]{\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} will be inserted into the input stream again and the % process will loop until the \meta{boolean expression} evaluates to % \texttt{false}. % \end{function} % % \begin{function}[rEXP, updated = 2012-07-08]{\bool_until_do:nn} % \begin{syntax} % \cs{bool_until_do:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % This function firsts 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 will % then loop until the \meta{boolean expression} is \texttt{true}. % \end{function} % % \begin{function}[rEXP, updated = 2012-07-08]{\bool_while_do:nn} % \begin{syntax} % \cs{bool_while_do:nn} \Arg{boolean expression} \Arg{code} % \end{syntax} % This function firsts 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 will % then loop until the \meta{boolean expression} is \texttt{false}. % \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: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{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} will 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} % % \begin{function}[EXP]{\__prg_break_point:Nn} % \begin{syntax} % \cs{__prg_break_point:Nn} \cs[no-index]{\meta{type}_map_break:} \meta{tokens} % \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. After the loop ends, the % \meta{tokens} are 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]{\__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 is simply % used as a recognizable marker for the \meta{type}. % \end{function} % % \begin{variable}{\g__prg_map_int} % This integer is used by non-expandable mapping functions to track % the level of nesting in force. The functions \cs{__prg_map_1:w}, % \cs{__prg_map_2:w}, \emph{etc.}, labelled by \cs{g__prg_map_int} % hold functions to be mapped over various list datatypes in inline % and variable mappings. % \end{variable} % % \begin{function}[EXP]{\__prg_break_point:} % This copy of \cs{prg_do_nothing:} is used to mark the end of a fast % short-term recursions: the function \cs{__prg_break:n} uses this to % break out of the loop. % \end{function} % % \begin{function}[EXP]{\__prg_break:, \__prg_break:n} % \begin{syntax} % \cs{__prg_break:n} \Arg{tokens} \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 \cs{prop_get:Nn}), and % inserts \meta{tokens} in the input stream. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3prg} implementation} % % \TestFiles{m3prg001.lvt,m3prg002.lvt,m3prg003.lvt} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \subsection{Primitive conditionals} % % \begin{macro}{\if_bool:N} % \begin{macro}{\if_predicate:w} % Those two primitive \TeX{} conditionals are synonyms. % \begin{macrocode} \cs_new_eq:NN \if_bool:N \tex_ifodd:D \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} % { % \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. % \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. % \begin{macrocode} \cs_new_eq:NN \bool_set_eq:NN \cs_set_eq:NN \cs_new_eq:NN \bool_set_eq:Nc \cs_set_eq:Nc \cs_new_eq:NN \bool_set_eq:cN \cs_set_eq:cN \cs_new_eq:NN \bool_set_eq:cc \cs_set_eq:cc \cs_new_eq:NN \bool_gset_eq:NN \cs_gset_eq:NN \cs_new_eq:NN \bool_gset_eq:Nc \cs_gset_eq:Nc \cs_new_eq:NN \bool_gset_eq:cN \cs_gset_eq:cN \cs_new_eq:NN \bool_gset_eq:cc \cs_gset_eq: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}. % \begin{macrocode} \cs_new_protected:Npn \bool_set:Nn #1#2 { \tex_chardef:D #1 = \bool_if_p:n {#2} } \cs_new_protected:Npn \bool_gset:Nn #1#2 { \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} % % Booleans are not based on token lists but do need checking: this % code complements similar material in \pkg{l3tl}. % \begin{macrocode} %<*package> \if_bool:N \l@expl@check@declarations@bool \cs_set_protected:Npn \bool_set_true:N #1 { \__chk_if_exist_var:N #1 \cs_set_eq:NN #1 \c_true_bool } \cs_set_protected:Npn \bool_set_false:N #1 { \__chk_if_exist_var:N #1 \cs_set_eq:NN #1 \c_false_bool } \cs_set_protected:Npn \bool_gset_true:N #1 { \__chk_if_exist_var:N #1 \cs_gset_eq:NN #1 \c_true_bool } \cs_set_protected:Npn \bool_gset_false:N #1 { \__chk_if_exist_var:N #1 \cs_gset_eq:NN #1 \c_false_bool } \cs_set_protected:Npn \bool_set_eq:NN #1 { \__chk_if_exist_var:N #1 \cs_set_eq:NN #1 } \cs_set_protected:Npn \bool_gset_eq:NN #1 { \__chk_if_exist_var:N #1 \cs_gset_eq:NN #1 } \cs_set_protected:Npn \bool_set:Nn #1#2 { \__chk_if_exist_var:N #1 \tex_chardef:D #1 = \bool_if_p:n {#2} } \cs_set_protected:Npn \bool_gset:Nn #1#2 { \__chk_if_exist_var:N #1 \tex_global:D \tex_chardef:D #1 = \bool_if_p:n {#2} } \fi: % % \end{macrocode} % % \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_meaning:w \c_true_bool #1 \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \bool_if_p:N { c } \cs_generate_variant:Nn \bool_if:NT { c } \cs_generate_variant:Nn \bool_if:NF { c } \cs_generate_variant:Nn \bool_if:NTF { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\bool_show:N, \bool_show:c, \bool_show:n} % \begin{macro}[aux]{\@@_to_str:n} % Show the truth value of the boolean, as \texttt{true} or % \texttt{false}. % \begin{macrocode} \cs_new_protected:Npn \bool_show:N #1 { \__msg_show_variable:NNNnn #1 \bool_if_exist:NTF ? { } { > ~ \token_to_str:N #1 = \@@_to_str:n {#1} } } \cs_new_protected:Npn \bool_show:n { \__msg_show_wrap:Nn \@@_to_str:n } \cs_new:Npn \@@_to_str:n #1 { \bool_if:nTF {#1} { true } { false } } \cs_generate_variant:Nn \bool_show:N { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\bool_log:N, \bool_log:c, \bool_log:n} % Redirect output of \cs{bool_show:N} to the log. % \begin{macrocode} \cs_new_protected:Npn \bool_log:N { \__msg_log_next: \bool_show:N } \cs_new_protected:Npn \bool_log:n { \__msg_log_next: \bool_show:n } \cs_generate_variant:Nn \bool_log:N { c } % \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}. 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 GetNotNext % function, which eventually reverses the logic compared to % GetNext. % \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 evaluating the predicates within this sub-expression % and break to the nearest Close. Then return \meta{false}. % \item[\meta{true}Or] Current truth value is true, logical Or % seen, stop evaluating the predicates within this sub-expression % and break to 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} % We introduce an additional Stop operation with the same % semantics as the Close operation. % \begin{description} % \item[\meta{true}Stop] Current truth value is true, return % \meta{true}. % \item[\meta{false}Stop] Current truth value is false, return % \meta{false}. % \end{description} % The reasons for this follow below. % \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} % \begin{macro}[EXP, aux] % { % \@@_if_left_parentheses:wwwn , % \@@_if_right_parentheses:wwwn , % \@@_if_or:wwwn % } % 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 will be closed at the end of the expression parsing % (see |S| below). % % Minimal (\enquote{short-circuit}) evaluation of boolean expressions % requires skipping to the end of the current parenthesized group when % \meta{true}\verb"||" is seen, but to the next \verb"||" or closing % parenthesis when \meta{false}|&&| is seen. To avoid having separate % functions for the two cases, we transform the boolean expression by % doubling each parenthesis and adding parenthesis around each % \verb"||". This ensures that |&&| will bind tighter than \verb"||". % % The replacement is done in three passes, for left and right % parentheses and for \verb"||". At each pass, the part of the % expression that has been transformed is stored before \cs{q_nil}, % the rest lies until the first \cs{q_mark}, followed by an empty % brace group. A trailing marker ensures that the auxiliaries' % delimited arguments will not run-away. As long as the delimiter % matches inside the expression, material is moved before \cs{q_nil} % and we continue. Afterwards, the trailing marker is taken as a % delimiter, |#4| is the next auxiliary, immediately followed by a new % \cs{q_nil} delimiter, which indicates that nothing has been treated % at this pass. The last step calls \cs{@@_if_parse:NNNww} which % cleans up and triggers the evaluation of the expression itself. % \begin{macrocode} \cs_new:Npn \bool_if_p:n #1 { \group_align_safe_begin: \@@_if_left_parentheses:wwwn \q_nil #1 \q_mark { } ( \q_mark { \@@_if_right_parentheses:wwwn \q_nil } ) \q_mark { \@@_if_or:wwwn \q_nil } || \q_mark \@@_if_parse:NNNww \q_stop } \cs_new:Npn \@@_if_left_parentheses:wwwn #1 \q_nil #2 ( #3 \q_mark #4 { #4 \@@_if_left_parentheses:wwwn #1 #2 (( \q_nil #3 \q_mark {#4} } \cs_new:Npn \@@_if_right_parentheses:wwwn #1 \q_nil #2 ) #3 \q_mark #4 { #4 \@@_if_right_parentheses:wwwn #1 #2 )) \q_nil #3 \q_mark {#4} } \cs_new:Npn \@@_if_or:wwwn #1 \q_nil #2 || #3 \q_mark #4 { #4 \@@_if_or:wwwn #1 #2 )||( \q_nil #3 \q_mark {#4} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP, aux]{\@@_if_parse:NNNww} % After removing extra tokens from the transformation phase, start % evaluating. At the end, we will need to finish the special % \texttt{align_safe} group before finally % returning a \cs{c_true_bool} or \cs{c_false_bool} as there might % otherwise be something left in front in the input stream. For this % we call the Stop operation, denoted simply by a |S| following the % last Close operation. % \begin{macrocode} \cs_new:Npn \@@_if_parse:NNNww #1#2#3#4 \q_mark #5 \q_stop { \__bool_get_next:NN \use_i:nn (( #4 )) S } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\@@_get_next:NN} % The GetNext operation. This is a switch: if what follows is neither % |!| nor |(|, we assume it is a predicate. The first argument is % \cs{use_ii:nn} if the logic must eventually be reversed (after a % |!|), otherwise it is \cs{use_i:nn}. 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. % \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}[aux]{\@@_!:Nw} % The Not operation reverses the logic: discard the |!| token and call % the GetNext operation with its first argument reversed. % \begin{macrocode} \cs_new:cpn { @@_!:Nw } #1#2 { \exp_after:wN \@@_get_next:NN #1 \use_ii:nn \use_i:nn } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\@@_(:Nw} % The Open operation starts a sub-expression after discarding the % token. This is done by calling GetNext, with a post-processing step % which looks for And, Or or Close afterwards. % \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:nn } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\@@_p:Nw} % If what follows GetNext is neither |!| nor |(|, evaluate the % predicate using the primitive \cs{__int_value:w}. The canonical % true and 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}[aux]{\@@_choose:NNN} % Branching the eight-way switch. The arguments are 1: \cs{use_i:nn} % or \cs{use_ii:nn}, 2: $0$ or $1$ encoding the current truth value, % 3: the next operation, And, Or, Close or Stop. If |#1| is % \cs{use_ii:nn}, the logic of |#2| must be reversed. % \begin{macrocode} \cs_new:Npn \@@_choose:NNN #1#2#3 { \use:c { @@_ #3 _ #1 #2 { \if_meaning:w 0 #2 1 \else: 0 \fi: } :w } } % \end{macrocode} % \end{macro} % % \begin{macro}[aux] % { % \@@_)_0:w, % \@@_)_1:w, % \@@_S_0:w, % \@@_S_1:w, % } % Closing a group is just about returning the result. The Stop % operation is similar except it closes the special alignment group % before returning the boolean. % \begin{macrocode} \cs_new:cpn { @@_)_0:w } { \c_false_bool } \cs_new:cpn { @@_)_1:w } { \c_true_bool } \cs_new:cpn { @@_S_0:w } { \group_align_safe_end: \c_false_bool } \cs_new:cpn { @@_S_1:w } { \group_align_safe_end: \c_true_bool } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]+\@@_&_1:w+ % \begin{macro}[aux]+\@@_|_0:w+ % Two cases where we simply continue scanning. We must remove the % second |&| or \verb"|". % \begin{macrocode} \cs_new:cpn { @@_&_1:w } & { \@@_get_next:NN \use_i:nn } \cs_new:cpn { @@_|_0:w } | { \@@_get_next:NN \use_i:nn } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[aux]+\@@_&_0:w+ % \begin{macro}[aux]+\@@_|_1:w+ % \begin{macro}[aux] % { % \@@_eval_skip_to_end_auxi:Nw, % \@@_eval_skip_to_end_auxii:Nw, % \@@_eval_skip_to_end_auxiii:Nw % } % When the truth value has already been decided, we have to throw away % the remainder of the current group as we are doing minimal % evaluation. This is slightly tricky as there are no braces so we % have to play match the |()| manually. % \begin{macrocode} \cs_new:cpn { @@_&_0:w } & { \@@_eval_skip_to_end_auxi:Nw \c_false_bool } \cs_new:cpn { @@_|_1:w } | { \@@_eval_skip_to_end_auxi:Nw \c_true_bool } % \end{macrocode} % There is always at least one |)| waiting, namely the outer % one. However, we are facing the problem that there may be more than % one that need to be finished off and we have to detect the correct % number of them. Here is a complicated example showing how this is % done. After evaluating the following, we realize we must skip % everything after the first And. Note the extra Close at the end. % \begin{quote} % |\c_false_bool && ((abc) && xyz) && ((xyz) && (def)))| % \end{quote} % First read up to the first Close. This gives us the list we first % read up until the first right parenthesis so we are looking at the % token list % \begin{quote} % |((abc| % \end{quote} % This contains two Open markers so we must remove two groups. Since % no evaluation of the contents is to be carried out, it doesn't % matter how we remove the groups as long as we wind up with the % correct result. We therefore first remove a |()| pair and what % preceded the Open -- but leave the contents as it may contain Open % tokens itself -- leaving % \begin{quote} % |(abc && xyz) && ((xyz) && (def)))| % \end{quote} % Another round of this gives us % \begin{quote} % |(abc && xyz| % \end{quote} % which still contains an Open so we remove another |()| pair, giving us % \begin{quote} % |abc && xyz && ((xyz) && (def)))| % \end{quote} % Again we read up to a Close and again find Open tokens: % \begin{quote} % |abc && xyz && ((xyz| % \end{quote} % Further reduction gives us % \begin{quote} % |(xyz && (def)))| % \end{quote} % and then % \begin{quote} % |(xyz && (def| % \end{quote} % with reduction to % \begin{quote} % |xyz && (def))| % \end{quote} % and ultimately we arrive at no Open tokens being skipped and we can % finally close the group nicely. % \begin{macrocode} %% ( \cs_new:Npn \@@_eval_skip_to_end_auxi:Nw #1#2 ) { \@@_eval_skip_to_end_auxii:Nw #1#2 ( % ) \q_no_value \q_stop {#2} } % \end{macrocode} % If no right parenthesis, then |#3| is no_value and we are done, return % the boolean |#1|. If there is, we need to grab a |()| pair and then % recurse % \begin{macrocode} \cs_new:Npn \@@_eval_skip_to_end_auxii:Nw #1#2 ( #3#4 \q_stop #5 % ) { \quark_if_no_value:NTF #3 {#1} { \@@_eval_skip_to_end_auxiii:Nw #1 #5 } } % \end{macrocode} % Keep the boolean, throw away anything up to the |(| as it is % irrelevant, remove a |()| pair but remember to reinsert |#3| as it may % contain |(| tokens! % \begin{macrocode} \cs_new:Npn \@@_eval_skip_to_end_auxiii:Nw #1#2 ( #3 ) { % ( \@@_eval_skip_to_end_auxi:Nw #1#3 ) } % \end{macrocode} % \end{macro} % \end{macro} % \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}{\bool_xor_p:nn} % \UnitTested % Exclusive or. If the boolean expressions have same truth value, % return false, otherwise return true. % \begin{macrocode} \cs_new:Npn \bool_xor_p:nn #1#2 { \int_compare:nNnTF { \bool_if_p:n {#1} } = { \bool_if_p:n {#2} } \c_false_bool \c_true_bool } % \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} % % \subsection{Producing multiple copies} % % \begin{macrocode} %<@@=prg> % \end{macrocode} % % \begin{macro}{\prg_replicate:nn} % \UnitTested % \begin{macro}[aux]{\@@_replicate:N, \@@_replicate_first:N} % \begin{macro}[aux]{\@@_replicate_} % \begin{macro}[aux] % { % \@@_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}[aux] % { % \@@_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 will severely affect 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:w #1 \__int_eval_end: \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_kernel_expandable_error:nn { kernel } { 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}[int]{\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 issues 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 will get some % sort of weird error message because the underlying % \tn{futurelet} will store 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 code documented only in Appendix~D of % \emph{The \TeX{}book}\dots % 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} \cs_new:Npn \group_align_safe_begin: { \if_int_compare:w \if_false: { \fi: `} = \c_zero \fi: } \cs_new:Npn \group_align_safe_end: { \if_int_compare:w `{ = \c_zero } \fi: } % \end{macrocode} % \end{macro} % % \begin{macrocode} %<@@=prg> % \end{macrocode} % % \begin{variable}{\g_@@_map_int} % A nesting counter for mapping. % \begin{macrocode} \int_new:N \g_@@_map_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_break_point:Nn} % \begin{macro}{\@@_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}{\@@_break_point:} % \begin{macro}{\@@_break:, \@@_break:n} % Also done in \pkg{l3basics} as in format mode these are needed within % \pkg{l3alloc}. % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex