diff options
author | Karl Berry <karl@freefriends.org> | 2011-07-31 18:27:06 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2011-07-31 18:27:06 +0000 |
commit | dac945b204407dae96f70ec228206516093156d7 (patch) | |
tree | 02ca182689947e45dec95e17ba08b503c959bd17 /Master/texmf-dist/source/latex/l3kernel/l3prg.dtx | |
parent | c1840548792cfbc7c9f746da1d53d89b94406e72 (diff) |
l3kernel (5jun11)
git-svn-id: svn://tug.org/texlive/trunk@23282 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3prg.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3prg.dtx | 2046 |
1 files changed, 2046 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx b/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx new file mode 100644 index 00000000000..b4cf9368f2a --- /dev/null +++ b/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx @@ -0,0 +1,2046 @@ +% \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 "expl3 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 2495 2011-07-06 16:57:08Z bruno $ + {L3 Experimental control structures} +%</driver|package> +%<*driver> +\documentclass[full]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \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 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}. The conditionals are then +% defined in the obvious way 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. +% \end{function} +% +% \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}. The conditionals are then defined in the +% obvious way 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. +%\end{function} +% +% \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} +% \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} +% \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. The branching versions then leave either +% \meta{true code} or \meta{false code} in the input stream, as +% appropriate to the truth of the test and the variant of the function +% chosen. The logical truth of the test is left in the input stream by +% the predicate version. +% \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. The branching versions then leave either +% \meta{true code} or \meta{false code} in the input stream, as +% appropriate to the truth of the test and the variant of the function +% chosen. The logical truth of the test is left in the input stream by +% the predicate version. +% \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}[EXP]{\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}[EXP]{\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}[EXP]{\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}[EXP]{\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}[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. For example +% \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. +% \end{function} +% +% \begin{function}[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}[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}[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}[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}[EXP]{\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 \\ } +% \prg_stepwise_function:nnnN { 1 } { 5 } { 1 } \my_func:n +% \end{verbatim} +% would print +% \begin{quote} +% I saw 1 \\ +% I saw 2 \\ +% I saw 3 \\ +% I saw 4 \\ +% I saw 5 \\ +% \end{quote} +% \end{function} +% +% \begin{function}{\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}{\prg_stepwise_variable:nnnn} +% \begin{syntax} +% \cs{prg_stepwise_inline:nnnn} \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. The branching +% versions then leave either \meta{true code} or \meta{false code} +% in the input stream, as appropriate to the truth of the test and +% the variant of the function chosen. The logical truth of the test +% is left in the input stream by the predicate version. +% \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. The branching +% versions then leave either \meta{true code} or \meta{false code} +% in the input stream, as appropriate to the truth of the test and +% the variant of the function chosen. The logical truth of the test +% is left in the input stream by the predicate version. +% \end{function} +% +% \begin{function}[EXP,TF]{\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. The branching +% versions then leave either \meta{true code} or \meta{false code} +% in the input stream, as appropriate to the truth of the test and +% the variant of the function chosen. +% \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. The branching +% versions then leave either \meta{true code} or \meta{false code} +% in the input stream, as appropriate to the truth of the test and +% the variant of the function chosen. The logical truth of the test +% is left in the input stream by the predicate version. +% \end{function} +% +% \section{Internal programming functions} +% +% \begin{function}[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 +% \cs{tex_halign:D}. 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 \cs{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}{\scan_align_safe_stop:} +% \begin{syntax} +% \cs{scan_align_safe_stop:} +% \end{syntax} +% This function gets \TeX{} on the right track inside an alignment +% cell but without destroying any kerning. +% \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} +% +% \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#3#4 {\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: +%</package> +% \end{macrocode} +% +% \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_bool:N #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 |!<bool>| 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. +% \begin{macrocode} +\cs_new_eq:NN \prg_case_end:nw \use_i_delimit_by_q_recursion_stop:nw +% \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. +% \begin{macrocode} +\cs_new:Npn \prg_case_int:nnn #1 + { \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 + { \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 + { \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 + { \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_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 + { \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} +% \end{macro} +% 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} +% +% \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 + { + \int_compare:nNnTF {#2} > { 0 } + { \exp_args:Nf \prg_stepwise_function_incr:nnnN } + { \exp_args:Nf \prg_stepwise_function_decr:nnnN } + { \int_eval:n {#1} } {#2} + } +\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}[aux] +% {\prg_stepwise_inline_incr:Nnnn, \prg_stepwise_inline_decr:Nnnn} +% The approach here is similar but with a global integer required +% to make the nesting safe (as seen in other in line functions). +% \begin{macrocode} +\cs_new_protected:Npn \prg_stepwise_inline:nnnn #1#2#3#4 + { + \int_gincr:N \g_prg_stepwise_level_int + \cs_gset_nopar:cpn + { g_prg_stepwise_ \int_use:N \g_prg_stepwise_level_int :n } + ##1 {#4} + \int_compare:nNnTF {#2} > { 0 } + { \exp_args:Ncf \prg_stepwise_inline_incr:Nnnn } + { \exp_args:Ncf \prg_stepwise_inline_decr:Nnnn } + { g_prg_stepwise_ \int_use:N \g_prg_stepwise_level_int :n } + { \int_eval:n {#1} } {#2} {#3} + \int_gdecr:N \g_prg_stepwise_level_int + } +\cs_new_protected:Npn \prg_stepwise_inline_incr:Nnnn #1#2#3#4 + { + \int_compare:nNnF {#2} > {#4} + { + #1 {#2} + \exp_args:NNf \prg_stepwise_inline_incr:Nnnn #1 + { \int_eval:n { #2 + #3 } } {#3} {#4} + } + } +\cs_new_protected:Npn \prg_stepwise_inline_decr:Nnnn #1#2#3#4 + { + \int_compare:nNnF {#2} < {#4} + { + #1 {#2} + \exp_args:NNf \prg_stepwise_inline_decr:Nnnn #1 + { \int_eval:n { #2 + #3 } } {#3} {#4} + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\prg_stepwise_variable:nnnNn} +% \UnitTested +% A wrapper for the above. +% \begin{macrocode} +\cs_new_protected:Npn \prg_stepwise_variable:nnnNn #1#2#3#4#5 + { + \prg_stepwise_inline:nnnn {#1} {#2} {#3} + { + \tl_set:Nn #4 {##1} + #5 + } + } +% \end{macrocode} +% \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: without \cs{} things go wrong in alignments. +% \begin{macrocode} +\prg_new_conditional:Npnn \mode_if_math: { p , T , F , TF } + { + \scan_align_safe_stop: + \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 \cs{halign} or \cs{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 +% |\tex_futurelet:D| 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 +% \begin{macrocode} +\cs_new_nopar:Npn \group_align_safe_begin: + { \if_false: { \fi: \if_int_compare:w `} = \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 |\tex_omit:D| or |\tex_noalign:D| and hasn't looked at the +% preamble yet. Thus an |\tex_ifmmode:D| 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 \cs{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. +% \begin{macrocode} +\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{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 depreciated 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} +\cs_new_protected:Npn \prg_new_map_functions:Nn #1#2 { \deprectiated } +\cs_new_protected:Npn \prg_set_map_functions:Nn #1#2 { \deprectiated } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +%</initex|package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex
\ No newline at end of file |