% \iffalse meta-comment % %% File: l3prg.dtx Copyright (C) 2005-2011 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 released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX3 Project. %% %% ----------------------------------------------------------------------- % %<*driver|package> \RequirePackage{l3names} \GetIdInfo$Id: l3prg.dtx 2915 2011-10-15 21:21:08Z bruno $ {L3 Experimental control structures} % %<*driver> \documentclass[full]{l3doc} \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 % typical states returned are \meta{true} and \meta{false} but other % states are possible, say an \meta{error} state for erroneous % input, \emph{e.g.}, text as input in a function comparing integers. % % \LaTeX3 has two primary forms of conditional flow processing based % on these states. One type is predicate functions that turn the % returned state into a boolean \meta{true} or \meta{false}. For % example, the function |\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 % |\if_predicate:w| or in functions to be described below. The other type % is the kind of functions choosing a particular argument from the % input stream based on the result of the testing as in % |\cs_if_free:NTF| which also takes one argument (the |N|) and then % executes either \meta{true} or \meta{false} depending on the % result. Important to note here is that the arguments are executed % after exiting the underlying |\if...\fi:| structure. % % \section{Defining a set of conditional functions} % % \begin{function} % { % \prg_new_conditional:Npnn, \prg_set_conditional:Npnn, % \prg_new_conditional:Nnn, \prg_set_conditional:Nnn % } % \begin{syntax} % \cs{prg_set_conditional:Npnn} \cs{\meta{name}:\meta{arg spec}} \meta{parameters} \Arg{conditions} \Arg{code} \\ % \cs{prg_set_conditional:Nnn} \cs{\meta{name}:\meta{arg spec}} \Arg{conditions} \Arg{code} % \end{syntax} % These functions creates a family of conditionals using the same % \Arg{code} to perform the test created. 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 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} % % The conditionals are defined by \cs{prg_new_conditional:Npnn} and friends % as: % \begin{itemize} % \item \cs{\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. % \item \cs{\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{\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{\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. If \meta{code} is expandable then % \cs{prg_set_conditional:Npnn} will generate a family of conditionals % which are also expandable. All of the functions are created globally. % % An example can easily clarify matters here: % \begin{verbatim} % \prg_set_conditional:Nnn \foo_if_bar:NN { 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|, % |\foo_if_bar:NNT| but not |\foo_if_bar:NNF| (because |F| is missing from % the \meta{conds} list). The return statements % take care of resolving the remaining |\else:| and |\fi:| before % returning the state. There must be a return statement for each % branch, failing to do so will result in an error if that branch is % executed. % % \begin{function} % { % \prg_new_protected_conditional:Npnn, \prg_set_protected_conditional:Npnn, % \prg_new_protected_conditional:Nnn, \prg_set_protected_conditional:Nnn % } % \begin{syntax} % \cs{prg_set_protected_conditional:Npnn} % ~~\cs{\meta{name}:\meta{arg spec}} \meta{parameters} \meta{conditions} \Arg{code} \\ % \cs{prg_set_protected_conditional:Nnn} % ~~\cs{\meta{name}:\meta{arg spec}} \meta{conditions} \Arg{code} % \end{syntax} % These functions creates a family of conditionals using the same % \Arg{code} to perform the test created. 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 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}. % \end{function} % % The conditionals are defined by \cs{prg_new_protected_conditional:Npnn} % and friends as: % \begin{itemize} % \item \cs{\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{\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{\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:Npn}: 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. \cs{prg_set_protected_conditional:Npn} will generate % a family of protected conditional functions, and so \meta{code} % does not need to be expandable. All of the functions are created % globally.% % \begin{function}{\prg_new_eq_conditional:NN, \prg_set_eq_conditional:NN} % \begin{syntax} % \cs{prg_new_eq_conditional:NN} \cs{\meta{name1}:\meta{arg spec1}} \cs{\meta{name2}:\meta{arg spec2}} % \end{syntax} % These will set the definitions of the functions % \begin{itemize}\parskip=0pt % \item \cs{\meta{name1}_p:\meta{arg spec1}} % \item \cs{\meta{name1}:\meta{arg spec1}T} % \item \cs{\meta{name1}:\meta{arg spec1}F} % \item \cs{\meta{name1}:\meta{arg spec1}TF} % \end{itemize} % equal to those for % \begin{itemize}\parskip=0pt % \item \cs{\meta{name2}_p:\meta{arg spec2}} % \item \cs{\meta{name2}:\meta{arg spec2}T} % \item \cs{\meta{name2}:\meta{arg spec2}F} % \item \cs{\meta{name2}:\meta{arg spec2}TF} % \end{itemize} % In most cases, the two \meta{arg specs} will be identical, although % this is not enforced. In the case of the \texttt{new} function, a % check is made for any existing definitions for \meta{name1}. The % functions are set globally. % \end{function} % % \begin{function}[EXP]{\prg_return_true:, \prg_return_false:} % \begin{syntax} % \cs{prg_return_true:} % \cs{prg_return_false:} % \end{syntax} % These functions define the logical state at the end of a conditional. % As such, they should appear within the code for a conditional % statement generated by \cs{prg_set_conditional:Npnn}, \emph{etc}. % \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 |\if_predicate:w| test. The problem of the % primitive |\if_false:| and |\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: |\c_true_bool| or % |\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 are expandable and expect the % input to also be fully expandable (which will generally mean being % constructed from predicate functions, possibly nested). % % \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} % \begin{syntax} % \cs{bool_set_false:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{false} within the current % \TeX{} group. % \end{function} % % \begin{function}{\bool_gset_false:N, \bool_gset_false:c} % \begin{syntax} % \cs{bool_sget_false:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{false} globally. % \end{function} % % \begin{function}{\bool_set_true:N, \bool_set_true:c} % \begin{syntax} % \cs{bool_set_true:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{true} within the current % \TeX{} group. % \end{function} % % \begin{function}{\bool_gset_true:N, \bool_gset_true:c} % \begin{syntax} % \cs{bool_gset_true:N} \meta{boolean} % \end{syntax} % Sets \meta{boolean} logically \texttt{true} globally. % \end{function} % % \begin{function} % {\bool_set_eq:NN, \bool_set_eq:cN, \bool_set_eq:Nc, \bool_set_eq:cc} % \begin{syntax} % \cs{bool_set_eq:NN} \meta{boolean1} \meta{boolean2} % \end{syntax} % Sets the content of \meta{boolean1} equal to that of \meta{boolean2}. % This assignment is restricted to the current \TeX{} group level. % \end{function} % % \begin{function} % {\bool_gset_eq:NN, \bool_gset_eq:cN, \bool_gset_eq:Nc, \bool_gset_eq:cc} % \begin{syntax} % \cs{bool_gset_eq:NN} \meta{boolean1} \meta{boolean2} % \end{syntax} % Sets the content of \meta{boolean1} equal to that of \meta{boolean2}. % This assignment is global and so is not limited by the current % \TeX{} group level. % \end{function} % % \begin{function}{\bool_set:Nn, \bool_set:cn} % \begin{syntax} % \cs{bool_set:Nn} \meta{boolean} \Arg{boolexpr} % \end{syntax} % Evaluates the \meta{boolean expression} as described for % \cs{bool_if:n(TF)}, and sets the \meta{boolean} variable to % the logical truth of this evaluation. This assignment is local. % \end{function} % % \begin{function}{\bool_gset:Nn, \bool_gset:cn} % \begin{syntax} % \cs{bool_gset:Nn} \meta{boolean} \Arg{boolexpr} % \end{syntax} % Evaluates the \meta{boolean expression} as described for % \cs{bool_if:n(TF)}, and sets the \meta{boolean} variable to % the logical truth of this evaluation. This assignment is global. % \end{function} % % \begin{function}[EXP,pTF]{\bool_if:N, \bool_if:c} % \begin{syntax} % \cs{bool_if_p:N} \Arg{boolean} % \cs{bool_if:NTF} \Arg{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{variable}{\l_tmpa_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} % 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 |&&|, \verb"||" and |!|. 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 } || % \int_compare_p:n { 1 = \error } % is skipped % ) && % ! ( \int_compare_p:n { 2 = 4 } ) % \end{verbatim} % is a valid boolean expression. Note that minimal evaluation is % carried out whenever possible so that whenever a truth value cannot % be changed any more, the remaining tests within the current group % are skipped. % % \begin{function}[EXP,pTF]{\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. Minimal evaluation is used % in the processing, so that once a result is defined there is % not further expansion of the tests. For example % \begin{verbatim} % \bool_if_p:n % { % \int_compare_p:nNn { 1 } = { 1 } % && % ( % \int_compare_p:nNn { 2 } = { 3 } || % \int_compare_p:nNn { 4 } = { 4 } || % \int_compare_p:nNn { 1 } = { \error } % is skipped % ) % && % ! ( \int_compare_p:nNn { 2 } = { 4 } ) % } % \end{verbatim} % will be \texttt{true} and will not evaluate % |\int_compare_p:nNn { 1 } = { \error }|. The logical Not applies to % the next single predicate or group. As shown above, this means that % any predicates requiring an argument have to be given within % parentheses. % \end{function} % % \begin{function}[EXP]{\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]{\bool_xor_p:nn} % \begin{syntax} % \cs{bool_xor_p:nn} \Arg{boolexpr1} \Arg{boolexpr1} % \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_until_do:Nn, \bool_until_do:cn} % \begin{syntax} % \cs{bool_until_do:Nn} \Arg{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} \Arg{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]{\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]{\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{Switching by case} % % For cases where a number of cases need to be considered a family of % case-selecting functions are available. % % \begin{function}[updated = 2011-09-17, EXP]{\prg_case_int:nnn} % \begin{syntax} % \cs{prg_case_int:nnn} \Arg{test integer expression} \\ % ~~|{| \\ % ~~~~\Arg{intexpr case1} \Arg{code case1} \\ % ~~~~\Arg{intexpr case2} \Arg{code case2} \\ % ~~~~\ldots \\ % ~~~~\Arg{intexpr case$_n$} \Arg{code case$_n$} \\ % ~~|}| \\ % ~~\Arg{else case} % \end{syntax} % This function evaluates the \meta{test integer expression} and % compares this in turn to each of the % \meta{integer expression cases}. If the two are equal then the % associated \meta{code} is left in the input stream. If none of % the tests are \texttt{true} then the \texttt{else code} will be % left in the input stream. % \end{function} % % As an example of \cs{prg_case_int:nnn}: % \begin{verbatim} % \prg_case_int:nnn % { 2 * 5 } % { % { 5 } { Small } % { 4 + 6 } { Medium } % { -2 * 10 } { Negative } % } % { No idea! } % \end{verbatim} % will leave \enquote{\texttt{Medium}} in the input stream. % % \begin{function}[updated = 2011-07-06, EXP]{\prg_case_dim:nnn} % \begin{syntax} % \cs{prg_case_dim:nnn} \Arg{test dimension expression} \\ % ~~|{| \\ % ~~~~\Arg{dimexpr case1} \Arg{code case1} \\ % ~~~~\Arg{dimexpr case2} \Arg{code case2} \\ % ~~~~\ldots \\ % ~~~~\Arg{dimexpr case$_n$} \Arg{code case$_n$} \\ % ~~|}| \\ % ~~\Arg{else case} % \end{syntax} % This function evaluates the \meta{test dimension expression} and % compares this in turn to each of the % \meta{dimension expression cases}. If the two are equal then the % associated \meta{code} is left in the input stream. If none of % the tests are \texttt{true} then the \texttt{else code} will be % left in the input stream. % \end{function} % % \begin{function}[updated = 2011-09-17, EXP] % {\prg_case_str:nnn, \prg_case_str:onn, \prg_case_str:xxn} % \begin{syntax} % \cs{prg_case_str:nnn} \Arg{test string} \\ % ~~|{| \\ % ~~~~\Arg{string case1} \Arg{code case1} \\ % ~~~~\Arg{string case2} \Arg{code case2} \\ % ~~~~\ldots \\ % ~~~~\Arg{string case$_n$} \Arg{code case$_n$} \\ % ~~|}| \\ % ~~\Arg{else case} % \end{syntax} % This function compares the \meta{test string} in turn with each % of the \meta{string cases}. If the two are equal (as described for % \cs{str_if_eq:nnTF} then the % associated \meta{code} is left in the input stream. If none of % the tests are \texttt{true} then the \texttt{else code} will be % left in the input stream. The |xx| variant is fully expandable, % in the same way as the underlying \cs{str_if_eq:xxTF} test. % \end{function} % % \begin{function}[updated = 2011-09-17, EXP] % {\prg_case_tl:Nnn, \prg_case_tl:cnn} % \begin{syntax} % \cs{prg_case_tl:Nnn} \meta{test token list variable} \\ % ~~"{" \\ % ~~~~\meta{token list variable case1} \Arg{code case1} \\ % ~~~~\meta{token list variable case2} \Arg{code case2} \\ % ~~~~\ldots \\ % ~~~~\meta{token list variable case$_n$} \Arg{code case$_n$} \\ % ~~"}" \\ % ~~\Arg{else case} % \end{syntax} % This function compares the \meta{test token list variable} in turn % with each of the \meta{token list variable cases}. If the two % are equal (as described for % \cs{tl_if_eq:nnTF} % then the associated \meta{code} is left in the input % stream. If none of the tests are \texttt{true} then the % \texttt{else code} will be left in the input stream. % \end{function} % % \section{Producing $n$ 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} % % \begin{function}[updated = 2011-09-06,rEXP]{\prg_stepwise_function:nnnN} % \begin{syntax} % \cs{prg_stepwise_function:nnnN} \Arg{initial value} \Arg{step} \Arg{final value} \meta{function} % \end{syntax} % This function first evaluates the \meta{initial value}, \meta{step} % and \meta{final value}, all of which should be integer expressions. % The \meta{function} is then placed in front of each \meta{value} % from the \meta{initial value} to the \meta{final value} in turn % (using \meta{step} between each \meta{value}). Thus \meta{function} % should absorb one numerical argument. For example % \begin{verbatim} % \cs_set_nopar:Npn \my_func:n #1 { [I~saw~#1] \quad } % \prg_stepwise_function:nnnN { 1 } { 5 } { 1 } \my_func:n % \end{verbatim} % would print % \begin{quote} % [I saw 1] \quad % [I saw 2] \quad % [I saw 3] \quad % [I saw 4] \quad % [I saw 5] \quad % \end{quote} % \end{function} % % \begin{function}[updated = 2011-09-06]{\prg_stepwise_inline:nnnn} % \begin{syntax} % \cs{prg_stepwise_inline:nnnn} \Arg{initial value} \Arg{step} \Arg{final value} \Arg{code} % \end{syntax} % This function first evaluates the \meta{initial value}, \meta{step} % and \meta{final value}, all of which should be integer expressions. % The \meta{code} is then placed in front of each \meta{value} % from the \meta{initial value} to the \meta{final value} in turn % (using \meta{step} between each \meta{value}). Thus the \meta{code} % should define a function of one argument (|#1|). % \end{function} % % \begin{function}[updated = 2011-09-06]{\prg_stepwise_variable:nnnNn} % \begin{syntax} % \cs{prg_stepwise_variable:nnnNn} \\ % ~~\Arg{initial value} \Arg{step} \Arg{final value} \meta{tl~var} \Arg{code} % \end{syntax} % This function first evaluates the \meta{initial value}, \meta{step} % and \meta{final value}, all of which should be integer expressions. % The \meta{code} is inserted into the input stream, with the % \meta{tl~var} defined as the current \meta{value}. Thus the % \meta{code} should make use of the \meta{tl~var}. % \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{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}[updated = 2011-09-06]{\scan_align_safe_stop:} % \begin{syntax} % \cs{scan_align_safe_stop:} % \end{syntax} % Stops \TeX{}'s scanner looking for expandable control sequences at % the beginning of an alignment cell. This function is required, for % example, to obtain the expected output when testing \cs{mode_if_math:TF} % at the start of a math array cell: placing % \cs{scan_align_safe_stop:} before \cs{mode_if_math:TF} will give the % correct result. This function does not destroy any kerning if used in % other locations, but \emph{does} render functions non-expandable. % \begin{texnote} % This is a protected version of \cs{prg_do_nothing:}, which therefore % stops \TeX{}'s scanner in the circumstances described without producing % any affect on the output. % \end{texnote} % \end{function} % % \begin{function}[EXP]{\prg_variable_get_scope:N} % \begin{syntax} % \cs{prg_variable_get_scope:N} \meta{variable} % \end{syntax} % Returns the scope (\texttt{g} for global, blank otherwise) for the % \meta{variable}. % \end{function} % % \begin{function}[EXP]{\prg_variable_get_type:N} % \begin{syntax} % \cs{prg_variable_get_type:N} \meta{variable} % \end{syntax} % Returns the type of \meta{variable} (\texttt{tl}, \texttt{int}, % \emph{etc.}) % \end{function} % % \begin{function}[EXP]{\if_predicate:w} % \begin{syntax} % "\if_predicate:w" "\else:" "\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 but to make the % coding clearer this should be done through "\if_bool:N".) % \end{function} % % \begin{function}[EXP]{\if_bool:N} % \begin{syntax} % "\if_bool:N" "\else:" "\fi:" % \end{syntax} % This function takes a boolean variable and % branches according to the result. % \end{function} % % \section{Experimental programmings functions} % % \begin{function}{\prg_quicksort:n} % \begin{syntax} % \cs{prg_quicksort:n} |{| \Arg{item~1} \Arg{item~2} \dots \Arg{item~n} |}| % \end{syntax} % Performs a quicksort on the token list. The comparisons are % performed by the function \cs{prg_quicksort_compare:nnTF} which is up % to the programmer to define. When the sorting process is over, all % items are given as argument to the function % \cs{prg_quicksort_function:n} which the programmer also controls. % \end{function} % % \begin{function}{ % \prg_quicksort_function:n , % \prg_quicksort_compare:nnTF % } % \begin{syntax} % "\prg_quicksort_function:n" \Arg{element} \\ % "\prg_quicksort_compare:nnTF" \Arg{element 1} \Arg{element 2}\\ % \end{syntax} % The two functions the programmer must define before calling % |\prg_quicksort:n|. As an example we could define % \begin{quote} % |\cs_set_nopar:Npn\prg_quicksort_function:n #1{{#1}}|\\ % |\cs_set_nopar:Npn\prg_quicksort_compare:nnTF #1#2 {\int_compare:nNnTF{#1}>{#2}}| % \end{quote} % Then the function call % \begin{quote} % |\prg_quicksort:n {876234520}| % \end{quote} % would return |{0}{2}{2}{3}{4}{5}{6}{7}{8}|. An alternative example % where one sorts a list of words, |\prg_quicksort_compare:nnTF| could % be defined as % \begin{quote} % |\cs_set_nopar:Npn\prg_quicksort_compare:nnTF #1#2 {|\\ % | \int_compare:nNnTF{\tl_compare:nn{#1}{#2}}>\c_zero }| % \end{quote} % % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3prg} implementation} % % \TestFiles{m3prg001.lvt,m3prg002.lvt,m3prg003.lvt} %% % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<*package> \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} \package_check_loaded_expl: % % \end{macrocode} % % \subsection{Primitive conditionals} % % \begin{macro}{\if_bool:N} % \begin{macro}{\if_predicate:w} % Those two primitive \TeX{} conditionals are synonyms. % They should not be used outside the kernel code. % \begin{macrocode} \tex_let:D \if_bool:N \tex_ifodd:D \tex_let:D \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 that that is the case! % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{The boolean data type} % % \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_nopar: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_nopar:Npn \bool_set_true:N #1 { \cs_set_eq:NN #1 \c_true_bool } \cs_new_protected_nopar:Npn \bool_set_false:N #1 { \cs_set_eq:NN #1 \c_false_bool } \cs_new_protected_nopar:Npn \bool_gset_true:N #1 { \cs_gset_eq:NN #1 \c_true_bool } \cs_new_protected_nopar: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 |\c_true_bool| or |\c_false_bool|. % \begin{macrocode} \cs_new:Npn \bool_set:Nn #1#2 { \tex_chardef:D #1 = \bool_if_p:n {#2} } \cs_new: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} % % \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} % \end{macro} % % \begin{variable}{\l_tmpa_bool, \g_tmpa_bool} % A few booleans just if you need them. % \begin{macrocode} \bool_new:N \l_tmpa_bool \bool_new:N \g_tmpa_bool % \end{macrocode} % \end{variable} % % \subsection{Boolean expressions} % % \begin{macro}[pTF]{\bool_if:n} % \UnitTested % \begin{macro}[aux]{\bool_get_next:N} % \begin{macro}[aux]{\bool_cleanup:N} % \begin{macro}[aux]{\bool_choose:NN} % \begin{macro}[aux] % { % bool_!:w, % \bool_Not:w, % \bool_Not:w, % \bool_(:w, % \bool_p:w, % \bool_8_1:w, % \bool_I_1:w, % \bool_8_0:w, % \bool_I_0:w, % \bool_)_0:w, % \bool_)_1:w, % \bool_S_0:w, % \bool_S_1:w % } % \begin{macro}[aux] % { % \bool_eval_skip_to_end:Nw, \bool_eval_skip_to_end_aux:Nw, % \bool_eval_skip_to_end_aux_ii:Nw % } % 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, insert a negating function (if-even in % this case) and call GetNext. % \item If none of the above, start evaluating a new expression by % reinserting the token found (this is supposed to be a predicate % function) in front of Eval. % \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 following % semantics: % \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. % % Now for how these works in practice. The canonical true and false % values have numerical values $1$ and $0$ respectively. We evaluate % this using the primitive |\int_value:w:D| operation. First we % issue a |\group_align_safe_begin:| as we are using |&&| as syntax % shorthand for the And operation and we need to hide it for \TeX{}. % We also need to finish this special group before finally % returning a |\c_true_bool| or |\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} \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: } \cs_new:Npn \bool_if_p:n #1 { \group_align_safe_begin: \bool_get_next:N ( #1 ) S } % \end{macrocode} % The GetNext operation. We make it a switch: If not a |!| or |(|, we % assume it is a predicate. % \begin{macrocode} \cs_new:Npn \bool_get_next:N #1 { \use:c { bool_ \if_meaning:w !#1 ! \else: \if_meaning:w (#1 ( \else: p \fi: \fi: :w } #1 } % \end{macrocode} % This variant gets called when a Not has just been entered. % It (eventually) results in a reversal of the logic of the directly % following material. % \begin{macrocode} \cs_new:Npn \bool_get_not_next:N #1 { \use:c { bool_not_ \if_meaning:w !#1 ! \else: \if_meaning:w (#1 ( \else: p \fi: \fi: :w } #1 } % \end{macrocode} % We need these later on to nullify the unity operation |!!|. % \begin{macrocode} \cs_new:Npn \bool_get_next:NN #1#2 { \bool_get_next:N #2 } \cs_new:Npn \bool_get_not_next:NN #1#2 { \bool_get_not_next:N #2 } % \end{macrocode} % The Not operation. Discard the token read and reverse the truth % value of the next expression if there % are brackets; otherwise % if we're coming up to a |!| then we don't need to reverse anything % (but we then want to continue scanning ahead in case some fool has written % |!!(...)|); % otherwise we have a boolean that we can reverse here and now. % \begin{macrocode} \cs_new:cpn { bool_!:w } #1#2 { \if_meaning:w ( #2 \exp_after:wN \bool_Not:w \else: \if_meaning:w ! #2 \exp_after:wN \exp_after:wN \exp_after:wN \bool_get_next:NN \else: \exp_after:wN \exp_after:wN \exp_after:wN \bool_Not:N \fi: \fi: #2 } % \end{macrocode} % Variant called when already inside a Not. % Essentially the opposite of the above. % \begin{macrocode} \cs_new:cpn { bool_not_!:w } #1#2 { \if_meaning:w ( #2 \exp_after:wN \bool_not_Not:w \else: \if_meaning:w ! #2 \exp_after:wN \exp_after:wN \exp_after:wN \bool_get_not_next:NN \else: \exp_after:wN \exp_after:wN \exp_after:wN \bool_not_Not:N \fi: \fi: #2 } % \end{macrocode} % These occur when processing |!(...)|. The idea is to use a variant % of |\bool_get_next:N| that finishes its parsing with a logic reversal. % Of course, the double logic reversal gets us back to where we started. % \begin{macrocode} \cs_new:Npn \bool_Not:w { \exp_after:wN \int_value:w \bool_get_not_next:N } \cs_new:Npn \bool_not_Not:w { \exp_after:wN \int_value:w \bool_get_next:N } % \end{macrocode} % These occur when processing |!| and can be evaluated directly. % \begin{macrocode} \cs_new:Npn \bool_Not:N #1 { \exp_after:wN \bool_p:w \if_meaning:w #1 \c_true_bool \c_false_bool \else: \c_true_bool \fi: } \cs_new:Npn \bool_not_Not:N #1 { \exp_after:wN \bool_p:w \if_meaning:w #1 \c_true_bool \c_true_bool \else: \c_false_bool \fi: } % \end{macrocode} % The Open operation. Discard the token read and start a % sub-expression. % |\bool_get_next:N| continues building up the logical expressions as usual; % |\bool_not_cleanup:N| is what reverses the logic if we're inside |!(...)|. % \begin{macrocode} \cs_new:cpn { bool_(:w } #1 { \exp_after:wN \bool_cleanup:N \int_value:w \bool_get_next:N } \cs_new:cpn { bool_not_(:w } #1 { \exp_after:wN \bool_not_cleanup:N \int_value:w \bool_get_next:N } % \end{macrocode} % Otherwise just evaluate the predicate and look for And, Or or Close % afterwards. % \begin{macrocode} \cs_new:cpn { bool_p:w } { \exp_after:wN \bool_cleanup:N \int_value:w } \cs_new:cpn { bool_not_p:w } {\exp_after:wN \bool_not_cleanup:N \int_value:w } % \end{macrocode} % This cleanup function can be omitted once predicates return their % true/false booleans outside the conditionals. % \begin{macrocode} \cs_new:Npn \bool_cleanup:N #1 { \exp_after:wN \bool_choose:NN \exp_after:wN #1 \int_to_roman:w - `\q } \cs_new:Npn \bool_not_cleanup:N #1 { \exp_after:wN \bool_not_choose:NN \exp_after:wN #1 \int_to_roman:w - `\q } % \end{macrocode} % Branching the six way switch. % Reversals should be reasonably straightforward. % \begin{macrocode} \cs_new_nopar:Npn \bool_choose:NN #1#2 { \use:c { bool_ #2 _ #1 :w } } \cs_new_nopar:Npn \bool_not_choose:NN #1#2 { \use:c { bool_not_ #2 _ #1 :w } } % \end{macrocode} % Continues scanning. Must remove the second "&" or \verb"|". % \begin{macrocode} \cs_new_nopar:cpn { bool_&_1:w } & { \bool_get_next:N } \cs_new_nopar:cpn { bool_|_0:w } | { \bool_get_next:N } \cs_new_nopar:cpn { bool_not_&_0:w } & { \bool_get_next:N } \cs_new_nopar:cpn { bool_not_|_1:w } | { \bool_get_next:N } % \end{macrocode} % 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_nopar:cpn { bool_)_0:w } { \c_false_bool } \cs_new_nopar:cpn { bool_)_1:w } { \c_true_bool } \cs_new_nopar:cpn { bool_not_)_0:w } { \c_true_bool } \cs_new_nopar:cpn { bool_not_)_1:w } { \c_false_bool } \cs_new_nopar:cpn { bool_S_0:w } { \group_align_safe_end: \c_false_bool } \cs_new_nopar:cpn { bool_S_1:w } { \group_align_safe_end: \c_true_bool } % \end{macrocode} % 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_nopar:cpn { bool_&_0:w } & { \bool_eval_skip_to_end:Nw \c_false_bool } \cs_new_nopar:cpn { bool_|_1:w } | { \bool_eval_skip_to_end:Nw \c_true_bool } \cs_new_nopar:cpn { bool_not_&_1:w } & { \bool_eval_skip_to_end:Nw \c_false_bool } \cs_new_nopar:cpn { bool_not_|_0:w } | { \bool_eval_skip_to_end: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 \bool_eval_skip_to_end:Nw #1#2 ) { \bool_eval_skip_to_end_aux: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 \bool_eval_skip_to_end_aux:Nw #1#2 ( #3#4 \q_stop #5 % ) { \quark_if_no_value:NTF #3 {#1} { \bool_eval_skip_to_end_aux_ii: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 \bool_eval_skip_to_end_aux_ii:Nw #1#2 ( #3 ) { % ( \bool_eval_skip_to_end:Nw #1#3 ) } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\bool_not_p:n} % \UnitTested % The Not variant just reverses the outcome of |\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{Switching by case} % % A family of functions to select one case of a number: the same ideas % are used for a number of different situations. % % \begin{macro}[aux]{\prg_case_end:nw} % In all cases the end statement is the same. Here, |#1| will be the % code needed, |#2| the other cases to throw away, including the % \enquote{else} case. The \cs{c_zero} marker stops the expansion of % \tn{romannumeral} which begins each \cs{prg_case_...} function. % \begin{macrocode} \cs_new:Npn \prg_case_end:nw #1 #2 \q_recursion_stop { \c_zero #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\prg_case_int:nnn} % \UnitTested % \begin{macro}[aux]{\prg_case_int_aux:nnn,\prg_case_int_aux:nw} % For integer cases, the first task to fully expand the check % condition. After that, a loop is started to compare each possible % value and stop if the test is true. The tested value is put at the % end to ensure that there is necessarily a match, which will fire the % \enquote{else} pathway. The leading \tn{romannumeral} triggers an % expansion which is then stopped in \cs{prg_case_end:nw}. % \begin{macrocode} \cs_new:Npn \prg_case_int:nnn #1 { \tex_romannumeral:D \exp_args:Nf \prg_case_int_aux:nnn { \int_eval:n {#1} } } \cs_new:Npn \prg_case_int_aux:nnn #1 #2 #3 { \prg_case_int_aux:nw {#1} #2 {#1} {#3} \q_recursion_stop } \cs_new:Npn \prg_case_int_aux:nw #1#2#3 { \int_compare:nNnTF {#1} = {#2} { \prg_case_end:nw {#3} } { \prg_case_int_aux:nw {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prg_case_dim:nnn} % \UnitTested % \begin{macro}[aux]{\prg_case_dim_aux:nnn,\prg_case_dim_aux:nw} % The dimension function is the same, just a change of calculation % method. % \begin{macrocode} \cs_new:Npn \prg_case_dim:nnn #1 { \tex_romannumeral:D \exp_args:Nf \prg_case_dim_aux:nnn { \dim_eval:n {#1} } } \cs_new:Npn \prg_case_dim_aux:nnn #1 #2 #3 { \prg_case_dim_aux:nw {#1} #2 {#1} {#3} \q_recursion_stop } \cs_new:Npn \prg_case_dim_aux:nw #1#2#3 { \dim_compare:nNnTF {#1} = {#2} { \prg_case_end:nw {#3} } { \prg_case_dim_aux:nw {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prg_case_str:nnn, \prg_case_str:onn, \prg_case_str:xxn} % \UnitTested % \begin{macro}[aux]{\prg_case_str_aux:nw, \prg_case_str_x_aux:nw} % No calculations for strings, otherwise no surprises. % \begin{macrocode} \cs_new:Npn \prg_case_str:nnn #1#2#3 { \tex_romannumeral:D \prg_case_str_aux:nw {#1} #2 {#1} {#3} \q_recursion_stop } \cs_new:Npn \prg_case_str_aux:nw #1#2#3 { \str_if_eq:nnTF {#1} {#2} { \prg_case_end:nw {#3} } { \prg_case_str_aux:nw {#1} } } \cs_generate_variant:Nn \prg_case_str:nnn { o } \cs_new:Npn \prg_case_str:xxn #1#2#3 { \tex_romannumeral:D \prg_case_str_x_aux:nw {#1} #2 {#1} {#3} \q_recursion_stop } \cs_new:Npn \prg_case_str_x_aux:nw #1#2#3 { \str_if_eq:xxTF {#1} {#2} { \prg_case_end:nw {#3} } { \prg_case_str_x_aux:nw {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prg_case_tl:Nnn, \prg_case_tl:cnn} % \UnitTested % \begin{macro}[aux]{\prg_case_tl_aux:Nw} % Similar again, but this time with some variants. % \begin{macrocode} \cs_new:Npn \prg_case_tl:Nnn #1#2#3 { \tex_romannumeral:D \prg_case_tl_aux:Nw #1 #2 #1 {#3} \q_recursion_stop } \cs_new:Npn \prg_case_tl_aux:Nw #1#2#3 { \tl_if_eq:NNTF #1 #2 { \prg_case_end:nw {#3} } { \prg_case_tl_aux:Nw #1 } } \cs_generate_variant:Nn \prg_case_tl:Nnn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Producing $n$ copies} % % \begin{macro}{\prg_replicate:nn} % \UnitTested % \begin{macro}[aux]{\prg_replicate_aux:N, \prg_replicate_first_aux:N} % \begin{macro}[aux]{\prg_replicate_} % \begin{macro}[aux] % { % \prg_replicate_0:n, % \prg_replicate_1:n, % \prg_replicate_2:n, % \prg_replicate_3:n, % \prg_replicate_4:n, % \prg_replicate_5:n, % \prg_replicate_6:n, % \prg_replicate_7:n, % \prg_replicate_8:n, % \prg_replicate_9:n % } % \begin{macro}[aux] % { % \prg_replicate_first_-:n, % \prg_replicate_first_0:n, % \prg_replicate_first_1:n, % \prg_replicate_first_2:n, % \prg_replicate_first_3:n, % \prg_replicate_first_4:n, % \prg_replicate_first_5:n, % \prg_replicate_first_6:n, % \prg_replicate_first_7:n, % \prg_replicate_first_8:n, % \prg_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{int_to_roman: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 % |\prg_replicate:nn{1000}{\prg_replicate:nn{1000}{|\meta{code}|}}|. An % alternative approach is to create a string of |m|'s with % \cs{int_to_roman: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_nopar:Npn \prg_replicate:nn #1 { \int_to_roman:w \exp_after:wN \prg_replicate_first_aux:N \int_value:w \int_eval:w #1 \int_eval_end: \cs_end: } \cs_new_nopar:Npn \prg_replicate_aux:N #1 { \cs:w prg_replicate_#1 :n \prg_replicate_aux:N } \cs_new_nopar:Npn \prg_replicate_first_aux:N #1 { \cs:w prg_replicate_first_ #1 :n \prg_replicate_aux:N } % \end{macrocode} % Then comes all the functions that do the hard work of inserting all % the copies. % \begin{macrocode} \cs_new_nopar:Npn \prg_replicate_ :n #1 { \cs_end: } \cs_new:cpn { prg_replicate_0:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} } \cs_new:cpn { prg_replicate_1:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1 } \cs_new:cpn { prg_replicate_2:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1 } \cs_new:cpn { prg_replicate_3:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1 } \cs_new:cpn { prg_replicate_4:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1 } \cs_new:cpn { prg_replicate_5:n } #1 { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1 } \cs_new:cpn { prg_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 { prg_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 { prg_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 { prg_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 { prg_replicate_first_-:n } #1 { \c_zero \negative_replication } \cs_new:cpn { prg_replicate_first_0:n } #1 { \c_zero } \cs_new:cpn { prg_replicate_first_1:n } #1 { \c_zero #1 } \cs_new:cpn { prg_replicate_first_2:n } #1 { \c_zero #1#1 } \cs_new:cpn { prg_replicate_first_3:n } #1 { \c_zero #1#1#1 } \cs_new:cpn { prg_replicate_first_4:n } #1 { \c_zero #1#1#1#1 } \cs_new:cpn { prg_replicate_first_5:n } #1 { \c_zero #1#1#1#1#1 } \cs_new:cpn { prg_replicate_first_6:n } #1 { \c_zero #1#1#1#1#1#1 } \cs_new:cpn { prg_replicate_first_7:n } #1 { \c_zero #1#1#1#1#1#1#1 } \cs_new:cpn { prg_replicate_first_8:n } #1 { \c_zero #1#1#1#1#1#1#1#1 } \cs_new:cpn { prg_replicate_first_9:n } #1 { \c_zero #1#1#1#1#1#1#1#1#1 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\prg_stepwise_function:nnnN} % \begin{macro}[aux] % {\prg_stepwise_function_incr:nnnN, \prg_stepwise_function_decr:nnnN} % Repeating a function by steps fist needs a check on the direction % of the steps. After that, do the function for the start value % then step and loop around. % \begin{macrocode} \cs_new:Npn \prg_stepwise_function:nnnN #1#2#3#4 { \int_compare:nNnTF {#2} = \c_zero { \msg_expandable_error:n { Zero~step~size~for~stepwise~function. } } { \int_compare:nNnTF {#2} > \c_zero { \exp_args:Nf \prg_stepwise_function_incr:nnnN } { \exp_args:Nf \prg_stepwise_function_decr:nnnN } { \int_eval:n {#1} } {#2} {#3} #4 } } \cs_new:Npn \prg_stepwise_function_incr:nnnN #1#2#3#4 { \int_compare:nNnF {#1} > {#3} { #4 {#1} \exp_args:Nf \prg_stepwise_function_incr:nnnN { \int_eval:n { #1 + #2 } } {#2} {#3} #4 } } \cs_new:Npn \prg_stepwise_function_decr:nnnN #1#2#3#4 { \int_compare:nNnF {#1} < {#3} { #4 {#1} \exp_args:Nf \prg_stepwise_function_decr:nnnN { \int_eval:n { #1 + #2 } } {#2} {#3} #4 } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[aux]{\g_prg_stepwise_level_int} % For nesting, the usual approach of using a counter. % \begin{macrocode} \int_new:N \g_prg_stepwise_level_int % \end{macrocode} % \end{macro} % % \begin{macro}{\prg_stepwise_inline:nnnn} % \begin{macro}{\prg_stepwise_variable:nnnNn} % \UnitTested % \begin{macro}[aux]{\prg_stepwise_aux:NNnnnn} % The approach here is to build a function, with a global integer required % to make the nesting safe (as seen in other in line functions), % and map that function using \cs{prg_stepwise_function:nnnN}. % \begin{macrocode} \cs_new_protected:Npn \prg_stepwise_inline:nnnn { \exp_args:NNc \prg_stepwise_aux:NNnnnn \cs_gset_nopar:Npn { g_prg_stepwise_ \int_use:N \g_prg_stepwise_level_int :n } } \cs_new_protected:Npn \prg_stepwise_variable:nnnNn #1#2#3#4#5 { \exp_args:NNc \prg_stepwise_aux:NNnnnn \cs_gset_nopar:Npx { g_prg_stepwise_ \int_use:N \g_prg_stepwise_level_int :n } {#1}{#2}{#3} { \tl_set:Nn \exp_not:N #4 {##1} \exp_not:n {#5} } } \cs_new_protected:Npn \prg_stepwise_aux:NNnnnn #1#2#3#4#5#6 { #1 #2 ##1 {#6} \int_gincr:N \g_prg_stepwise_level_int \prg_stepwise_function:nnnN {#3}{#4}{#5} #2 \int_gdecr:N \g_prg_stepwise_level_int } % \end{macrocode} % \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 |\c_zero| 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, % the programmer should insert \cs{scan_align_safe_stop:} before % the test. % \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 |\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 |\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_nopar:Npn \group_align_safe_begin: { \if_int_compare:w \if_false: { \fi: `} = \c_zero \fi: } \cs_new_nopar:Npn \group_align_safe_end: { \if_int_compare:w `{ = \c_zero } \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\scan_align_safe_stop:} % When \TeX{} is in the beginning of an align cell (right after the % |\cr|) it is in a somewhat strange mode as it is looking ahead to % find an \tn{omit} or \tn{noalign} and hasn't looked at the % preamble yet. Thus an \tn{ifmmode} test will always fail unless % we insert |\scan_stop:| to stop \TeX{}'s scanning ahead. On the other % hand we don't want to insert a |\scan_stop:| every time as that will % destroy kerning between letters\footnote{Unless we enforce an extra % pass with an appropriate value of \tn{pretolerance}.} % Unfortunately there is no way to detect if we're in the beginning of % an alignment cell as they have different characteristics depending % on column number, \emph{etc.} However we \emph{can} detect if we're in an % alignment cell by checking the current group type and we can also % check if the previous node was a character or ligature. What is done % here is that |\scan_stop:| is only inserted if an only % if a)~we're in the outer part of an alignment cell and b)~the last node % \emph{wasn't} a char node or a ligature node. Thus an older definition % here was % \begin{verbatim} % \cs_new_nopar:Npn \scan_align_safe_stop: % { % \int_compare:nNnT \etex_currentgrouptype:D = \c_six % { % \int_compare:nNnF \etex_lastnodetype:D = \c_zero % { % \int_compare:nNnF \etex_lastnodetype:D = \c_seven % { \scan_stop: } % } % } % } % \end{verbatim} % However, this is not truly expandable, as there are places where the % \cs{scan_stop:} ends up in the result. A simpler alternative, which % can be used selectively, is therefore defined. % \begin{macrocode} \cs_new_protected_nopar:Npn \scan_align_safe_stop: { } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\prg_variable_get_scope:N} % \begin{macro}[aux]{\prg_variable_get_scope_aux:w} % \begin{macro}[int]{\prg_variable_get_type:N} % \begin{macro}[aux]{\prg_variable_get_type:w} % Expandable functions to find the type of a variable, and to % return \texttt{g} if the variable is global. The trick for % \cs{prg_variable_get_scope:N} is the same as that in % \cs{cs_split_function:NN}, but it can be simplified as the % requirements here are less complex. % \begin{macrocode} \group_begin: \tex_lccode:D `\& = `\g \scan_stop: \tex_catcode:D `\& = \c_twelve \tl_to_lowercase:n { \group_end: \cs_new_nopar:Npn \prg_variable_get_scope:N #1 { \exp_last_unbraced:Nf \prg_variable_get_scope_aux:w { \cs_to_str:N #1 \exp_stop_f: \q_stop } } \cs_new_nopar:Npn \prg_variable_get_scope_aux:w #1#2 \q_stop { \token_if_eq_meaning:NNT & #1 { g } } } \group_begin: \tex_lccode:D `\& = `\_ \scan_stop: \tex_catcode:D `\& = \c_twelve \tl_to_lowercase:n { \group_end: \cs_new_nopar:Npn \prg_variable_get_type:N #1 { \exp_after:wN \prg_variable_get_type_aux:w \token_to_str:N #1 & a \q_stop } \cs_new_nopar:Npn \prg_variable_get_type_aux:w #1 & #2#3 \q_stop { \token_if_eq_meaning:NNTF a #2 {#1} { \prg_variable_get_type_aux:w #2#3 \q_stop } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Experimental programmings functions} % % % \begin{macro}[aux]{\prg_define_quicksort:nnn} % |#1| is the name, |#2| and |#3| are the tokens enclosing the % argument. For the somewhat strange \meta{clist} type which doesn't % enclose the items but uses a separator we define it by hand % afterwards. When doing the first pass, the algorithm wraps all % elements in braces and then uses a generic quicksort which works % on token lists. % % As an example % \begin{quote} % |\prg_define_quicksort:nnn{seq}{\seq_elt:w}{\seq_elt_end:w}| % \end{quote} % defines the user function |\seq_quicksort:n| and furthermore % expects to use the two functions |\seq_quicksort_compare:nnTF| % which compares the items and |\seq_quicksort_function:n| which is % placed before each sorted item. It is up to the programmer to % define these functions when needed. For the |seq| type a sequence % is a token list variable, so one additionally has to define % \begin{quote} % |\cs_set_nopar:Npn \seq_quicksort:N{\exp_args:No\seq_quicksort:n}| % \end{quote} % % % For details on the implementation see \enquote{Sorting in \TeX{}'s Mouth} % by Bernd Raichle. Firstly we define the function for parsing the % initial list and then the braced list afterwards. % \begin{macrocode} \cs_new_protected_nopar:Npn \prg_define_quicksort:nnn #1#2#3 { \cs_set:cpx{#1_quicksort:n}##1{ \exp_not:c{#1_quicksort_start_partition:w} ##1 \exp_not:n{#2\q_nil#3\q_stop} } \cs_set:cpx{#1_quicksort_braced:n}##1{ \exp_not:c{#1_quicksort_start_partition_braced:n} ##1 \exp_not:N\q_nil\exp_not:N\q_stop } \cs_set:cpx {#1_quicksort_start_partition:w} #2 ##1 #3{ \exp_not:N \quark_if_nil:nT {##1}\exp_not:N \use_none_delimit_by_q_stop:w \exp_not:c{#1_quicksort_do_partition_i:nnnw} {##1}{}{} } \cs_set:cpx {#1_quicksort_start_partition_braced:n} ##1 { \exp_not:N \quark_if_nil:nT {##1}\exp_not:N \use_none_delimit_by_q_stop:w \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn} {##1}{}{} } % \end{macrocode} % Now for doing the partitions. % \begin{macrocode} \cs_set:cpx {#1_quicksort_do_partition_i:nnnw} ##1##2##3 #2 ##4 #3 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##1}{##4} \exp_not:c{#1_quicksort_partition_greater_ii:nnnn} \exp_not:c{#1_quicksort_partition_less_ii:nnnn} } {##1}{##2}{##3}{##4} } \cs_set:cpx {#1_quicksort_do_partition_i_braced:nnnn} ##1##2##3##4 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##1}{##4} \exp_not:c{#1_quicksort_partition_greater_ii_braced:nnnn} \exp_not:c{#1_quicksort_partition_less_ii_braced:nnnn} } {##1}{##2}{##3}{##4} } \cs_set:cpx {#1_quicksort_do_partition_ii:nnnw} ##1##2##3 #2 ##4 #3 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##4}{##1} \exp_not:c{#1_quicksort_partition_less_i:nnnn} \exp_not:c{#1_quicksort_partition_greater_i:nnnn} } {##1}{##2}{##3}{##4} } \cs_set:cpx {#1_quicksort_do_partition_ii_braced:nnnn} ##1##2##3##4 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##4}{##1} \exp_not:c{#1_quicksort_partition_less_i_braced:nnnn} \exp_not:c{#1_quicksort_partition_greater_i_braced:nnnn} } {##1}{##2}{##3}{##4} } % \end{macrocode} % This part of the code handles the two branches in each % sorting. Again we will also have to do it braced. % \begin{macrocode} \cs_set:cpx {#1_quicksort_partition_less_i:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_i:nnnw}{##1}{##2}{{##4}##3}} \cs_set:cpx {#1_quicksort_partition_less_ii:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_ii:nnnw}{##1}{##2}{##3{##4}}} \cs_set:cpx {#1_quicksort_partition_greater_i:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_i:nnnw}{##1}{{##4}##2}{##3}} \cs_set:cpx {#1_quicksort_partition_greater_ii:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_ii:nnnw}{##1}{##2{##4}}{##3}} \cs_set:cpx {#1_quicksort_partition_less_i_braced:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn}{##1}{##2}{{##4}##3}} \cs_set:cpx {#1_quicksort_partition_less_ii_braced:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_ii_braced:nnnn}{##1}{##2}{##3{##4}}} \cs_set:cpx {#1_quicksort_partition_greater_i_braced:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn}{##1}{{##4}##2}{##3}} \cs_set:cpx {#1_quicksort_partition_greater_ii_braced:nnnn} ##1##2##3##4{ \exp_not:c{#1_quicksort_do_partition_ii_braced:nnnn}{##1}{##2{##4}}{##3}} % \end{macrocode} % Finally, the big kahuna! This is where the sub-lists are sorted. % \begin{macrocode} \cs_set:cpx {#1_do_quicksort_braced:nnnnw} ##1##2##3##4\q_stop { \exp_not:c{#1_quicksort_braced:n}{##2} \exp_not:c{#1_quicksort_function:n}{##1} \exp_not:c{#1_quicksort_braced:n}{##3} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\prg_quicksort:n} % \UnitTested % A simple version. Sorts a list of tokens, uses the function % |\prg_quicksort_compare:nnTF| to compare items, and places the % function |\prg_quicksort_function:n| in front of each of them. % \begin{macrocode} \prg_define_quicksort:nnn {prg}{}{} % \end{macrocode} % \end{macro} % % \begin{macro}{\prg_quicksort_function:n} % \UnitTested % \begin{macro}{\prg_quicksort_compare:nnTF} % \UnitTested % \begin{macrocode} \cs_set:Npn \prg_quicksort_function:n {\ERROR} \cs_set:Npn \prg_quicksort_compare:nnTF {\ERROR} % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Deprecated functions} % % These were deprecated on 2011-05-27 and will be removed entirely by % 2011-08-31. % % \begin{macro}{\prg_new_map_functions:Nn} % \begin{macro}{\prg_set_map_functions:Nn} % As we have restructured the structured variables, these are no % longer needed. % \begin{macrocode} %<*deprecated> \cs_new_protected:Npn \prg_new_map_functions:Nn #1#2 { \deprecated } \cs_new_protected:Npn \prg_set_map_functions:Nn #1#2 { \deprecated } % % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex