diff options
-rw-r--r-- | Master/texmf-dist/doc/latex/functional/README.txt | 2 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/functional/functional.pdf | bin | 189483 -> 481941 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/latex/functional/functional.tex | 3729 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/functional/functional.sty | 1300 |
4 files changed, 4704 insertions, 327 deletions
diff --git a/Master/texmf-dist/doc/latex/functional/README.txt b/Master/texmf-dist/doc/latex/functional/README.txt index fcb1672c0c3..58c2081c9a1 100644 --- a/Master/texmf-dist/doc/latex/functional/README.txt +++ b/Master/texmf-dist/doc/latex/functional/README.txt @@ -1,4 +1,4 @@ -Functional: LaTeX2 functional interfaces to LaTeX3 programming layer +Functional: LaTeX2 functional interfaces for LaTeX3 programming layer Copyright : 2022 (c) Jianrui Lyu <tolvjr@163.com> Repository: https://github.com/lvjr/functional Repository: https://bitbucket.org/lvjr/functional diff --git a/Master/texmf-dist/doc/latex/functional/functional.pdf b/Master/texmf-dist/doc/latex/functional/functional.pdf Binary files differindex f9f553a0daa..e4f7ca1b8f4 100644 --- a/Master/texmf-dist/doc/latex/functional/functional.pdf +++ b/Master/texmf-dist/doc/latex/functional/functional.pdf diff --git a/Master/texmf-dist/doc/latex/functional/functional.tex b/Master/texmf-dist/doc/latex/functional/functional.tex index 18dfa306621..b3eb028adc1 100644 --- a/Master/texmf-dist/doc/latex/functional/functional.tex +++ b/Master/texmf-dist/doc/latex/functional/functional.tex @@ -1,9 +1,9 @@ -% -*- coding: utf-8 -*- - +% -*- coding: utf-8 -*- +% !TEX program = lualatex \documentclass[oneside]{book} \usepackage[a4paper,margin=2.5cm]{geometry} -\newcommand*{\myversion}{2022A} +\newcommand*{\myversion}{2022B} \newcommand*{\mydate}{Version \myversion\ (\the\year-\mylpad\month-\mylpad\day)} \newcommand*{\mylpad}[1]{\ifnum#1<10 0\the#1\else\the#1\fi} @@ -18,6 +18,8 @@ \NewCodeHighEnv{code}{style/main=gray9,style/code=gray9} \NewCodeHighEnv{demo}{style/main=gray9,style/code=gray9,demo} +\usepackage{enumitem} + \NewDocumentCommand\MySubScript{m}{$_{#1}$} \ExplSyntaxOn @@ -34,13 +36,13 @@ } \ExplSyntaxOff -\NewDocumentEnvironment{variable}{m}{ +\NewDocumentEnvironment{variable}{om}{ \vspace{5pt} \begin{minipage}{\linewidth} \hrule\vspace{4pt}\obeylines% \begingroup \ttfamily\bfseries\color{azure3} - \PrintVarList{#1} + \PrintVarList{#2} \endgroup \par\vspace{4pt}\hrule \end{minipage}\par\nopagebreak\vspace{4pt} @@ -48,7 +50,7 @@ \vspace{5pt}% } -\NewDocumentEnvironment{function}{m}{ +\NewDocumentEnvironment{function}{om}{ \vspace{5pt}% }{\vspace{5pt}} @@ -74,6 +76,9 @@ \texttt{\{}\textsl{$\langle$\ignorespaces\lTmpaTl\unskip$\rangle$}\texttt{\}}% } +\NewDocumentCommand\nan{}{\texttt{NaN}} +\NewDocumentCommand\enquote{m}{``#1''} + \let\tn=\cs \RenewDocumentCommand\emph{m}{% @@ -92,7 +97,7 @@ \begin{document} -\title{\sffamily LaTeX2 \textcolor{green3}{Functional} Interfaces to LaTeX3 Programming Layer} +\title{\sffamily LaTeX2 \textcolor{green3}{Functional} Interfaces for LaTeX3 Programming Layer} \author{Jianrui Lyu (tolvjr@163.com)\\\url{https://github.com/lvjr/functional}} \date{\mydate\vspace{1cm}\\\myabstract\vspace{10cm}} @@ -120,44 +125,48 @@ Note that many paragraphs in this manual are copied from the documentation of \t We will compare our first example with a similar \verb!Lua! example: -\begin{code} --- lua code -- -function MathSquare (arg) - local lTmpaInt = arg * arg - return lTmpaInt -end -print(MathSquare(5)) -print(MathSquare(MathSquare(5))) -\end{code} - +\begin{minipage}{0.55\textwidth} \begin{codehigh} %% function code -\ExplSyntaxOn +\IgnoreSpacesOn \PrgNewFunction \MathSquare { m } { - \IntSet \lTmpaInt { \IntEval { #1 * #1 } } + \IntSet \lTmpaInt { \IntEval {#1 * #1} } \Result { \Value \lTmpaInt } } -\ExplSyntaxOff +\IgnoreSpacesOff \MathSquare{5} \MathSquare{\MathSquare{5}} \end{codehigh} +\end{minipage}% +\begin{minipage}{0.45\textwidth} +\begin{code} +-- define a function -- +function MathSquare (arg) + local lTmpaInt = arg * arg + return lTmpaInt +end +-- use the function -- +print(MathSquare(5)) +print(MathSquare(MathSquare(5))) +\end{code} +\end{minipage} -%\ExplSyntaxOn +%\IgnoreSpacesOn %\PrgNewFunction \MathSquare { m }{ % \IntSet \lTmpaInt { \IntEval { #1 * #1 } } % \Result { \Value \lTmpaInt } %} -%\ExplSyntaxOff +%\IgnoreSpacesOff %\MathSquare{5} %\MathSquare{\MathSquare{5}} -%\ExplSyntaxOn +%\IgnoreSpacesOn %\PrgNewFunction \MathCubic { m } % { % \IntSet \lTmpaInt { \IntEval { #1 * #1 * #1 } } % \Result { \Value \lTmpaInt } % } -%\ExplSyntaxOff +%\IgnoreSpacesOff %\MathCubic{2} %\MathCubic{\MathCubic{2}} @@ -176,38 +185,42 @@ with \cs{Result} command. And \verb!functional! package is responsible for inserting result tokens to the input stream at the appropriate time. To remove space tokens inside function code in defining functions, -you'd better put function definitions inside \verb!\ExplSyntaxOn! and -\verb!\ExplSyntaxOff! block. Within this block, \verb!~! is used to input a space. +you'd better put function definitions inside \verb!\IgnoreSpacesOn! and +\verb!\IgnoreSpacesOff! block. Within this block, \verb!~! is used to input a space. At the end of this section, we will compare our factorial example with a similar \verb!Lua! example: -\begin{code} --- lua code -- -function Factorial (n) - if n == 0 then - return 1 - else - return n * Factorial(n-1) - end -end -print(Factorial(4)) -\end{code} - +\begin{minipage}{0.69\textwidth} \begin{codehigh} -\ExplSyntaxOn -\PrgNewFunction \Factorial { m } { +\IgnoreSpacesOn +\PrgNewFunction \Fact { m } { \IntCompareTF {#1} = {0} { \Result {1} }{ - \Result { \IntMathMult {#1} { \Factorial { \IntMathSub{#1}{1} } } } + \Result {\IntMathMult{#1}{\Fact{\IntMathSub{#1}{1}}}} } } -\ExplSyntaxOff -\Factorial{4} +\IgnoreSpacesOff +\Fact{4} \end{codehigh} +\end{minipage}% +\begin{minipage}{0.31\textwidth} +\begin{code} +-- define a function -- +function Fact (n) + if n == 0 then + return 1 + else + return n * Fact(n-1) + end +end +-- use the function -- +print(Fact(4)) +\end{code} +\end{minipage} -%\ExplSyntaxOn +%\IgnoreSpacesOn %\PrgNewFunction \Factorial { m } { % \IntCompareTF {#1} = {0} { % \Result {1} @@ -215,7 +228,7 @@ print(Factorial(4)) % \Result { \IntMathMult {#1} { \Factorial { \IntMathSub{#1}{1} } } } % } %} -%\ExplSyntaxOff +%\IgnoreSpacesOff %\Factorial{0} %\Factorial{4} @@ -223,47 +236,50 @@ print(Factorial(4)) In \verb!Lua! language, a function or a condition expression makes a block, and the values of local variables will be reset after a block. -For example - -\begin{code} --- lua code -- -local a = 1 -print(a) ---- 1 -function SomeFun() - local a = 2 - print(a) ---- 2 - if 1 > 0 then - local a = 3 - print(a) ---- 3 - end - print(a) ---- 2 -end -SomeFun() -print(a) ---- 1 -\end{code} - In \verb!functional! package, a condition expression is in fact a function, and you can make every function become a group by setting \verb!\Functional{scoping=true}!. For example +\begin{minipage}{0.58\textwidth} \begin{codehigh} \Functional{scoping=true} -\ExplSyntaxOn +\IgnoreSpacesOn \IntSet \lTmpaInt {1} -\IntLog \lTmpaInt % ---- 1 +\IntLogVar \lTmpaInt % ---- 1 \PrgNewFunction \SomeFun { } { \IntSet \lTmpaInt {2} - \IntLog \lTmpaInt % ---- 2 + \IntLogVar \lTmpaInt % ---- 2 \IntCompareTF {1} > {0} { \IntSet \lTmpaInt {3} - \IntLog \lTmpaInt % ---- 3 + \IntLogVar \lTmpaInt % ---- 3 }{ } - \IntLog \lTmpaInt % ---- 2 + \IntLogVar \lTmpaInt % ---- 2 } \SomeFun -\IntLog \lTmpaInt % ---- 1 -\ExplSyntaxOff +\IntLogVar \lTmpaInt % ---- 1 +\IgnoreSpacesOff \end{codehigh} +\end{minipage}% +\begin{minipage}{0.42\textwidth} +\begin{code} +-- lua code -- +-- begin example -- +local a = 1 +print(a) ---- 1 +function SomeFun() + local a = 2 + print(a) ---- 2 + if 1 > 0 then + local a = 3 + print(a) ---- 3 + end + print(a) ---- 2 +end +SomeFun() +print(a) ---- 1 +-- end example -- +\end{code} +\end{minipage} Same as \verb!expl3!, the names of local variables \emph{must} start with \verb!l!, while names of global variables \emph{must} start with \verb!g!. @@ -275,22 +291,46 @@ by checking leading letters of their names. So for integer variables, you can wr The previous example will produce different result if we change variable from \verb!\lTmpaInt! to \verb!\gTmpaInt!. +\begin{minipage}{0.58\textwidth} \begin{codehigh} \Functional{scoping=true} +\IgnoreSpacesOn \IntSet \gTmpaInt {1} -\IntLog \gTmpaInt % ---- 1 +\IntLogVar \gTmpaInt % ---- 1 \PrgNewFunction \SomeFun { } { \IntSet \gTmpaInt {2} - \IntLog \gTmpaInt % ---- 2 + \IntLogVar \gTmpaInt % ---- 2 \IntCompareTF {1} > {0} { \IntSet \gTmpaInt {3} - \IntLog \gTmpaInt % ---- 3 + \IntLogVar \gTmpaInt % ---- 3 }{ } - \IntLog \gTmpaInt % ---- 3 + \IntLogVar \gTmpaInt % ---- 3 } \SomeFun -\IntLog \gTmpaInt % ---- 3 +\IntLogVar \gTmpaInt % ---- 3 +\IgnoreSpacesOff \end{codehigh} +\end{minipage}% +\begin{minipage}{0.42\textwidth} +\begin{code} +-- lua code -- +-- begin example -- +a = 1 +print(a) ---- 1 +function SomeFun() + a = 2 + print(a) ---- 2 + if 1 > 0 then + a = 3 + print(a) ---- 3 + end + print(a) ---- 3 +end +SomeFun() +print(a) ---- 3 +-- end example -- +\end{code} +\end{minipage} As you can see, the values of global variables will never be reset after a group. @@ -326,9 +366,9 @@ For example, the tracing log of the first example in this chapter will be the fo [I] \Result{25} [O] 25 [O] 25 - [I] \IntSet\lTmpaInt {25} - [O] - [I] \Value\lTmpaInt + [I] \IntSet{\lTmpaInt }{25} + [O] + [I] \Value{\lTmpaInt } [O] 25 [I] \Result{25} [O] 25 @@ -340,9 +380,9 @@ For example, the tracing log of the first example in this chapter will be the fo [I] \Result{625} [O] 625 [O] 625 - [I] \IntSet\lTmpaInt {625} - [O] - [I] \Value\lTmpaInt + [I] \IntSet{\lTmpaInt }{625} + [O] + [I] \Value{\lTmpaInt } [O] 625 [I] \Result{625} [O] 625 @@ -354,16 +394,22 @@ For example, the tracing log of the first example in this chapter will be the fo Within \verb!expl3!, there are eight commands for defining new functions, which is good for power users. +\begin{minipage}{0.5\textwidth} \begin{code}[language=latex/latex3] \cs_new:Npn \cs_new_nopar:Npn \cs_new_protected:Npn \cs_new_protected_nopar:Npn +\end{code} +\end{minipage}% +\begin{minipage}{0.5\textwidth} +\begin{code}[language=latex/latex3] \cs_new:Nn \cs_new_nopar:Nn \cs_new_protected:Nn \cs_new_protected_nopar:Nn \end{code} +\end{minipage} Within \verb!functional! package, there is only one command (\cs{PrgNewFunction}) for defining new functions, which is good for normal users. @@ -377,6 +423,7 @@ Since \verb!functional! package gets the results of functions by evaluation Within \verb!expl3!, there are several expansion variants for arguments, and many expansion functions for expanding them, which are necessary for power users. +\begin{minipage}{0.5\textwidth} \begin{code}[language=latex/latex3] \module_foo:c \module_bar:e @@ -386,7 +433,8 @@ and many expansion functions for expanding them, which are necessary for power u \module_bar:V \module_bar:v \end{code} - +\end{minipage}% +\begin{minipage}{0.5\textwidth} \begin{code}[language=latex/latex3] \exp_args:Nc \exp_args:Ne @@ -396,6 +444,7 @@ and many expansion functions for expanding them, which are necessary for power u \exp_args:NV \exp_args:Nv \end{code} +\end{minipage} Within \verb!functional! package, there are only three variants (\verb!c!, \verb!e!, \verb!V!) are provided, and these variants are defined @@ -426,7 +475,7 @@ simply composing \cs{Name} and \cs{Value} functions: \Value{\Name{lTmpaInt}} \end{demohigh} -\chapter{Basic Definitions (\texttt{l3basics})} +\chapter{Functional Progarmming (\texttt{Prg})} \section{Deļ¬ning Functions and Conditionals} @@ -470,6 +519,8 @@ will be created at the same time. \verb!\FooIfBarTF! function has two extra argu which are \Arg{true code} and \Arg{false code}.\par \end{function} +\section{Collecting Returned Values} + \begin{function}{\Result} \begin{syntax} \cs{Result} \Arg{tokens} @@ -478,7 +529,9 @@ Appends \meta{tokens} to \verb!\gResultTl!, which holds the returned value of cu This function is normally used in the \meta{code} of \cs{PrgNewFunction} and \cs{PrgNewConditional}. \end{function} -\section{Expanding and Using Tokens} +\chapter{Argument Using (\texttt{Use})} + +\section{Expanding Tokens} \begin{function}{\Name} \begin{syntax} @@ -538,6 +591,8 @@ Recovers the content of the \meta{variable}, then prevents expansion of this material inside the argument of \cs{Expand} function. \end{function} +\section{Using Tokens} + \begin{function}{\UseOne,\GobbleOne} \begin{syntax} \cs{UseOne} \Arg{argument} @@ -578,9 +633,18 @@ For example \end{demohigh} \end{function} -\chapter{Control Structures (\texttt{l3prg})} +\chapter{Control Structures (\texttt{Bool})} + +\section{Constant and Scratch Booleans} -\section{Scratch Variables of Booleans} +\begin{variable}{\cTrueBool,\cFalseBool} +Constants that represent \verb|true| and \verb|false|, respectively. Used to +implement predicates. For example +\begin{demohigh} +\BoolVarIfTF \cTrueBool {\Result{True!}} {\Result{False!}} +\BoolVarIfTF \cFalseBool {\Result{True!}} {\Result{False!}} +\end{demohigh} +\end{variable} \begin{variable}{\lTmpaBool,\lTmpbBool,\lTmpcBool,\lTmpiBool,\lTmpjBool,\lTmpkBool} Scratch booleans for local assignment. These are never used by @@ -596,10 +660,7 @@ function. However, they may be overwritten by other code and so should only be used for short-term storage. \end{variable} -%\BoolIfTF\cTrueBool{\Result{true}}{\Result{false}} -%\BoolIfTF\cFalseBool{\Result{true}}{\Result{false}} - -\section{Public Functions for Booleans} +\section{Creating and Setting Booleans} \begin{function}{\BoolNew} \begin{syntax} @@ -610,6 +671,33 @@ name is already taken. The declaration is global. The \meta{boolean} is initially \texttt{false}. \end{function} +\begin{function}{\BoolConst} +\begin{syntax} +\cs{BoolConst} \meta{boolean} \Arg{boolexpr} +\end{syntax} +Creates a new constant \meta{boolean} or raises an error if the name +is already taken. The value of the \meta{boolean} is set globally to +the result of evaluating the \meta{boolexpr}. +For example +\begin{codehigh} +\BoolConst \cFooSomeBool {\IntCompare{3}>{2}} +\BoolVarLog \cFooSomeBool +\end{codehigh} +\end{function} + +\begin{function}{\BoolSet} +\begin{syntax} +\cs{BoolSet} \meta{boolean} \Arg{boolexpr} +\end{syntax} +Evaluates the \meta{boolean expression} and sets the \meta{boolean} variable to +the logical truth of this evaluation. +For example +\begin{codehigh} +\BoolSet \lTmpaBool {\IntCompare{3}<{2}} +\BoolVarLog \lTmpaBool +\end{codehigh} +\end{function} + \begin{function}{\BoolSetTrue} \begin{syntax} \cs{BoolSetTrue} \meta{boolean} @@ -624,24 +712,165 @@ Sets \meta{boolean} logically \texttt{true}. Sets \meta{boolean} logically \texttt{false}. \end{function} -\begin{function}{\BoolIf,\BoolIfTF} +\begin{function}{\BoolSetEq} +\begin{syntax} +\cs{BoolSetEq} \meta{boolean_1} \meta{boolean_2} +\end{syntax} +Sets \meta{boolean_1} to the current value of \meta{boolean_2}. +For example +\begin{codehigh} +\BoolSetTrue \lTmpaBool +\BoolSetEq \lTmpbBool \lTmpaBool +\BoolVarLog \lTmpbBool +\end{codehigh} +\end{function} + +\section{Viewing Booleans} + +\begin{function}{\BoolLog} +\begin{syntax} +\cs{BoolLog} \Arg{boolean expression} +\end{syntax} +Writes the logical truth of the \meta{boolean expression} in the log file. +\end{function} + +\begin{function}{\BoolVarLog} +\begin{syntax} +\cs{BoolVarLog} \meta{boolean} +\end{syntax} +Writes the logical truth of the \meta{boolean} in the log file. +\end{function} + +\begin{function}{\BoolShow} +\begin{syntax} +\cs{BoolShow} \Arg{boolean expression} +\end{syntax} +Displays the logical truth of the \meta{boolean expression} on the terminal. +\end{function} + +\begin{function}{\BoolVarShow} +\begin{syntax} +\cs{BoolVarShow} \meta{boolean} +\end{syntax} +Displays the logical truth of the \meta{boolean} on the terminal. +\end{function} + +\section{Booleans and Conditionals} + +\begin{function}{\BoolIfExist,\BoolIfExistTF} +\begin{syntax} +\cs{BoolIfExist} \meta{boolean} +\cs{BoolIfExistTF} \meta{boolean} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{boolean} is currently defined. This does not +check that the \meta{boolean} really is a boolean variable. +For example +\begin{demohigh} +\BoolIfExistTF \lTmpaBool {\Result{Yes}} {\Result{No}} +\BoolIfExistTF \lFooUndefinedBool {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\BoolVarIf,\BoolVarIfTF} \begin{syntax} -\cs{BoolIf} \meta{boolean} -\cs{BoolIfTF} \meta{boolean} \Arg{true code} \Arg{false code} +\cs{BoolVarIf} \meta{boolean} +\cs{BoolVarIfTF} \meta{boolean} \Arg{true code} \Arg{false code} \end{syntax} Tests the current truth of \meta{boolean}, and continues evaluation based on this result. For example \begin{demohigh} -\BoolSetTrue\lTmpaBool -\BoolIfTF\lTmpaBool{\Result{True!}}{\Result{False!}} -\BoolSetFalse\lTmpaBool -\BoolIfTF\lTmpaBool{\Result{True!}}{\Result{False!}} +\BoolSetTrue \lTmpaBool +\BoolVarIfTF \lTmpaBool {\Result{True!}} {\Result{False!}} +\BoolSetFalse \lTmpaBool +\BoolVarIfTF \lTmpaBool {\Result{True!}} {\Result{False!}} +\end{demohigh} +\end{function} + +\begin{function}{\BoolVarNot,\BoolVarNotTF} +\begin{syntax} +\cs{BoolVarNot} \meta{boolean} +\cs{BoolVarNotTF} \meta{boolean} \Arg{true code} \Arg{false code} +\end{syntax} +Evaluates \meta{true code} if \meta{boolean} is \verb!false!, +and \meta{false code} If \meta{boolean} is \verb!true!. +For example +\begin{demohigh} +\BoolVarNotTF {\IntCompare{3}>{2}} {\Result{Yes}} {\Result{No}} \end{demohigh} \end{function} -\chapter{Token Lists (\texttt{l3tl})} +\begin{function}{\BoolVarAnd,\BoolVarAndTF} +\begin{syntax} +\cs{BoolVarAnd} \meta{boolean_1} \meta{boolean_2} +\cs{BoolVarAndTF} \meta{boolean_1} \meta{boolean_2} \Arg{true code} \Arg{false code} +\end{syntax} +Implements the \enquote{And} operation between two booleans, +hence is \texttt{true} if both are \texttt{true}. +%Contrarily to the infix operator \verb|&&|, +The \meta{boolean_2} is only evaluated if it is needed to determine the result of +\cs{BoolVarAnd}. +For example +\begin{demohigh} +\BoolVarAndTF {\IntCompare{3}>{2}} {\IntCompare{3}>{4}} {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\BoolVarOr,\BoolVarOrTF} +\begin{syntax} +\cs{BoolVarOr} \meta{boolean_1} \meta{boolean_2} +\cs{BoolVarOrTF} \meta{boolean_1} \meta{boolean_2} \Arg{true code} \Arg{false code} +\end{syntax} +Implements the \enquote{Or} operation between two booleans, +hence is \texttt{true} if either one is \texttt{true}. +%Contrarily to the infix operator \verb"||", +The \meta{boolean_2} is only evaluated if it is needed to determine the result of +\cs{BoolVarOr}. +For example +\begin{demohigh} +\BoolVarOrTF {\IntCompare{3}>{2}} {\IntCompare{3}>{4}} {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\BoolVarXor,\BoolVarXorTF} +\begin{syntax} +\cs{BoolVarXor} \meta{boolean_1} \meta{boolean_2} +\cs{BoolVarXorTF} \meta{boolean_1} \meta{boolean_2} \Arg{true code} \Arg{false code} +\end{syntax} +Implements an \enquote{exclusive or} operation between two booleans. +For example +\begin{demohigh} +\BoolVarXorTF {\IntCompare{3}>{2}} {\IntCompare{3}>{4}} {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} -\section{Scratch Variables of Token Lists} +\chapter{Token Lists (\texttt{Tl})} + +\section{Constant and Scratch Token Lists} + +\begin{variable}{\cSpaceTl} +An explicit space character contained in a token list% +%(compare this with \cs{c_space_token}) +. For use where an explicit space is required. +\end{variable} + +\begin{variable}{\cEmptyTl} +Constant that is always empty. +\end{variable} + +%\begin{variable}{\cNoValueTl} +%A marker for the absence of an argument. This constant \verb|tl| can safely +%be typeset (\emph{cf.}~\cs{qNil}), with the result being \verb|-NoValue-|. +%It is important to note that \cs{cNoValueTl} is constructed such that it +%will \emph{not} match the simple text input \verb|-NoValue-|, \emph{i.e.} +%that +%\begin{demohigh} +%\TlIfEqTF {\TlUse\cNoValueTl} {-NoValue-} {Result{Yes}} {\Result{No}} +%\end{demohigh} +%The \cs{cNoValueTl} marker is intended for +%use in creating document-level interfaces, where it serves as an indicator +%that an (optional) argument was omitted. In particular, it is distinct +%from a simple empty \verb|tl|. +%\end{variable} \begin{variable}{\lTmpaTl,\lTmpbTl,\lTmpcTl,\lTmpiTl,\lTmpjTl,\lTmpkTl} Scratch token lists for local assignment. These are never used by @@ -657,17 +886,26 @@ function. However, they may be overwritten by other code and so should only be used for short-term storage. \end{variable} -\section{Public Functions for Token Lists} +\section{Creating and Using Token Lists} \begin{function}{\TlNew} \begin{syntax} -\cs{TlNew} \meta{tl~var} +\cs{TlNew} \meta{tl var} \end{syntax} -Creates a new \meta{tl~var} or raises an error if the +Creates a new \meta{tl var} or raises an error if the name is already taken. The declaration is global. The \meta{tl~var} is initially empty. \end{function} +\begin{function}{\TlConst} +\begin{syntax} +\cs{TlConst} \meta{tl var} \Arg{token list} +\end{syntax} +Creates a new constant \meta{tl var} or raises an error +if the name is already taken. The value of the +\meta{tl var} is set globally to the \meta{token list}. +\end{function} + \begin{function}{\TlUse} \begin{syntax} \cs{TlUse} \meta{tl~var} @@ -678,6 +916,97 @@ does not exist or if it is invalid. Note that it is possible to use a \meta{tl~var} directly without an accessor function. \end{function} +\begin{function}{\TlToStr} +\begin{syntax} +\cs{TlToStr} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, returning the +resulting character tokens. A \meta{string} +is a series of tokens with category code $12$ (other) with the exception +of spaces, which retain category code $10$ (space). +%This function requires only a single expansion. +%Its argument \emph{must} be braced. +%\begin{texnote} +%This is the \eTeX{} primitive \tn{detokenize}. +%Converting a \meta{token list} to a \meta{string} yields a +%concatenation of the string representations of every token in the +%\meta{token list}. +%The string representation of a control sequence is +%\begin{itemize} +%\item an escape character, whose character code is given by the +%internal parameter \tn{escapechar}, absent if the +%\tn{escapechar} is negative or greater than the largest +%character code; +%\item the control sequence name, as defined by \cs{cs_to_str:N}; +%\item a space, unless the control sequence name is a single +%character whose category at the time of expansion of +%\cs{tl_to_str:n} is not \enquote{letter}. +%\end{itemize} +%The string representation of an explicit character token is that +%character, doubled in the case of (explicit) macro parameter +%characters (normally \verb|#|). +%In particular, the string representation of a token list may +%depend on the category codes in effect when it is evaluated, and +%the value of the \tn{escapechar}: for instance |\tl_to_str:n {\a}| +%normally produces the three character \enquote{backslash}, +%\enquote{lower-case a}, \enquote{space}, but it may also produce a +%single \enquote{lower-case a} if the escape character is negative +%and \texttt{a} is currently not a letter. +%\end{texnote} +\end{function} + +\begin{function}{\TlVarToStr} +\begin{syntax} +\cs{TlVarToStr} \meta{tl var} +\end{syntax} +Converts the content of the \meta{tl var} to a string, returning the +resulting character tokens. A \meta{string} +is a series of tokens with category code $12$ (other) with the exception +of spaces, which retain category code $10$ (space). +\end{function} + +\section{Viewing Token Lists} + +\begin{function}{\TlLog} +\begin{syntax} +\cs{TlLog} \Arg{token list} +\end{syntax} +Writes the \meta{token list} in the log file. See also +\cs{TlShow} which displays the result in the terminal. +\end{function} + +\begin{function}{\TlVarLog} +\begin{syntax} +\cs{TlVarLog} \meta{tl var} +\end{syntax} +Writes the content of the \meta{tl var} in the log file. See also +\cs{TlVarShow} which displays the result in the terminal. +\end{function} + +\begin{function}{\TlShow} +\begin{syntax} +\cs{TlShow} \Arg{token list} +\end{syntax} +Displays the \meta{token list} on the terminal. +%\begin{texnote} +%This is similar to the \eTeX{} primitive \tn{showtokens}, wrapped +%to a fixed number of characters per line. +%\end{texnote} +\end{function} + +\begin{function}{\TlVarShow} +\begin{syntax} +\cs{TlVarShow} \meta{tl var} +\end{syntax} +Displays the content of the \meta{tl var} on the terminal. +\begin{texnote} +This is similar to the \TeX{} primitive \tn{show}, wrapped to a +fixed number of characters per line. +\end{texnote} +\end{function} + +\section{Setting Token List Variables} + \begin{function}{\TlSet} \begin{syntax} \cs{TlSet} \meta{tl~var} \Arg{tokens} @@ -685,24 +1014,50 @@ a \meta{tl~var} directly without an accessor function. Sets \meta{tl~var} to contain \meta{tokens}, removing any previous content from the variable. For example \begin{demohigh} -\TlSet\lTmpiTl{\IntMathMult{4}{5}} -\TlUse\lTmpiTl +\TlSet \lTmpiTl {\IntMathMult{4}{5}} +\TlUse \lTmpiTl \end{demohigh} \end{function} +\begin{function}{\TlSetEq} +\begin{syntax} +\cs{TlSetEq} \meta{tl var_1} \meta{tl var_2} +\end{syntax} +Sets the content of \meta{tl var_1} equal to that of +\meta{tl var_2}. +\end{function} + \begin{function}{\TlClear} \begin{syntax} \cs{TlClear} \meta{tl~var} \end{syntax} Clears all entries from the \meta{tl~var}. For example \begin{demohigh} -\TlSet\lTmpjTl{One} -\TlClear\lTmpjTl -\TlSet\lTmpjTl{Two} -\TlUse\lTmpjTl +\TlSet \lTmpjTl {One} +\TlClear \lTmpjTl +\TlSet \lTmpjTl {Two} +\TlUse \lTmpjTl \end{demohigh} \end{function} +\begin{function}{\TlClearNew} +\begin{syntax} +\cs{TlClearNew} \meta{tl var} +\end{syntax} +Ensures that the \meta{tl var} exists globally by applying +\cs{TlNew} if necessary, then applies \cs{TlClear} to leave +the \meta{tl var} empty. +\end{function} + +\begin{function}{\TlConcat} +\begin{syntax} +\cs{TlConcat} \meta{tl var_1} \meta{tl var_2} \meta{tl var_3} +\end{syntax} +Concatenates the content of \meta{tl var_2} and \meta{tl var_3} +together and saves the result in \meta{tl var_1}. The \meta{tl var_2} +is placed at the left side of the new token list. +\end{function} + \begin{function}{\TlPutLeft} \begin{syntax} \cs{TlPutLeft} \meta{tl~var} \Arg{tokens} @@ -710,9 +1065,9 @@ Clears all entries from the \meta{tl~var}. For example Appends \meta{tokens} to the left side of the current content of \meta{tl~var}. For example \begin{demohigh} -\TlSet\lTmpkTl{Functional} -\TlPutLeft\lTmpkTl{Hello} -\TlUse\lTmpkTl +\TlSet \lTmpkTl {Functional} +\TlPutLeft \lTmpkTl {Hello} +\TlUse \lTmpkTl \end{demohigh} \end{function} @@ -723,49 +1078,1233 @@ Appends \meta{tokens} to the left side of the current content of Appends \meta{tokens} to the right side of the current content of \meta{tl~var}. For example \begin{demohigh} -\TlSet\lTmpkTl{Functional} -\TlPutRight\lTmpkTl{World} -\TlUse\lTmpkTl +\TlSet \lTmpkTl {Functional} +\TlPutRight \lTmpkTl {World} +\TlUse \lTmpkTl +\end{demohigh} +\end{function} + +\section{Replacing Tokens} + +Within token lists, replacement takes place at the top level: there is +no recursion into brace groups (more precisely, within a group defined by +a categroy code $1$/$2$ pair). + +\begin{function}{\TlReplaceOnce} +\begin{syntax} +\cs{TlReplaceOnce} \meta{tl var} \Arg{old tokens} \Arg{new tokens} +\end{syntax} +Replaces the first (leftmost) occurrence of \meta{old tokens} in the +\meta{tl var} with \meta{new tokens}. \meta{Old tokens} +cannot contain \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +\end{function} + +\begin{function}{\TlReplaceAll} +\begin{syntax} +\cs{TlReplaceAll} \meta{tl var} \Arg{old tokens} \Arg{new tokens} +\end{syntax} +Replaces all occurrences of \meta{old tokens} in the +\meta{tl var} with \meta{new tokens}. \meta{Old tokens} +cannot contain \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +As this function +operates from left to right, the pattern \meta{old tokens} +may remain after the replacement (see \cs{TlRemoveAll} +for an example). +\end{function} + +\begin{function}{\TlRemoveOnce} +\begin{syntax} +\cs{TlRemoveOnce} \meta{tl var} \Arg{tokens} +\end{syntax} +Removes the first (leftmost) occurrence of \meta{tokens} from the +\meta{tl var}. \meta{Tokens} cannot contain \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +\end{function} + +\begin{function}{\TlRemoveAll} +\begin{syntax} +\cs{TlRemoveAll} \meta{tl var} \Arg{tokens} +\end{syntax} +Removes all occurrences of \meta{tokens} from the +\meta{tl var}. \meta{Tokens} cannot contain \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +As this function +operates from left to right, the pattern \meta{tokens} +may remain after the removal, for instance, +\begin{demohigh} +\TlSet \lTmpaTl {abbccd} +\TlRemoveAll \lTmpaTl {bc} +\TlUse \lTmpaTl \end{demohigh} \end{function} +\begin{function}{\TlTrimSpaces} +\begin{syntax} +\cs{TlTrimSpaces} \Arg{token list} +\end{syntax} +Removes any leading and trailing explicit space characters +(explicit tokens with character code $32$ and category code $10$) +from the \meta{token list} and returns the result. +%\begin{texnote} +%The result is returned within \tn{unexpanded}, which means that the token +%list does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\TlVarTrimSpaces} +\begin{syntax} +\cs{TlVarTrimSpaces} \meta{tl var} +\end{syntax} +Sets the \meta{tl var} to contain the result of removing any leading +and trailing explicit space characters (explicit tokens with +character code $32$ and category code $10$) from its contents. +\end{function} + +\section{Working with the Content of Token Lists} + +\begin{function}{\TlCount} +\begin{syntax} +\cs{TlCount} \Arg{tokens} +\end{syntax} +Counts the number of \meta{items} in \meta{tokens} and returns this information. +Unbraced tokens count as one element as do each token group (\verb|{|$\cdots$\verb|}|). +This process ignores any unprotected spaces within \meta{tokens}. %See also \cs{TlVarCount}. +%This function requires three expansions, giving an \meta{integer denotation}. +\end{function} + +\begin{function}{\TlVarCount} +\begin{syntax} +\cs{TlVarCount} \meta{tl var} +\end{syntax} +Counts the number of \meta{items} in the \meta{tl var} and returns this information. +Unbraced tokens count as one element as do each token group (\verb|{|$\cdots$\verb|}|). +This process ignores any unprotected spaces within the \meta{tl var}. %See also \cs{TlCount}. +%This function requires three expansions, giving an \meta{integer denotation}. +\end{function} + +\begin{function}{\TlHead} +\begin{syntax} +\cs{TlHead} \Arg{token list} +\end{syntax} +Returns the first \meta{item} in the \meta{token list}, +discarding the rest of the \meta{token list}. +All leading explicit space characters +(explicit tokens with character code $32$ and category code $10$) +are discarded; for example +\begin{demohigh} +\fbox {1\TlHead{ abc }2} +\fbox {1\TlHead{ abc }2} +\end{demohigh} +If the \enquote{head} is a brace group, rather than a single token, +the braces are removed, and so +\begin{codehigh} +\TlHead { { ab} c } +\end{codehigh} +yields \verb*| ab|. +A blank \meta{token list} (see \cs{TlIfBlank}) results in +\cs{TlHead} returning nothing. +%\begin{texnote} +%The result is returned within \cs{exp_not:n}, which means that the token +%list does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\TlVarHead} +\begin{syntax} +\cs{TlVarHead} \meta{tl var} +\end{syntax} +Returns the first \meta{item} in the \meta{tl var}, +discarding the rest of the \meta{tl var}. +All leading explicit space characters +(explicit tokens with character code $32$ and category code $10$) +are discarded. +\end{function} + +\begin{function}{\TlTail} +\begin{syntax} +\cs{TlTail} \Arg{token list} +\end{syntax} +Discards all leading explicit space characters +(explicit tokens with character code $32$ and category code $10$) +and the first \meta{item} in the \meta{token list}, and returns the +remaining tokens. Thus for example +\begin{codehigh} +\TlTail { a {bc} d } +\end{codehigh} +and +\begin{codehigh} +\TlTail { a {bc} d } +\end{codehigh} +both return \verb*| {bc} d |. A blank \meta{token list} (see \cs{TlIfBlank}) +results in \cs{TlTail} returning nothing. +%\begin{texnote} +%The result is returned within \cs{exp_not:n}, which means that the +%token list does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\TlVarTail} +\begin{syntax} +\cs{TlVarTail} \meta{tl var} +\end{syntax} +Discards all leading explicit space characters +(explicit tokens with character code $32$ and category code $10$) +and the first \meta{item} in the \meta{tl var}, and returns the +remaining tokens. +\end{function} + +\begin{function}{\TlItem,\TlVarItem} +\begin{syntax} +\cs{TlItem} \Arg{token list} \Arg{integer expression} +\cs{TlVarItem} \meta{tl var} \Arg{integer expression} +\end{syntax} +Indexing items in the \meta{token list} from $1$ on the left, this +function evaluates the \meta{integer expression} and returns the +appropriate item from the \meta{token list}. +If the \meta{integer expression} is negative, indexing occurs from +the right of the token list, starting at $-1$ for the right-most item. +If the index is out of bounds, then the function returns nothing. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{item} +%does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\TlRandItem,\TlVarRandItem} +\begin{syntax} +\cs{TlRandItem} \Arg{token list} +\cs{TlVarRandItem} \meta{tl var} +\end{syntax} +Selects and returns a pseudo-random item of the \meta{token list}. +If the \meta{token list} is blank, the result is empty. +%This is not available in older versions of \XeTeX{}. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{item} +%does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\section{Mapping over Token Lists} + +All mappings are done at the current group level, \emph{i.e.} any +local assignments made by the \meta{function} or \meta{code} discussed +below remain in effect after the loop. + +%\begin{function}{\TlMapFunction} +%\begin{syntax} +%\cs{TlMapFunction} \Arg{token list} \meta{function} +%\end{syntax} +%Applies \meta{function} to every \meta{item} in the \meta{token list}, +%The \meta{function} receives one argument for each iteration. +%This may be a number of tokens if the \meta{item} was stored within +%braces. Hence the \meta{function} should anticipate receiving +%\texttt{n}-type arguments. +%\end{function} +% +%\begin{function}{\TlVarMapFunction} +%\begin{syntax} +%\cs{TlVarMapFunction} \meta{tl var} \meta{function} +%\end{syntax} +%Applies \meta{function} to every \meta{item} in the \meta{tl var}. +%The \meta{function} receives one argument for each iteration. +%This may be a number of tokens if the \meta{item} was stored within +%braces. Hence the \meta{function} should anticipate receiving +%\texttt{n}-type arguments. +%\end{function} + +\begin{function}{\TlMapInline} +\begin{syntax} +\cs{TlMapInline} \Arg{token list} \Arg{inline function} +\end{syntax} +Applies the \meta{inline function} to every \meta{item} stored within the +\meta{token list}. The \meta{inline function} should consist of code which +receives the \meta{item} as \verb|#1|. +\end{function} + +\begin{function}{\TlVarMapInline} +\begin{syntax} +\cs{TlVarMapInline} \meta{tl var} \Arg{inline function} +\end{syntax} +Applies the \meta{inline function} to every \meta{item} stored within the +\meta{tl var}. The \meta{inline function} should consist of code which +receives the \meta{item} as \verb|#1|. +\end{function} + +%\begin{function}{\TlMapTokens,\TlVarMapTokens} +%\begin{syntax} +%\cs{TlMapTokens} \Arg{tokens} \Arg{code} +%\cs{TlVarMapTokens} \meta{tl var} \Arg{code} +%\end{syntax} +%Analogue of \cs{tl_map_function:NN} which maps several tokens +%instead of a single function. The \meta{code} receives each \meta{item} in +%the \meta{tl var} or in \meta{tokens} as a trailing brace group. For +%instance, +%\begin{verbatim} +%\tl_map_tokens:Nn \l_my_tl { \prg_replicate:nn { 2 } } +%\end{verbatim} +%expands to twice each \meta{item} in the \meta{tl var}: for each \meta{item} in +%\cs{l_my_tl} the function \cs{prg_replicate:nn} receives |2| and +%\meta{item} as its two arguments. The function +%\cs{tl_map_inline:Nn} is typically faster but is not expandable. +%\end{function} + +\begin{function}{\TlMapVariable} +\begin{syntax} +\cs{TlMapVariable} \Arg{token list} \meta{variable} \Arg{code} +\end{syntax} +Stores each \meta{item} of the \meta{token list} in turn in the +(token list) \meta{variable} and applies the \meta{code}. The +\meta{code} will usually make use of the \meta{variable}, but this +is not enforced. The assignments to the \meta{variable} are local. +Its value after the loop is the last \meta{item} in the +\meta{tl var}, or its original value if the \meta{tl var} is blank. +\end{function} + +\begin{function}{\TlVarMapVariable} +\begin{syntax} +\cs{TlVarMapVariable} \meta{tl var} \meta{variable} \Arg{code} +\end{syntax} +Stores each \meta{item} of the \meta{tl var} in turn in the (token +list) \meta{variable} and applies the \meta{code}. The \meta{code} +will usually make use of the \meta{variable}, but this is not +enforced. The assignments to the \meta{variable} are local. Its +value after the loop is the last \meta{item} in the \meta{tl var}, +or its original value if the \meta{tl var} is blank. +\end{function} + +%\begin{function}{\TlMapBreak} +%\begin{syntax} +%\cs{TlMapBreak} +%\end{syntax} +%Used to terminate a tl map function before all +%entries in the \meta{token list variable} have been processed. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\tl_map_inline:Nn \l_my_tl +%{ +%\str_if_eq:nnT { #1 } { bingo } { \tl_map_break: } +%Do something useful +%} +%\end{verbatim} +%See also \cs{tl_map_break:n}. +%Use outside of a tl map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before the \meta{tokens} are +%inserted into the input stream. +%This depends on the design of the mapping function. +%\end{texnote} +%\end{function} +% +%\begin{function}{\TlMapBreakDo} +%\begin{syntax} +%\cs{TlMapBreakDo} \Arg{code} +%\end{syntax} +%Used to terminate a tl map function before all +%entries in the \meta{token list variable} have been processed, inserting +%the \meta{code} after the mapping has ended. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\tl_map_inline:Nn \l_my_tl +%{ +%\str_if_eq:nnT { #1 } { bingo } +%{ \tl_map_break:n { <code> } } +%Do something useful +%} +%\end{verbatim} +%Use outside of a tl map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before the \meta{code} is +%inserted into the input stream. +%This depends on the design of the mapping function. +%\end{texnote} +%\end{function} + +\section{Token List Conditionals} + +\begin{function}{\TlIfExist,\TlIfExistTF} +\begin{syntax} +\cs{TlIfExist} \meta{tl var} +\cs{TlIfExistTF} \meta{tl var} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{tl var} is currently defined. This does not +check that the \meta{tl var} really is a token list variable. +\end{function} + \begin{function}{\TlIfEmpty,\TlIfEmptyTF} \begin{syntax} -\cs{TlIfEmpty} \meta{tl~var} -\cs{TlIfEmptyTF} \meta{tl~var} \Arg{true code} \Arg{false code} +\cs{TlIfEmpty} \Arg{token list} +\cs{TlIfEmptyTF} \Arg{token list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{token list} is entirely empty +(\emph{i.e.}~contains no tokens at all). For example +\begin{demohigh} +\TlIfEmptyTF {abc} {\Result{Empty}} {\Result{NonEmpty}} +\TlIfEmptyTF {} {\Result{Empty}} {\Result{NonEmpty}} +\end{demohigh} +\end{function} + +\begin{function}{\TlVarIfEmpty,\TlVarIfEmptyTF} +\begin{syntax} +\cs{TlVarIfEmpty} \meta{tl~var} +\cs{TlVarIfEmptyTF} \meta{tl~var} \Arg{true code} \Arg{false code} \end{syntax} Tests if the \meta{token list variable} is entirely empty (\emph{i.e.}~contains no tokens at all). For example \begin{demohigh} -\TlSet\lTmpaTl{abc} -\TlIfEmptyTF\lTmpaTl{\Result{Empty}}{\Result{NonEmpty}} -\TlClear\lTmpaTl -\TlIfEmptyTF\lTmpaTl{\Result{Empty}}{\Result{NonEmpty}} +\TlSet \lTmpaTl {abc} +\TlVarIfEmptyTF \lTmpaTl {\Result{Empty}} {\Result{NonEmpty}} +\TlClear \lTmpaTl +\TlVarIfEmptyTF \lTmpaTl {\Result{Empty}} {\Result{NonEmpty}} \end{demohigh} \end{function} +\begin{function}{\TlIfBlank} +\begin{syntax} +\cs{TlIfBlank} \Arg{token list} +\cs{TlIfBlankTF} \Arg{token list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{token list} consists only of blank spaces +(\emph{i.e.} contains no item). The test is \texttt{true} if +\meta{token list} is zero or more explicit space characters +(explicit tokens with character code $32$ and category code $10$), +and is \texttt{false} otherwise. +\end{function} + \begin{function}{\TlIfEq,\TlIfEqTF} \begin{syntax} -\cs{TlIfEq} \meta{tl~var_1} \meta{tl~var_2} -\cs{TlIfEqTF} \meta{tl~var_1} \meta{tl~var_2} \Arg{true code} \Arg{false code} +\cs{TlIfEq} \Arg{token list_1} \Arg{token list_2} +\cs{TlIfEqTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if \meta{token list_1} and \meta{token list_2} contain the +same list of tokens, both in respect of character codes and category +codes. See \cs{StrIfEq} if category codes are not important. +For example +\begin{demohigh} +\TlIfEqTF {abc} {abc} {\Result{Yes}} {\Result{No}} +\TlIfEqTF {abc} {xyz} {\Result{Yes}} {\Result{No}} +\end{demohigh} +%\begin{demohigh} +%\TlSet\lTmpaTl{abc} +%\TlSet\lTmpbTl{abc} +%\TlSet\lTmpcTl{xyz} +%\TlIfEqTF{\TlUse\lTmpaTl}{\TlUse\lTmpbTl}{\Result{Yes}}{\Result{No}} +%\TlIfEqTF{\TlUse\lTmpaTl}{\TlUse\lTmpcTl}{\Result{Yes}}{\Result{No}} +%\end{demohigh} +\end{function} + +\begin{function}{\TlVarIfEq,\TlVarIfEqTF} +\begin{syntax} +\cs{TlVarIfEq} \meta{tl var_1} \meta{tl var_2} +\cs{TlVarIfEqTF} \meta{tl var_1} \meta{tl var_2} \Arg{true code} \Arg{false code} \end{syntax} Compares the content of two \meta{token list variables} and is logically \texttt{true} if the two contain the same list of tokens (\emph{i.e.}~identical in both the list of characters they contain and the category codes of those characters). For example \begin{demohigh} -\TlSet\lTmpaTl{abc} -\TlSet\lTmpbTl{abc} -\TlSet\lTmpcTl{xyz} -\TlIfEqTF\lTmpaTl\lTmpbTl{\Result{Yes}}{\Result{No}} -\TlIfEqTF\lTmpaTl\lTmpcTl{\Result{Yes}}{\Result{No}} +\TlSet \lTmpaTl {abc} +\TlSet \lTmpbTl {abc} +\TlSet \lTmpcTl {xyz} +\TlVarIfEqTF \lTmpaTl \lTmpbTl {\Result{Yes}} {\Result{No}} +\TlVarIfEqTF \lTmpaTl \lTmpcTl {\Result{Yes}} {\Result{No}} +\end{demohigh} +See also \cs{StrVarIfEq} for a comparison that ignores category codes. +\end{function} + +\begin{function}{\TlIfIn,\TlIfInTF} +\begin{syntax} +\cs{TlIfIn} \Arg{token list_1} \Arg{token list_2} +\cs{TlIfInTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if \meta{token list_2} is found inside \meta{token list_1}. +The \meta{token list_2} cannot contain the tokens \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +The search does \emph{not} enter brace (category code $1$/$2$) groups. +\end{function} + +\begin{function}{\TlVarIfIn,\TlVarIfInTF} +\begin{syntax} +\cs{TlVarIfIn} \meta{tl var} \Arg{token list} +\cs{TlVarIfInTF} \meta{tl var} \Arg{token list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{token list} is found in the content of the +\meta{tl var}. The \meta{token list} cannot contain +the tokens \verb|{|, \verb|}| or \verb|#| +(more precisely, explicit character tokens with category code $1$ +(begin-group) or $2$ (end-group), and tokens with category code $6$). +\end{function} + +\begin{function}{\TlIfSingle,\TlIfSingleTF} +\begin{syntax} +\cs{TlIfSingle} \Arg{token list} +\cs{TlIfSingleTF} \Arg{token list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{token list} has exactly one \meta{item}, \emph{i.e.} is +a single normal token (neither an explicit space character nor a +begin-group character) or a single brace group, surrounded by +optional spaces on both sides. In other words, such a token list has +token count $1$ according to \cs{TlCount}. +\end{function} + +\begin{function}{\TlVarIfSingle,\TlVarIfSingleTF} +\begin{syntax} +\cs{TlVarIfSingle} \meta{tl var} +\cs{TlVarIfSingleTF} \meta{tl var} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the content of the \meta{tl var} consists of a single \meta{item}, +\emph{i.e.} is a single normal token (neither an explicit space +character nor a begin-group character) or a single brace group, +surrounded by optional spaces on both sides. In other words, such a +token list has token count $1$ according to \cs{TlVarCount}. +\end{function} + +\section{Token List Case Functions} + +\begin{function}{\TlVarCase} +\begin{syntax} +\cs{TlVarCase} \meta{test token list variable} +~ ~ \verb"{" +~ ~ ~ ~ \meta{token list variable case_1} \Arg{code case_1} +~ ~ ~ ~ \meta{token list variable case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \meta{token list variable case_n} \Arg{code case_n} +~ ~ \verb"}" +\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{TlVarIfEq}) +then the associated \meta{code} is left in the input +stream and other cases are discarded. The function +does nothing if there is no match. +\end{function} + +\begin{function}{\TlVarCaseT} +\begin{syntax} +\cs{TlVarCaseT} \meta{test token list variable} +~ ~ \verb"{" +~ ~ ~ ~ \meta{token list variable case_1} \Arg{code case_1} +~ ~ ~ ~ \meta{token list variable case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \meta{token list variable case_n} \Arg{code case_n} +~ ~ \verb"}" +~ ~ \Arg{true code} +\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{TlVarIfEq}) +then the associated \meta{code} is left in the input +stream and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case). +\end{function} + +\begin{function}{\TlVarCaseF} +\begin{syntax} +\cs{TlVarCaseF} \meta{test token list variable} +~ ~ \verb"{" +~ ~ ~ ~ \meta{token list variable case_1} \Arg{code case_1} +~ ~ ~ ~ \meta{token list variable case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \meta{token list variable case_n} \Arg{code case_n} +~ ~ \verb"}" +~ ~ \Arg{false code} +\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{TlVarIfEq}) +then the associated \meta{code} is left in the input +stream and other cases are discarded. If none +match then the \meta{false code} is inserted into the input stream + (after the code for the appropriate case). +\end{function} + +\begin{function}{\TlVarCaseTF} +\begin{syntax} +\cs{TlVarCaseTF} \meta{test token list variable} +~ ~ \verb"{" +~ ~ ~ ~ \meta{token list variable case_1} \Arg{code case_1} +~ ~ ~ ~ \meta{token list variable case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \meta{token list variable case_n} \Arg{code case_n} +~ ~ \verb"}" +~ ~ \Arg{true code} +~ ~ \Arg{false code} +\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{TlVarIfEq}) +then the associated \meta{code} is left in the input +stream and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case), while if none +match then the \meta{false code} is inserted. The function +\cs{TlVarCase}, which does nothing if there is no match, is also +available. +\end{function} + +\chapter{Strings (\texttt{Str})} + +\section{Constant and Scratch Strings} + +\begin{variable}{\cAmpersandStr,\cAtsignStr,\cBackslashStr,\cLeftBraceStr,\cRightBraceStr, +\cCircumflexStr,\cColonStr,\cDollarStr,\cHashStr,\cPercentStr,\cTildeStr,\cUnderscoreStr,\cZeroStr} +Constant strings, containing a single character token, with category code $12$. +\end{variable} + +\begin{variable}{\lTmpaStr,\lTmpbStr,\lTmpcStr,\lTmpiStr,\lTmpjStr,\lTmpkStr} +Scratch strings for local assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\begin{variable}{\gTmpaStr,\gTmpbStr,\gTmpcStr,\gTmpiStr,\gTmpjStr,\gTmpkStr} +Scratch strings for global assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\section{Creating and Using Strings} + +\begin{function}{\StrNew} +\begin{syntax} +\cs{StrNew} \meta{str var} +\end{syntax} +Creates a new \meta{str var} or raises an error if the name is +already taken. The declaration is global. The \meta{str var} is +initially empty. +\end{function} + +\begin{function}{\StrConst} +\begin{syntax} +\cs{StrConst} \meta{str var} \Arg{token list} +\end{syntax} +Creates a new constant \meta{str var} or raises an error if the name +is already taken. The value of the \meta{str var} is set +globally to the \meta{token list}, converted to a string. +\end{function} + +\begin{function}{\StrUse} +\begin{syntax} +\cs{StrUse} \meta{str var} +\end{syntax} +Recovers the content of a \meta{str var} and returns the value. +An error is raised if the variable +does not exist or if it is invalid. Note that it is possible to use +a \meta{str} directly without an accessor function. +\end{function} + +\section{Viewing Strings} + +\begin{function}{\StrLog} +\begin{syntax} +\cs{StrLog} \Arg{token list} +\end{syntax} +Writes \meta{token list} in the log file. +\end{function} + +\begin{function}{\StrVarLog} +\begin{syntax} +\cs{StrVarLog} \meta{str var} +\end{syntax} +Writes the content of the \meta{str var} in the log file. +For example +\begin{codehigh} +\StrSet \lTmpiStr {1234\abcd5678} +\StrVarLog \lTmpiStr +\end{codehigh} +\end{function} + +\begin{function}{\StrShow} +\begin{syntax} +\cs{StrShow} \Arg{token list} +\end{syntax} +Displays \meta{token list} on the terminal. +\end{function} + +\begin{function}{\StrVarShow} +\begin{syntax} +\cs{StrVarShow} \meta{str var} +\end{syntax} +Displays the content of the \meta{str var} on the terminal. +\end{function} + +\section{Setting String Variables} + +\begin{function}{\StrSet} +\begin{syntax} +\cs{StrSet} \meta{str var} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, and stores the +result in \meta{str var}. For example +\begin{demohigh} +\StrSet \lTmpiStr {\IntMathMult{4}{5}} +\StrUse \lTmpiStr +\end{demohigh} +\end{function} + +\begin{function}{\StrSetEq} +\begin{syntax} +\cs{StrSetEq} \meta{str var_1} \meta{str var_2} +\end{syntax} +Sets the content of \meta{str var_1} equal to that of +\meta{str var_2}. +\end{function} + +\begin{function}{\StrClear} +\begin{syntax} +\cs{StrClear} \meta{str var} +\end{syntax} +Clears the content of the \meta{str var}. For example +\begin{demohigh} +\StrSet \lTmpjStr {One} +\StrClear \lTmpjStr +\StrSet \lTmpjStr {Two} +\StrUse \lTmpjStr +\end{demohigh} +\end{function} + +\begin{function}{\StrClearNew} +\begin{syntax} +\cs{StrClearNew} \meta{str var} +\end{syntax} +Ensures that the \meta{str var} exists globally by applying +\cs{StrNew} if necessary, then applies \cs{StrClear} to leave +the \meta{str var} empty. +\end{function} + +\begin{function}{\StrConcat} +\begin{syntax} +\cs{StrConcat} \meta{str var_1} \meta{str var_2} \meta{str var_3} +\end{syntax} +Concatenates the content of \meta{str var_2} and \meta{str var_3} +together and saves the result in \meta{str var_1}. The \meta{str var_2} +is placed at the left side of the new string variable. +The \meta{str var_2} and \meta{str var_3} must indeed be strings, as +this function does not convert their contents to a string. +\end{function} + +\begin{function}{\StrPutLeft} +\begin{syntax} +\cs{StrPutLeft} \meta{str var} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, and prepends the +result to \meta{str var}. The current contents of the \meta{str +var} are not automatically converted to a string. For example +\begin{demohigh} +\StrSet \lTmpkStr {Functional} +\StrPutLeft \lTmpkStr {Hello} +\StrUse \lTmpkStr +\end{demohigh} +\end{function} + +\begin{function}{\StrPutRight} +\begin{syntax} +\cs{StrPutRight} \meta{str var} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, and appends the +result to \meta{str var}. The current contents of the \meta{str +var} are not automatically converted to a string. For example +\begin{demohigh} +\StrSet \lTmpkStr {Functional} +\StrPutRight \lTmpkStr {World} +\StrUse \lTmpkStr +\end{demohigh} +\end{function} + +\section{Modifying String Variables} + +\begin{function}{\StrReplaceOnce} +\begin{syntax} +\cs{StrReplaceOnce} \meta{str var} \Arg{old} \Arg{new} +\end{syntax} +Converts the \meta{old} and \meta{new} token lists to strings, then +replaces the first (leftmost) occurrence of \meta{old string} in the +\meta{str var} with \meta{new string}. +\end{function} + +\begin{function}{\StrReplaceAll} +\begin{syntax} +\cs{StrReplaceAll} \meta{str var} \Arg{old} \Arg{new} +\end{syntax} +Converts the \meta{old} and \meta{new} token lists to strings, then +replaces all occurrences of \meta{old string} in the +\meta{str var} with \meta{new string}. +As this function +operates from left to right, the pattern \meta{old string} +may remain after the replacement. +\end{function} + +\begin{function}{\StrRemoveOnce} +\begin{syntax} +\cs{StrRemoveOnce} \meta{str var} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string} then +removes the first (leftmost) occurrence of \meta{string} from the +\meta{str var}. +\end{function} + +\begin{function}{\StrRemoveAll} +\begin{syntax} +\cs{StrRemoveAll} \meta{str var} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string} then +removes all occurrences of \meta{string} from the \meta{str var}. +As this function operates from left to right, +the pattern \meta{string} may remain after the removal, for instance, +\begin{demohigh} +\StrSet \lTmpaStr {abbccd} +\StrRemoveAll \lTmpaStr {bc} +\TlUse \lTmpaStr +\end{demohigh} +\end{function} + +\section{Working with the Content of Strings} + +\begin{function}{\StrCount} +\begin{syntax} +\cs{StrCount} \Arg{token list} +\end{syntax} +Returns the number of characters in the string representation of \meta{token list}, +as an integer denotation. All characters including spaces are counted. +\par +Due to naming conflict, you need to use \cs{StrSize} instead of \cs{StrCount} +if you want to use \verb!functional! package together with \verb!xstring! package. +\end{function} + +\begin{function}{\StVarCount} +\begin{syntax} +\cs{StrVarCount} \meta{tl var} +\end{syntax} +Returns the number of characters in the string representation of the \meta{tl var}, +as an integer denotation. All characters including spaces are counted. +\end{function} + +\begin{function}{\StrHead} +\begin{syntax} +\cs{StrHead} \Arg{token list} +\end{syntax} +Converts the \meta{token list} into a \meta{string}. +The first character in the \meta{string} is then returned, +with category code \enquote{other}. If the first character is a space, +it returns a space token with category code $10$ (blank space). +If the \meta{string} is empty, then nothing is returned. +\end{function} + +\begin{function}{\StrVarHead} +\begin{syntax} +\cs{StrVarHead} \meta{tl var} +\end{syntax} +Converts the \meta{tl var} into a \meta{string}. +The first character in the \meta{string} is then returned, +with category code \enquote{other}. If the first character is a space, +it returns a space token with category code $10$ (blank space). +If the \meta{string} is empty, then nothing is returned. +\end{function} + +\begin{function}{\StrTail} +\begin{syntax} +\cs{StrTail} \Arg{token list} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, removes the first +character, and returns the remaining characters (if any) +with category codes $12$ and $10$ (for spaces). +If the first character is a space, it only trims that space. +If the \meta{token list} is empty, +then nothing is left on the input stream. +\end{function} + +\begin{function}{\StrVarTail} +\begin{syntax} +\cs{StrVarTail} \meta{tl var} +\end{syntax} +Converts the \meta{tl var} to a \meta{string}, removes the first +character, and returns the remaining characters (if any) +with category codes $12$ and $10$ (for spaces). +If the first character is a space, it only trims that space. +If the \meta{token list} is empty, +then nothing is left on the input stream. +\end{function} + +\begin{function}{\StrItem} +\begin{syntax} +\cs{StrItem} \Arg{token list} \Arg{integer expression} +\end{syntax} +Converts the \meta{token list} to a \meta{string}, +and returns the character in position \meta{integer expression} of +the \meta{string}, starting at $1$ for the first (left-most) +character. All characters including spaces are taken into account. +If the \meta{integer expression} is negative, +characters are counted from the end of the \meta{string}. Hence, +$-1$ is the right-most character, \emph{etc.} +\end{function} + +\begin{function}{\StrVarItem} +\begin{syntax} +\cs{StrVarItem} \meta{tl var} \Arg{integer expression} +\end{syntax} +Converts the \meta{tl var} to a \meta{string}, +and returns the character in position \meta{integer expression} of +the \meta{string}, starting at $1$ for the first (left-most) +character. All characters including spaces are taken into account. +If the \meta{integer expression} is negative, +characters are counted from the end of the \meta{string}. Hence, +$-1$ is the right-most character, \emph{etc.} +\end{function} + +\section{Mapping over Strings} + +All mappings are done at the current group level, \emph{i.e.} any +local assignments made by the \meta{function} or \meta{code} discussed +below remain in effect after the loop. + +%\begin{function}{\StrMapFunction,\StrVarMapFunction} +%\begin{syntax} +%\cs{StrMapFunction} \Arg{token list} \meta{function} +%\cs{StrVarMapFunction} \meta{str var} \meta{function} +%\end{syntax} +%Converts the \meta{token list} to a \meta{string} then +%applies \meta{function} to every \meta{character} in the +%\meta{string} including spaces. +%\end{function} + +\begin{function}{\StrMapInline,\StrVarMapInline} +\begin{syntax} +\cs{StrMapInline} \Arg{token list} \Arg{inline function} +\cs{StrVarMapInline} \meta{str var} \Arg{inline function} +\end{syntax} +Converts the \meta{token list} to a \meta{string} then +applies the \meta{inline function} to every \meta{character} in the +\meta{str var} including spaces. +The \meta{inline function} should consist of code which +receives the \meta{character} as \verb|#1|. +\end{function} + +%\begin{function}{\StrMapTokens, \StrVarMapTokens} +%\begin{syntax} +%\cs{StrMapTokens} \Arg{token list} \Arg{code} +%\cs{StrVarMapTokens} \meta{str var} \Arg{code} +%\end{syntax} +%Converts the \meta{token list} to a \meta{string} then applies +%\meta{code} to every \meta{character} in the \meta{string} including spaces. +%The \meta{code} receives each character as a trailing brace group. +%This is equivalent to \cs{StrMapFunction} if the +%\meta{code} consists of a single function. +%\end{function} + +\begin{function}{\StrMapVariable,\StrMapVariable} +\begin{syntax} +\cs{StrMapVariable} \Arg{token list} \meta{variable} \Arg{code} +\cs{StrVarMapVariable} \meta{str var} \meta{variable} \Arg{code} +\end{syntax} +Converts the \meta{token list} to a \meta{string} then stores each +\meta{character} in the \meta{string} (including spaces) in turn in +the (string or token list) \meta{variable} and applies the +\meta{code}. The \meta{code} will usually make use of the +\meta{variable}, but this is not enforced. The assignments to the +\meta{variable} are local. Its value after the loop is the last +\meta{character} in the \meta{string}, or its original value if the +\meta{string} is empty. +\end{function} + +%\begin{function}{\StrMapBreak} +%\begin{syntax} +%\cs{StrMapBreak} +%\end{syntax} +%Used to terminate a string map function before all +%characters in the \meta{string} have been processed. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\str_map_inline:Nn \l_my_str +%{ +%\str_if_eq:nnT { #1 } { bingo } { \str_map_break: } +%Do something useful +%} +%\end{verbatim} +%See also \cs{StrMapBreakDo}. +%Use outside of a string map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before continuing with the +%code that follows the loop. +%This depends on the design of the mapping function. +%\end{texnote} +%\end{function} +% +%\begin{function}{\StrMapBreakDo} +%\begin{syntax} +%\cs{StrMapBreakDo} \Arg{code} +%\end{syntax} +%Used to terminate a string map function before all +%characters in the \meta{string} have been processed, inserting +%the \meta{code} after the mapping has ended. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\str_map_inline:Nn \l_my_str +%{ +%\str_if_eq:nnT { #1 } { bingo } +%{ \str_map_break:n { <code> } } +%Do something useful +%} +%\end{verbatim} +%Use outside of a string map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before the \meta{code} is +%inserted into the input stream. +%This depends on the design of the mapping function. +%\end{texnote} +%\end{function} + +\section{String Conditionals} + +\begin{function}{\StrIfExist,\StrIfExistTF} +\begin{syntax} +\cs{StrIfExist} \meta{str var} +\cs{StrIfExistTF} \meta{str var} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{str var} is currently defined. This does not +check that the \meta{str var} really is a string. +\end{function} + +\begin{function}{\StrVarIfEmpty,\StrVarIfEmptyTF} +\begin{syntax} +\cs{StrVarIfEmpty} \meta{str var} +\cs{StrVarIfEmptyTF} \meta{str var} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{string variable} is entirely empty +(\emph{i.e.} contains no characters at all). For example +\begin{demohigh} +\StrSet \lTmpaStr {abc} +\StrVarIfEmptyTF \lTmpaStr {\Result{Empty}} {\Result{NonEmpty}} +\StrClear \lTmpaStr +\StrVarIfEmptyTF \lTmpaStr {\Result{Empty}} {\Result{NonEmpty}} \end{demohigh} -%See also \cs{StrIfEqTF} for a comparison that ignores category codes. \end{function} -\chapter{Integers (\texttt{l3int})} +\begin{function}{\StrIfEq,\StrIfEqTF} +\begin{syntax} +\cs{StrIfEq} \Arg{tl_1} \Arg{tl_2} +\cs{StrIfEqTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code} +\end{syntax} +Compares the two \meta{token lists} on a character by character +basis (namely after converting them to strings), +and is \texttt{true} if the two \meta{strings} contain the same +characters in the same order. +%Thus for example +%\begin{codehigh} +%\StrIfEq{abc}{\TlToStr{abc}} +%\end{codehigh} +%is logically \texttt{true}. +See \cs{TlIfEq} to compare +tokens (including their category codes) rather than characters. +For example +\begin{demohigh} +\StrIfEqTF {abc} {abc} {\Result{Yes}} {\Result{No}} +\StrIfEqTF {abc} {xyz} {\Result{Yes}} {\Result{No}} +\end{demohigh} +%\begin{demohigh} +%\StrSet\lTmpaStr{abc} +%\StrSet\lTmpbStr{abc} +%\StrSet\lTmpcStr{xyz} +%\StrIfEqTF{\lTmpaStr}{\lTmpbStr}{\Result{Yes}}{\Result{No}} +%\StrIfEqTF{\lTmpaStr}{\lTmpcStr}{\Result{Yes}}{\Result{No}} +%\StrIfEqTF{\StrUse\lTmpaStr}{\StrUse\lTmpbStr}{\Result{Yes}}{\Result{No}} +%\StrIfEqTF{\StrUse\lTmpaStr}{\StrUse\lTmpcStr}{\Result{Yes}}{\Result{No}} +%\end{demohigh} +\end{function} -\section{Scratch Variables of Integers} +\begin{function}{\StrVarIfEq,\StrVarIfEqTF} +\begin{syntax} +\cs{StrVarIfEq} \meta{str var_1} \meta{str var_2} +\cs{StrVarIfEqTF} \meta{str var_1} \meta{str var_2} \Arg{true code} \Arg{false code} +\end{syntax} +Compares the content of two \meta{str variables} and +is logically \texttt{true} if the two contain the same characters +in the same order. See \cs{TlVarIfEq} to compare tokens +(including their category codes) rather than characters. +\begin{demohigh} +\StrSet \lTmpaStr {abc} +\StrSet \lTmpbStr {abc} +\StrSet \lTmpcStr {xyz} +\StrVarIfEqTF \lTmpaStr \lTmpbStr {\Result{Yes}} {\Result{No}} +\StrVarIfEqTF \lTmpaStr \lTmpcStr {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\StrIfIn,\StrIfInTF} +\begin{syntax} +\cs{StrIfInTF} \Arg{tl_1} \Arg{tl_2} +\cs{StrIfInTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code} +\end{syntax} +Converts both \meta{token lists} to \meta{strings} and +tests whether \meta{string_2} is found inside \meta{string_1}. +\end{function} + +\begin{function}{\StrVarIfIn,\StrVarIfInTF} +\begin{syntax} +\cs{StrVarIfInTF} \meta{str var} \Arg{token list} +\cs{StrVarIfInTF} \meta{str var} \Arg{token list} \Arg{true code} \Arg{false code} +\end{syntax} +Converts the \meta{token list} to a \meta{string} and +tests if that \meta{string} is found in the content of the +\meta{str var}. +\end{function} + +\begin{function}{\StrCompare,\StrCompareTF} +\begin{syntax} +\cs{StrCompare} \Arg{tl_1} \meta{relation} \Arg{tl_2} +\cs{StrCompareTF} \Arg{tl_1} \meta{relation} \Arg{tl_2} \Arg{true code} \Arg{false code} +\end{syntax} +Compares the two \meta{token lists} on a character by character +basis (namely after converting them to strings) in a lexicographic +order according to the character codes of the characters. The +\meta{relation} can be \verb|<|, \verb|=|, or \verb|>| and the test is +\texttt{true} under the following conditions: +\begin{itemize}[nosep] +\item for \verb|<|, if the first string is earlier than the second in lexicographic order; +\item for \verb|=|, if the two strings have exactly the same characters; +\item for \verb|>|, if the first string is later than the second in lexicographic order. +\end{itemize} +For example: +\begin{demohigh} +\StrCompareTF {ab} < {abc} {\Result{Yes}} {\Result{No}} +\StrCompareTF {ab} < {aa} {\Result{Yes}} {\Result{No}} +\end{demohigh} +%\begin{texnote} +%This is a wrapper around the \TeX{} primitive +%\cs{(pdf)strcmp}. It is meant for programming +%and not for sorting textual contents, as it simply considers +%character codes and not more elaborate considerations of grapheme +%clusters, locale, etc. +%\end{texnote} +\par +Due to naming conflict, you need to use \cs{StrIfCompare}/\cs{StrIfCompareTF} as a replacement +if you want to use \verb!functional! package together with \verb!xstring! package. +\end{function} + +\section{String Case Functions} + +\begin{function}{\StrCase} +\begin{syntax} +\cs{StrCase} \Arg{test string} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{string case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{string case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{string case_n} \Arg{code case_n} +~ ~ \verb|}| +\end{syntax} +Compares the \meta{test string} in turn with each +of the \meta{string cases} (all token lists are converted to strings). +If the two are equal (as described for +\cs{StrIfEq}) then the associated \meta{code} is left in the +input stream and other cases are discarded. +\end{function} + +\begin{function}{\StrCaseT} +\begin{syntax} +\cs{StrCaseT} \Arg{test string} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{string case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{string case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{string case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +\end{syntax} +Compares the \meta{test string} in turn with each +of the \meta{string cases} (all token lists are converted to strings). +If the two are equal (as described for +\cs{StrIfEq}) then the associated \meta{code} is left in the +input stream and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case). +\end{function} + +\begin{function}{\StrCaseF} +\begin{syntax} +\cs{StrCaseF} \Arg{test string} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{string case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{string case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{string case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{false code} +\end{syntax} +Compares the \meta{test string} in turn with each +of the \meta{string cases} (all token lists are converted to strings). +If the two are equal (as described for +\cs{StrIfEq}) then the associated \meta{code} is left in the +input stream and other cases are discarded. If none +match then the \meta{false code} is inserted. +\end{function} + +\begin{function}{\StrCaseTF} +\begin{syntax} +\cs{StrCaseTF} \Arg{test string} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{string case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{string case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{string case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +~ ~ \Arg{false code} +\end{syntax} +Compares the \meta{test string} in turn with each +of the \meta{string cases} (all token lists are converted to strings). +If the two are equal (as described for +\cs{StrIfEq}) then the associated \meta{code} is left in the +input stream and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case), while if none +match then the \meta{false code} is inserted. +\end{function} + +\chapter{Integers (\texttt{Int})} + +\section{Constant and Scratch Integers} + +\begin{variable}{\cZeroInt,\cOneInt} +Integer values used with primitive tests and assignments: their +self-terminating nature makes these more convenient and faster than +literal numbers. +\end{variable} + +\begin{variable}{\cMaxInt} +The maximum value that can be stored as an integer. +\end{variable} + +\begin{variable}{\cMaxRegisterInt} +Maximum number of registers. +\end{variable} + +\begin{variable}{\cMaxCharInt} +Maximum character code completely supported by the engine. +\end{variable} \begin{variable}{\lTmpaInt,\lTmpbInt,\lTmpcInt,\lTmpiInt,\lTmpjInt,\lTmpkInt} Scratch integer for local assignment. These are never used by @@ -781,7 +2320,7 @@ function. However, they may be overwritten by other code and so should only be used for short-term storage. \end{variable} -\section{Public Functions for Integers} +\section{Integer Expressions} \begin{function}{\IntEval} \begin{syntax} @@ -793,7 +2332,7 @@ explicit sequence of decimal digits not starting with~\texttt{0}, for negative results \texttt{-}~followed by such a sequence, and \texttt{0}~for zero. For example \begin{demohigh} -\IntEval{(1+4)*(2-3)/5} +\IntEval {(1+4)*(2-3)/5} \end{demohigh} \end{function} @@ -804,7 +2343,7 @@ for negative results \texttt{-}~followed by such a sequence, and Adds \Arg{integer expression_1} and \Arg{integer expression_2}, and returns the result. For example \begin{demohigh} -\IntMathAdd{7}{3} +\IntMathAdd {7} {3} \end{demohigh} \end{function} @@ -812,10 +2351,10 @@ and returns the result. For example \begin{syntax} \cs{IntMathSub} \Arg{integer expression_1} \Arg{integer expression_2} \end{syntax} -Subtracts \Arg{integer expression_1} from \Arg{integer expression_2}, +Subtracts \Arg{integer expression_2} from \Arg{integer expression_1}, and returns the result. For example \begin{demohigh} -\IntMathSub{7}{3} +\IntMathSub {7} {3} \end{demohigh} \end{function} @@ -826,7 +2365,7 @@ and returns the result. For example Multiplies \Arg{integer expression_1} by \Arg{integer expression_2}, and returns the result. For example \begin{demohigh} -\IntMathMult{7}{3} +\IntMathMult {7} {3} \end{demohigh} \end{function} @@ -834,13 +2373,85 @@ and returns the result. For example \begin{syntax} \cs{IntMathDiv} \Arg{integer expression_1} \Arg{integer expression_2} \end{syntax} -Divides \Arg{integer expression_1} by \Arg{integer expression_2}, -and returns the result. For example +Evaluates the two \meta{integer expressions} as described earlier, +then divides the first value by the second, and rounds the result +to the closest integer. Ties are rounded away from zero. Note that +this is identical to using \verb|/| directly in an \meta{integer expression}. +The result is returned as an \meta{integer denotation}. +For example +\begin{demohigh} +\IntMathDiv {8} {3} +\end{demohigh} +\end{function} + +\begin{function}{\IntMathDivTrancate} +\begin{syntax} +\cs{IntMathDivTrancate} \Arg{integer expression_1} \Arg{integer expression_2} +\end{syntax} +Evaluates the two \meta{integer expressions} as described earlier, +then divides the first value by the second, and rounds the result towards zero. +Note that division using \verb|/| rounds to the closest integer instead. +The result is returned as an \meta{integer denotation}. +For example \begin{demohigh} -\IntMathDiv{7}{3} +\IntMathDivTruncate {8} {3} \end{demohigh} \end{function} +\begin{function}{\IntMathSign} +\begin{syntax} +\cs{IntMathSign} \Arg{intexpr} +\end{syntax} +Evaluates the \meta{integer expression} then leaves $1$ or $0$ or +$-1$ in the input stream according to the sign of the result. +\end{function} + +\begin{function}{\IntMathAbs} +\begin{syntax} +\cs{IntMathAbs} \Arg{integer expression} +\end{syntax} +Evaluates the \meta{integer expression} as described for +\cs{IntEval} and leaves the absolute value of the result in +the input stream as an \meta{integer denotation} after two +expansions. +\end{function} + +\begin{function}{\IntMathMax,\IntMathMin} +\begin{syntax} +\cs{IntMathMax} \Arg{intexpr_1} \Arg{intexpr_2} +\cs{IntMathMin} \Arg{intexpr_1} \Arg{intexpr_2} +\end{syntax} +Evaluates the \meta{integer expressions} as described for +\cs{IntEval} and leaves either the larger or smaller value +in the input stream as an \meta{integer denotation} after two +expansions. +\end{function} + +\begin{function}{\IntMathMod} +\begin{syntax} +\cs{IntMathMod} \Arg{intexpr_1} \Arg{intexpr_2} +\end{syntax} +Evaluates the two \meta{integer expressions} as described earlier, +then calculates the integer remainder of dividing the first +expression by the second. This is obtained by subtracting +\cs{IntMathDivTruncate} \Arg{intexpr_1} \Arg{intexpr_2} times +\meta{intexpr_2} from \meta{intexpr_1}. Thus, the result has the +same sign as \meta{intexpr_1} and its absolute value is strictly +less than that of \meta{intexpr_2}. The result is left in the input +stream as an \meta{integer denotation} after two expansions. +\end{function} + +\begin{function}{\IntMathRand} +\begin{syntax} +\cs{IntMathRand} \Arg{intexpr_1} \Arg{intexpr_2} +\end{syntax} +Evaluates the two \meta{integer expressions} and produces a +pseudo-random number between the two (with bounds included). +%This is not available in older versions of \XeTeX{}. +\end{function} + +\section{Creating and Using Integers} + \begin{function}{\IntNew} \begin{syntax} \cs{IntNew} \meta{integer} @@ -850,21 +2461,57 @@ already taken. The declaration is global. The \meta{integer} is initially equal to $0$. \end{function} +\begin{function}{\IntConst} +\begin{syntax} +\cs{IntConst} \meta{integer} \Arg{integer expression} +\end{syntax} +Creates a new constant \meta{integer} or raises an error if the name +is already taken. The value of the \meta{integer} is set +globally to the \meta{integer expression}. +\end{function} + \begin{function}{\IntUse} \begin{syntax} \cs{IntUse} \meta{integer} \end{syntax} Recovers the content of an \meta{integer} and returns the value. -An error is raised if the variable does -not exist or if it is invalid. Can be omitted in places where an -\meta{integer} is required (such as in the first and third arguments -of \cs{IntCompareTF}).% -%\begin{texnote} -%\cs{IntUse} is the \TeX{} primitive \tn{the}: this is one of -%several \verb!functional! names for this primitive. -%\end{texnote} +An error is raised if the variable does not exist or if it is invalid. +\end{function} + +\section{Viewing Integers} + +\begin{function}{\IntLog} +\begin{syntax} +\cs{IntLog} \Arg{integer expression} +\end{syntax} +Writes the result of evaluating the \meta{integer expression} +in the log file. +\end{function} + +\begin{function}{\IntVarLog} +\begin{syntax} +\cs{IntVarLog} \meta{integer} +\end{syntax} +Writes the value of the \meta{integer} in the log file. +\end{function} + +\begin{function}{\IntShow} +\begin{syntax} +\cs{IntShow} \Arg{integer expression} +\end{syntax} +Displays the result of evaluating the \meta{integer expression} +on the terminal. +\end{function} + +\begin{function}{\IntVarShow} +\begin{syntax} +\cs{IntVarShow} \meta{integer} +\end{syntax} +Displays the value of the \meta{integer} on the terminal. \end{function} +\section{Setting Integer Variables} + \begin{function}{\IntSet} \begin{syntax} \cs{IntSet} \meta{integer} \Arg{integer expression} @@ -873,23 +2520,40 @@ Sets \meta{integer} to the value of \meta{integer expression}, which must evaluate to an integer (as described for \cs{IntEval}). For example \begin{demohigh} -\IntSet\lTmpaInt{3+5} -\IntUse\lTmpaInt +\IntSet \lTmpaInt {3+5} +\IntUse \lTmpaInt \end{demohigh} \end{function} +\begin{function}{\IntSetEq} +\begin{syntax} +\cs{IntSetEq} \meta{integer_1} \meta{integer_2} +\end{syntax} +Sets the content of \meta{integer_1} equal to that of +\meta{integer_2}. +\end{function} + \begin{function}{\IntZero} \begin{syntax} \cs{IntZero} \meta{integer} \end{syntax} Sets \meta{integer} to $0$. For example \begin{demohigh} -\IntSet\lTmpaInt{5} -\IntZero\lTmpaInt -\IntUse\lTmpaInt +\IntSet \lTmpaInt {5} +\IntZero \lTmpaInt +\IntUse \lTmpaInt \end{demohigh} \end{function} +\begin{function}{\IntZeroNew} +\begin{syntax} +\cs{IntZeroNew} \meta{integer} +\end{syntax} +Ensures that the \meta{integer} exists globally by applying +\cs{IntNew} if necessary, then applies \cs{IntZero} to leave +the \meta{integer} set to zero. +\end{function} + \begin{function}{\IntIncr} \begin{syntax} \cs{IntIncr} \meta{integer} @@ -897,9 +2561,9 @@ Sets \meta{integer} to $0$. For example Increases the value stored in \meta{integer} by $1$. For example \begin{demohigh} -\IntSet\lTmpaInt{5} -\IntIncr\lTmpaInt -\IntUse\lTmpaInt +\IntSet \lTmpaInt {5} +\IntIncr \lTmpaInt +\IntUse \lTmpaInt \end{demohigh} \end{function} @@ -910,9 +2574,9 @@ For example Decreases the value stored in \meta{integer} by $1$. For example \begin{demohigh} -\IntSet\lTmpaInt{5} -\IntDecr\lTmpaInt -\IntUse\lTmpaInt +\IntSet \lTmpaInt {5} +\IntDecr \lTmpaInt +\IntUse \lTmpaInt \end{demohigh} \end{function} @@ -923,9 +2587,9 @@ For example Adds the result of the \meta{integer expression} to the current content of the \meta{integer}. For example \begin{demohigh} -\IntSet\lTmpaInt{5} -\IntAdd\lTmpaInt{2} -\IntUse\lTmpaInt +\IntSet \lTmpaInt {5} +\IntAdd \lTmpaInt {2} +\IntUse \lTmpaInt \end{demohigh} \end{function} @@ -936,32 +2600,119 @@ content of the \meta{integer}. For example Subtracts the result of the \meta{integer expression} from the current content of the \meta{integer}. For example \begin{demohigh} -\IntSet\lTmpaInt{5} -\IntSub\lTmpaInt{3} -\IntUse\lTmpaInt +\IntSet \lTmpaInt {5} +\IntSub \lTmpaInt {3} +\IntUse \lTmpaInt \end{demohigh} \end{function} +\section{Integer Step Functions} + +%\begin{function}{\IntStepFunction} +%\begin{syntax} +%\cs{IntStepFunction} \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}). The \meta{step} must +%be non-zero. If the \meta{step} is positive, the loop stops when +%the \meta{value} becomes larger than the \meta{final value}. If the +%\meta{step} is negative, the loop stops when the \meta{value} +%becomes smaller than the \meta{final value}. The \meta{function} +%should absorb one numerical argument. For example +%\begin{verbatim} +%\cs_set:Npn \my_func:n #1 { [I~saw~#1] \quad } +%\int_step_function:nnnN { 1 } { 1 } { 5 } \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}{\IntStepInline} +\begin{syntax} +\cs{IntStepInline} \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. +Then for each \meta{value} from the \meta{initial value} to the +\meta{final value} in turn (using \meta{step} between each +\meta{value}), the \meta{code} is inserted into the input stream +with \verb|#1| replaced by the current \meta{value}. Thus the +\meta{code} should define a function of one argument~(\verb|#1|). +For example +\begin{codehigh} +\IgnoreSpacesOn +\TlClear \lTmpaTl +\IntStepInline {1} {3} {30} { + \TlPutRight \lTmpaTl {[#1]} +} +\Result {\Value\lTmpaTl} +\IgnoreSpacesOff +\end{codehigh} +produces [1][4][7][10][13][16][19][22][25][28]. +\end{function} + \begin{function}{\IntStepVariable} \begin{syntax} -\cs{IntStepVariable} \Arg{initial value} \Arg{step} \Arg{final value} \meta{tl~var} \Arg{code} +\cs{IntStepVariable} \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. Then for each \meta{value} from the \meta{initial value} to the \meta{final value} in turn (using \meta{step} between each \meta{value}), the \meta{code} is evaluated, -with the \meta{tl~var} defined as the current \meta{value}. Thus +with the \meta{tl~var} defined as the current \meta{value}. Thus the \meta{code} should make use of the \meta{tl~var}. -For example -\begin{demohigh} -\TlClear\lTmpaTl -\IntStepVariable{1}{3}{30}\lTmpiTl{ - \TlPutRight\lTmpaTl{\Value\lTmpiTl} - \TlPutRight\lTmpaTl{ } -} -\Result{\Value\lTmpaTl} -\end{demohigh} +%For example +%\begin{demohigh} +%\IgnoreSpacesOn +%\TlClear\lTmpaTl +%\IntStepVariable{1}{3}{30}\lTmpiTl{ +% \TlPutRight\lTmpaTl{\Value\lTmpiTl} +% \TlPutRight\lTmpaTl{~} +%} +%\Result{\Value\lTmpaTl} +%\IgnoreSpacesOff +%\end{demohigh} +\end{function} + +\section{Integer Conditionals} + +\begin{function}{\IntIfExist, \IntIfExistTF} +\begin{syntax} +\cs{IntIfExist} \meta{integer} +\cs{IntIfExistTF} \meta{integer} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{integer} is currently defined. This does not +check that the \meta{integer} really is an integer variable. +\end{function} + +\begin{function}{\IntIfOdd,\IntIfOddTF} +\begin{syntax} +\cs{IntIfOdd} \Arg{integer expression} +\cs{IntIfOddTF} \Arg{integer expression} \Arg{true code} \Arg{false code} +\end{syntax} +This function first evaluates the \meta{integer expression} +as described for \cs{IntEval}. +It then evaluates if this is odd or even, as appropriate. +\end{function} + +\begin{function}{\IntIfEven,\IntIfEvenTF} +\begin{syntax} +\cs{IntIfEven} \Arg{integer expression} +\cs{IntIfEvenTF} \Arg{integer expression} \Arg{true code} \Arg{false code} +\end{syntax} +This function first evaluates the \meta{integer expression} +as described for \cs{IntEval}. +It then evaluates if this is even or odd, as appropriate. \end{function} \begin{function}{\IntCompare,\IntCompareTF} @@ -979,11 +2730,1632 @@ Less than & \texttt{<} \\ \end{tabular}\par} For example \begin{demohigh} -\IntCompareTF{2}>{1}{\Result{Greater}}{\Result{Less}} -\IntCompareTF{2}>{3}{\Result{Greater}}{\Result{Less}} +\IntCompareTF {2} > {1} {\Result{Greater}} {\Result{Less}} +\IntCompareTF {2} > {3} {\Result{Greater}} {\Result{Less}} +\end{demohigh} +\end{function} + +\section{Integer Case Functions} + +\begin{function}{\IntCase} +\begin{syntax} +\cs{IntCase} \Arg{test integer expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{intexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{intexpr case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{intexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +\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 +and other cases are discarded. +\end{function} + +\begin{function}{\IntCaseT} +\begin{syntax} +\cs{IntCaseT} \Arg{test integer expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{intexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{intexpr case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{intexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +\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 +and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case). +\end{function} + +\begin{function}{\IntCaseF} +\begin{syntax} +\cs{IntCaseF} \Arg{test integer expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{intexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{intexpr case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{intexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{false code} +\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 +and other cases are discarded. If none +match then the \meta{false code} is into the input stream +(after the code for the appropriate case). +For example +\begin{demohigh} +\IgnoreSpacesOn +\IntCaseF { 2 * 5 } + { + { 5 } { Small } + { 4 + 6 } { Medium } + { -2 * 10 } { Negative } + } + { No idea! } +\IgnoreSpacesOff +\end{demohigh} +\end{function} + +\begin{function}{\IntCaseTF} +\begin{syntax} +\cs{IntCaseTF} \Arg{test integer expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{intexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{intexpr case_2} \Arg{code case_2} +~ ~ ~ ~ $\cdots$ +~ ~ ~ ~ \Arg{intexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +~ ~ \Arg{false code} +\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 +and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case), while if none +match then the \meta{false code} is inserted. +\end{function} + +\chapter{Floating Point Numbers (\texttt{Fp})} + +\section{Constant and Scratch Floating Points} + +\begin{variable}{\cZeroFp, \cMinusZeroFp} +Zero, with either sign. +\end{variable} + +\begin{variable}{\cOneFp} +One as an \texttt{fp}: useful for comparisons in some places. +\end{variable} + +\begin{variable}{\cInfFp,\cMinusInfFp} +Infinity, with either sign. These can be input directly in a +floating point expression as \texttt{inf} and \texttt{-inf}. +\end{variable} + +\begin{variable}{\cEFp} +The value of the base of the natural logarithm, $\mathrm{e} = \exp(1)$. +\end{variable} + +\begin{variable}{\cPiFp} +The value of $\pi$. This can be input directly in a floating point +expression as \texttt{pi}. +\end{variable} + +\begin{variable}{\cOneDegreeFp} +The value of $1^{\circ}$ in radians. Multiply an angle given in +degrees by this value to obtain a result in radians. Note that +trigonometric functions expecting an argument in radians or in +degrees are both available. Within floating point expressions, this +can be accessed as \texttt{deg}. +\end{variable} + +\begin{variable}{\lTmpaFp,\lTmpbFp,\lTmpcFp,\lTmpiFp,\lTmpjFp,\lTmpkFp} +Scratch floating point numbers for local assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\begin{variable}{\gTmpaFp,\gTmpbFp,\gTmpcFp,\gTmpiFp,\gTmpjFp,\gTmpkFp} +Scratch floating point numbers for global assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\section{Floating Point Expressions} + +\begin{function}{\FpEval} +\begin{syntax} +\cs{FpEval} \Arg{floating point expression} +\end{syntax} +Evaluates the \meta{floating point expression} and returns the +result as a decimal number with no +exponent. Leading or trailing zeros may be inserted to compensate +for the exponent. Non-significant trailing zeros are trimmed, and +integers are expressed without a decimal separator. The values +$\pm\infty$ and \nan{} trigger an \enquote{invalid operation} +exception. +For a tuple, each item is converted using \cs{FpEval} and they are combined as +\verb|(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}\verb|)| +if $n>1$ and \verb|(|\meta{fp_1}\verb|,)| or \verb|()| for fewer items. +%This function is identical to \cs{fp_to_decimal:n}. +For example +\begin{demohigh} +\FpEval {(1.2+3.4)*(5.6-7.8)/9} +\end{demohigh} +\end{function} + +\begin{function}{\FpMathAdd} +\begin{syntax} +\cs{FpMathAdd} \Arg{fpexpr_1} \Arg{fpexpr_2} +\end{syntax} +Adds \Arg{fpexpr_1} and \Arg{fpexpr_2}, +and returns the result. For example +\begin{demohigh} +\FpMathAdd {2.8} {3.7} +\FpMathAdd {3.8-1} {2.7+1} +\end{demohigh} +\end{function} + +\begin{function}{\FpMathSub} +\begin{syntax} +\cs{FpMathSub} \Arg{fpexpr_1} \Arg{fpexpr_2} +\end{syntax} +Subtracts \Arg{fpexpr_2} from \Arg{fpexpr_1}, +and returns the result. For example +\begin{demohigh} +\FpMathSub {2.8} {3.7} +\FpMathSub {3.8-1} {2.7+1} +\end{demohigh} +\end{function} + +\begin{function}{\FpMathMult} +\begin{syntax} +\cs{FpMathMult} \Arg{fpexpr_1} \Arg{fpexpr_2} +\end{syntax} +Multiplies \Arg{fpexpr_1} by \Arg{fpexpr_2}, +and returns the result. For example +\begin{demohigh} +\FpMathMult {2.8} {3.7} +\FpMathMult {3.8-1} {2.7+1} +\end{demohigh} +\end{function} + +\begin{function}{\FpMathDiv} +\begin{syntax} +\cs{FpMathDiv} \Arg{fpexpr_1} \Arg{fpexpr_2} +\end{syntax} +Divides \Arg{fpexpr_1} by \Arg{fpexpr_2}, +and returns the result. For example +\begin{demohigh} +\FpMathDiv {2.8} {3.7} +\FpMathDiv {3.8-1} {2.7+1} +\end{demohigh} +\end{function} + +\begin{function}{\FpMathSign} +\begin{syntax} +\cs{FpMathSign} \Arg{fpexpr} +\end{syntax} +Evaluates the \meta{fpexpr} and returns the value +using \cs{FpEval}\verb|{sign(|\meta{result}\verb|)}|: $+1$ for positive +numbers and for $+\infty$, $-1$ for negative numbers and for +$-\infty$, $\pm 0$ for $\pm 0$. If the operand is a tuple or is +\nan{}, then \enquote{invalid operation} occurs and the result +is $0$. For example +\begin{demohigh} +\FpMathSign {3.5} +\FpMathSign {-2.7} \end{demohigh} \end{function} +\begin{function}{\FpMathAbs} +\begin{syntax} +\cs{FpMathAbs} \Arg{floating point expression} +\end{syntax} +Evaluates the \meta{floating point expression} as described for +\cs{FpEval} and returns the absolute value. +If the argument is $\pm\infty$, \nan{} or a tuple, +\enquote{invalid operation} occurs. Within floating point +expressions, \verb|abs()| can be used; it accepts $\pm\infty$ and \nan{} +as arguments. +\end{function} + +\begin{function}{\FpMathMax,\FpMathMin} +\begin{syntax} +\cs{FpMathMax} \Arg{fp expression_1} \Arg{fp expression_2} +\cs{FpMathMin} \Arg{fp expression_1} \Arg{fp expression_2} +\end{syntax} +Evaluates the \meta{floating point expressions} as described for \cs{FpEval} +and returns the resulting larger (\texttt{max}) or smaller (\texttt{min}) value. +If the argument is a tuple, \enquote{invalid operation} occurs, +but no other case raises exceptions. Within floating point expressions, +\verb|max()| and \verb|min()| can be used. +\end{function} + +\section{Creating and Using Floating Points} + +\begin{function}{\FpNew} +\begin{syntax} +\cs{FpNew} \meta{fp var} +\end{syntax} +Creates a new \meta{fp var} or raises an error if the name is +already taken. The declaration is global. The \meta{fp~var} is +initially $+0$. +\end{function} + +\begin{function}{\FpConst} +\begin{syntax} +\cs{FpConst} \meta{fp var} \Arg{floating point expression} +\end{syntax} +Creates a new constant \meta{fp var} or raises an error if the name +is already taken. The \meta{fp var} is set globally equal to +the result of evaluating the \meta{floating point expression}. +For example +\begin{demohigh} +\FpConst \cMyPiFp {3.1415926} +\FpUse \cMyPiFp +\end{demohigh} +\end{function} + +\begin{function}{\FpUse} +\begin{syntax} +\cs{FpUse} \meta{fp var} +\end{syntax} +Recovers the value of the \meta{fp var} and returns the value as a +decimal number with no exponent. +%Leading or trailing zeros may be inserted to compensate for the +%exponent. Non-significant trailing zeros are trimmed. Integers are +%expressed without a decimal separator. The values $\pm\infty$ +%and \nan{} trigger an \enquote{invalid operation} exception. +%For a tuple, each item is converted using \cs{fp_to_decimal:n} and they are combined as +%|(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)| +%if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items. +%This function is identical to \cs{fp_to_decimal:N}. +\end{function} + +\section{Viewing Floating Points} + +\begin{function}{\FpLog} +\begin{syntax} +\cs{FpLog} \Arg{floating point expression} +\end{syntax} +Evaluates the \meta{floating point expression} and writes the +result in the log file. +\end{function} + +\begin{function}{\FpVarLog} +\begin{syntax} +\cs{FpVarLog} \meta{fp var} +\end{syntax} +Writes the value of \meta{fp var} in the log file. +\end{function} + +\begin{function}{\FpShow} +\begin{syntax} +\cs{FpShow} \Arg{floating point expression} +\end{syntax} +Evaluates the \meta{floating point expression} and displays the +result in the terminal. +\end{function} + +\begin{function}{\FpVarShow} +\begin{syntax} +\cs{FpVarShow} \meta{fp var} +\end{syntax} +Displays the value of \meta{fp var} in the terminal. +\end{function} + +\section{Setting Floating Point Variables} + +\begin{function}{\FpSet} +\begin{syntax} +\cs{FpSet} \meta{fp var} \Arg{floating point expression} +\end{syntax} +Sets \meta{fp var} equal to the result of computing the +\meta{floating point expression}. For example +\begin{demohigh} +\FpSet \lTmpaFp {4/7} +\FpUse \lTmpaFp +\end{demohigh} +\end{function} + +\begin{function}{\FpSetEq} +\begin{syntax} +\cs{FpSetEq} \meta{fp var_1} \meta{fp var_2} +\end{syntax} +Sets the floating point variable \meta{fp var_1} equal to the current +value of \meta{fp var_2}. +\end{function} + +\begin{function}{\FpZero} +\begin{syntax} +\cs{FpZero} \meta{fp var} +\end{syntax} +Sets the \meta{fp var} to $+0$. For example +\begin{demohigh} +\FpSet \lTmpaFp {5.3} +\FpZero \lTmpaFp +\FpUse \lTmpaFp +\end{demohigh} +\end{function} + +\begin{function}{\FpZeroNew} +\begin{syntax} +\cs{FpZeroNew} \meta{fp var} +\end{syntax} +Ensures that the \meta{fp var} exists globally +by applying \cs{FpNew} if necessary, then applies +\cs{FpZero} to leave the \meta{fp var} set to $+0$. +\end{function} + +\begin{function}{\FpAdd} +\begin{syntax} +\cs{FpAdd} \meta{fp var} \Arg{floating point expression} +\end{syntax} +Adds the result of computing the \meta{floating point expression} to +the \meta{fp var}. +This also applies if \meta{fp var} and \meta{floating point +expression} evaluate to tuples of the same size. For example +\begin{demohigh} +\FpSet \lTmpaFp {5.3} +\FpAdd \lTmpaFp {2.11} +\FpUse \lTmpaFp +\end{demohigh} +\end{function} + +\begin{function}{\FpSub} +\begin{syntax} +\cs{FpSub} \meta{fp var} \Arg{floating point expression} +\end{syntax} +Subtracts the result of computing the \meta{floating point +expression} from the \meta{fp var}. +This also applies if \meta{fp var} and \meta{floating point +expression} evaluate to tuples of the same size. For example +\begin{demohigh} +\FpSet \lTmpaFp {5.3} +\FpSub \lTmpaFp {2.11} +\FpUse \lTmpaFp +\end{demohigh} +\end{function} + +\section{Floating Point Step Functions} + +%\begin{function}{\FpStepFunction} +%\begin{syntax} +%\cs{FpStepFunction} \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}, each of which should be a floating point +%expression evaluating to a floating point number, not a tuple. +%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}). The \meta{step} must +%be non-zero. If the \meta{step} is positive, the loop stops when +%the \meta{value} becomes larger than the \meta{final value}. If the +%\meta{step} is negative, the loop stops when the \meta{value} +%becomes smaller than the \meta{final value}. The \meta{function} +%should absorb one numerical argument. For example +%\begin{verbatim} +%\cs_set:Npn \my_func:n #1 { [I saw #1] \quad } +%\fp_step_function:nnnN { 1.0 } { 0.1 } { 1.5 } \my_func:n +%\end{verbatim} +%would print +%\begin{quote} +%[I saw 1.0] \quad +%[I saw 1.1] \quad +%[I saw 1.2] \quad +%[I saw 1.3] \quad +%[I saw 1.4] \quad +%[I saw 1.5] \quad +%\end{quote} +%\begin{texnote} +%Due to rounding, it may happen that adding the \meta{step} to the +%\meta{value} does not change the \meta{value}; such cases give an +%error, as they would otherwise lead to an infinite loop. +%\end{texnote} +%\end{function} + +\begin{function}{\FpStepInline} +\begin{syntax} +\cs{FpStepInline} \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 floating point +expressions evaluating to a floating point number, not a tuple. +Then for each \meta{value} from the \meta{initial value} to the +\meta{final value} in turn (using \meta{step} between each +\meta{value}), the \meta{code} is inserted into the input stream +with \verb|#1| replaced by the current \meta{value}. Thus the +\meta{code} should define a function of one argument (\verb|#1|). +For example +\begin{codehigh} +\IgnoreSpacesOn +\TlClear \lTmpaTl +\FpStepInline {1} {0.1} {1.5} { + \TlPutRight \lTmpaTl {[#1]} +} +\Result {\Value\lTmpaTl} +\IgnoreSpacesOff +\end{codehigh} +produces [1][1.1][1.2][1.3][1.4][1.5]. +\end{function} + +\begin{function}{\FpStepVariable} +\begin{syntax} +\cs{FpStepVariable} \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 floating point +expressions evaluating to a floating point number, not a tuple. +Then for each \meta{value} from the \meta{initial value} to the +\meta{final value} in turn (using \meta{step} between each +\meta{value}), 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}. +%For example +%\begin{demohigh} +%\IgnoreSpacesOn +%\TlClear\lTmpaTl +%\FpStepVariable{1}{0.1}{1.5}\lTmpiTl{ +% \TlPutRight\lTmpaTl{\Value\lTmpiTl} +% \TlPutRight\lTmpaTl{~} +%} +%\Result{\Value\lTmpaTl} +%\IgnoreSpacesOff +%\end{demohigh} +\end{function} + +\section{Float Point Conditionals} + +\begin{function}{\FpIfExist,\FpIfExistTF} +\begin{syntax} +\cs{FpIfExist} \meta{fp var} +\cs{FpIfExistTF} \meta{fp var} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{fp var} is currently defined. This does not +check that the \meta{fp var} really is a floating point variable. +For example +\begin{demohigh} +\FpIfExistTF \lTmpaFp {\Result{Yes}} {\Result{No}} +\FpIfExistTF \lMyUndefinedFp {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\FpCompare,\FpCompareTF} +\begin{syntax} +\cs{FpCompare} \Arg{fpexpr_1} \meta{relation} \Arg{fpexpr_2} +\cs{FpCompareTF} \Arg{fpexpr_1} \meta{relation} \Arg{fpexpr_2} \Arg{true code} \Arg{false code} +\end{syntax} +Compares the \meta{fpexpr_1} and the \meta{fpexpr_2}, and returns +\texttt{true} if the \meta{relation} is obeyed. For example +\begin{demohigh} +\FpCompareTF {1} > {0.9999} {\Result{Greater}} {\Result{Less}} +\FpCompareTF {1} > {1.0001} {\Result{Greater}} {\Result{Less}} +\end{demohigh} +Two floating points +$x$ and $y$ may obey four mutually exclusive relations: +$x<y$, $x=y$, $x>y$, or $x?y$ (\enquote{not ordered}). The last +case occurs exactly if one or both operands is \nan{} or is a tuple, +unless they are equal tuples. Note that a \nan{} is distinct from +any value, even another \nan{}, hence $x=x$ is not true for +a \nan{}. To test if a value is \nan{}, compare it to an arbitrary +number with the \enquote{not ordered} relation.\par +%\begin{demohigh} +%\FpCompareTF{0/0}?{0}{\Result{Is~a~Nan}}{\Result{Isn't~a~NaN}} +%\end{demohigh} +Tuples are equal if they have the same number of items and items +compare equal (in particular there must be no \nan{}). +At present any other comparison with tuples yields \verb|?| (not ordered). +This is experimental. +\end{function} + +\chapter{Dimensions (\texttt{Dim})} + +\section{Constant and Scratch Dimensions} + +\begin{variable}{\cMaxDim} +The maximum value that can be stored as a dimension. This can also +be used as a component of a skip. +\end{variable} + +\begin{variable}{\cZeroDim} +A zero length as a dimension. This can also be used as a component +of a skip. +\end{variable} + +\begin{variable}{\lTmpaDim,\lTmpbDim,\lTmpcDim,\lTmpiDim,\lTmpjDim,\lTmpkDim} +Scratch dimensions for local assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\begin{variable}{\gTmpaDim,\gTmpbDim,\gTmpcDim,\gTmpiDim,\gTmpjDim,\gTmpkDim} +Scratch dimensions for global assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\section{Dimension Expressions} + +\begin{function}{\DimEval} +\begin{syntax} +\cs{DimEval} \Arg{dimension expression} +\end{syntax} +Evaluates the \meta{dimension expression}, expanding any +dimensions and token list variables within the \meta{expression} +to their content (without requiring \cs{DimUse}/\cs{TlUse}) +and applying the standard mathematical rules. The result of the +calculation is returned as a \meta{dimension denotation}. +%This is expressed in points (\texttt{pt}), and requires suitable +%termination if used in a \TeX{}-style assignment as it is \emph{not} +%an \meta{internal dimension}. +For example +\begin{demohigh} +\DimEval {(1.2pt+3.4pt)/9} +\end{demohigh} +%\DimEval{(1.2pt+3.4pt)*(5.6-7.8)/9} +\end{function} + +\begin{function}{\DimMathAdd} +\begin{syntax} +\cs{DimMathAdd} \Arg{dimexpr_1} \Arg{dimexpr_2} +\end{syntax} +Adds \Arg{dimexpr_1} and \Arg{dimexpr_2}, +and returns the result. For example +\begin{demohigh} +\DimMathAdd {2.8pt} {3.7pt} +\DimMathAdd {3.8pt-1pt} {2.7pt+1pt} +\end{demohigh} +\end{function} + +\begin{function}{\DimMathSub} +\begin{syntax} +\cs{DimMathSub} \Arg{dimexpr_1} \Arg{dimexpr_2} +\end{syntax} +Subtracts \Arg{dimexpr_2} from \Arg{dimexpr_1}, +and returns the result. For example +\begin{demohigh} +\DimMathSub {2.8pt} {3.7pt} +\DimMathSub {3.8pt-1pt} {2.7pt+1pt} +\end{demohigh} +\end{function} + +\begin{function}{\DimMathRatio} +\begin{syntax} +\cs{DimMathRatio} \Arg{dimexpr_1} \Arg{dimexpr_2} +\end{syntax} +Parses the two \meta{dimension expressions}, +then calculates the ratio of the two and returns it. +The result is a ratio expression +between two integers, with all distances converted to scaled points. +For example +\begin{demohigh} +\DimMathRatio {5pt} {10pt} +\end{demohigh} +The returned value is suitable for use inside a \meta{dimension expression} +such as +\begin{codehigh} +\DimSet \lTmpaDim {10pt*\DimMathRatio{5pt}{10pt}} +\end{codehigh} +\end{function} + +\begin{function}{\DimMathSign} +\begin{syntax} +\cs{DimMathSign} \Arg{dimexpr} +\end{syntax} +Evaluates the \meta{dimexpr} then returns $1$ or $0$ or $-1$ +according to the sign of the result. For example +\begin{demohigh} +\DimMathSign {3.5pt} +\DimMathSign {-2.7pt} +\end{demohigh} +\end{function} + +\begin{function}{\DimMathAbs} +\begin{syntax} +\cs{DimMathAbs} \Arg{dimexpr} +\end{syntax} +Converts the \meta{dimexpr} to its absolute value, +returning the result as a \meta{dimension denotation}. +For example +\begin{demohigh} +\DimMathAbs {3.5pt} +\DimMathAbs {-2.7pt} +\end{demohigh} +\end{function} + +\begin{function}{\DimMathMax,\DimMathMin} +\begin{syntax} +\cs{DimMathMax} \Arg{dimexpr_1} \Arg{dimexpr_2} +\cs{DimMathMin} \Arg{dimexpr_1} \Arg{dimexpr_2} +\end{syntax} +Evaluates the two \meta{dimension expressions} and returns either the +maximum or minimum value as appropriate as a \meta{dimension denotation}. +For example +\begin{demohigh} +\DimMathMax {3.5pt} {-2.7pt} +\DimMathMin {3.5pt} {-2.7pt} +\end{demohigh} +\end{function} + +\section{Creating and Using Dimensions} + +\begin{function}{\DimNew} +\begin{syntax} +\cs{DimNew} \meta{dimension} +\end{syntax} +Creates a new \meta{dimension} or raises an error if the name is +already taken. The declaration is global. The \meta{dimension} +is initially equal to $0$\,pt. +\end{function} + +\begin{function}{\DimConst} +\begin{syntax} +\cs{DimConst} \meta{dimension} \Arg{dimension expression} +\end{syntax} +Creates a new constant \meta{dimension} or raises an error if the +name is already taken. The value of the \meta{dimension} is set +globally to the \meta{dimension expression}. For example +\begin{demohigh} +\DimConst \cFooSomeDim {1cm} +\DimUse \cFooSomeDim +\end{demohigh} +\end{function} + +\begin{function}{\DimUse} +\begin{syntax} +\cs{DimUse} \meta{dimension} +\end{syntax} +Recovers the content of a \meta{dimension} and returns the value. +An error is raised if the variable does not exist or if it is invalid. +\end{function} + +\section{Viewing Dimensions} + +\begin{function}{\DimLog} +\begin{syntax} +\cs{DimLog} \Arg{dimension expression} +\end{syntax} +Writes the result of evaluating the \meta{dimension expression} +in the log file. For example +\begin{codehigh} +\DimLog {\lFooSomeDim+1cm} +\end{codehigh} +\end{function} + +\begin{function}{\DimVarLog} +\begin{syntax} +\cs{DimVarLog} \meta{dimension} +\end{syntax} +Writes the value of the \meta{dimension} in the log file. For example +\begin{codehigh} +\DimVarLog \lFooSomeDim +\end{codehigh} +\end{function} + +\begin{function}{\DimShow} +\begin{syntax} +\cs{DimShow} \Arg{dimension expression} +\end{syntax} +Displays the result of evaluating the \meta{dimension expression} +on the terminal. For example +\begin{codehigh} +\DimShow {\lFooSomeDim+1cm} +\end{codehigh} +\end{function} + +\begin{function}{\DimVarShow} +\begin{syntax} +\cs{DimVarShow} \meta{dimension} +\end{syntax} +Displays the value of the \meta{dimension} on the terminal. For example +\begin{codehigh} +\DimVarShow \lFooSomeDim +\end{codehigh} +\end{function} + +\section{Setting Dimension Variables} + +\begin{function}{\DimSet} +\begin{syntax} +\cs{DimSet} \meta{dimension} \Arg{dimension expression} +\end{syntax} +Sets \meta{dimension} to the value of \meta{dimension expression}, which +must evaluate to a length with units. +\end{function} + +\begin{function}{\DimSetEq} +\begin{syntax} +\cs{DimSetEq} \meta{dimension_1} \meta{dimension_2} +\end{syntax} +Sets the content of \meta{dimension_1} equal to that of +\meta{dimension_2}. For example +\begin{demohigh} +\DimSet \lTmpaDim {10pt} +\DimSetEq \lTmpbDim \lTmpaDim +\DimUse \lTmpbDim +\end{demohigh} +\end{function} + +\begin{function}{\DimZero} +\begin{syntax} +\cs{DimZero} \meta{dimension} +\end{syntax} +Sets \meta{dimension} to $0$\,pt. For example +\begin{demohigh} +\DimSet \lTmpaDim {1em} +\DimZero \lTmpaDim +\DimUse \lTmpaDim +\end{demohigh} +\end{function} + +\begin{function}{\DimZeroNew} +\begin{syntax} +\cs{DimZeroNew} \meta{dimension} +\end{syntax} +Ensures that the \meta{dimension} exists globally by applying +\cs{DimNew} if necessary, then applies +\cs{DimZero} to set the \meta{dimension} to zero. For example +\begin{demohigh} +\DimZeroNew \lFooSomeDim +\DimUse \lFooSomeDim +\end{demohigh} +\end{function} + +\begin{function}{\DimAdd} +\begin{syntax} +\cs{DimAdd} \meta{dimension} \Arg{dimension expression} +\end{syntax} +Adds the result of the \meta{dimension expression} to the current +content of the \meta{dimension}. For example +\begin{demohigh} +\DimSet \lTmpaDim {5.3pt} +\DimAdd \lTmpaDim {2.11pt} +\DimUse \lTmpaDim +\end{demohigh} +\end{function} + +\begin{function}{\DimSub} +\begin{syntax} +\cs{DimSub} \meta{dimension} \Arg{dimension expression} +\end{syntax} +Subtracts the result of the \meta{dimension expression} from the +current content of the \meta{dimension}. For example +\begin{demohigh} +\DimSet \lTmpaDim {5.3pt} +\DimSub \lTmpaDim {2.11pt} +\DimUse \lTmpaDim +\end{demohigh} +\end{function} + +\section{Dimension Step Functions} + +%\begin{function}{\DimStepFunction} +%\begin{syntax} +%\cs{DimStepFunction} \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 dimension 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}). The \meta{step} must +%be non-zero. If the \meta{step} is positive, the loop stops when +%the \meta{value} becomes larger than the \meta{final value}. If the +%\meta{step} is negative, the loop stops when the \meta{value} +%becomes smaller than the \meta{final value}. The \meta{function} +%should absorb one argument. +%\end{function} + +\begin{function}{\DimStepInline} +\begin{syntax} +\cs{DimStepInline} \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 dimension expressions. +Then for each \meta{value} from the \meta{initial value} to the +\meta{final value} in turn (using \meta{step} between each +\meta{value}), the \meta{code} is inserted into the input stream +with \verb|#1| replaced by the current \meta{value}. Thus the +\meta{code} should define a function of one argument (\verb|#1|). +For example +\begin{codehigh} +\IgnoreSpacesOn +\TlClear \lTmpaTl +\DimStepInline {1pt} {0.1pt} {1.5pt} { + \TlPutRight \lTmpaTl {[#1]} +} +\Result {\Value\lTmpaTl} +\IgnoreSpacesOff +\end{codehigh} +produces [1.0pt][1.1pt][1.20001pt][1.30002pt][1.40002pt]. +\end{function} + +\begin{function}{\DimStepVariable} +\begin{syntax} +\cs{DimStepVariable} \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 dimension expressions. +Then for each \meta{value} from the \meta{initial value} to the +\meta{final value} in turn (using \meta{step} between each +\meta{value}), 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}. +%For example +%\begin{demohigh} +%\IgnoreSpacesOn +%\TlClear\lTmpaTl +%\DimStepVariable{1pt}{0.1pt}{1.5pt}\lTmpiTl{ +% \TlPutRight\lTmpaTl{\Value\lTmpiTl} +% \TlPutRight\lTmpaTl{~} +%} +%\Result{\Value\lTmpaTl} +%\IgnoreSpacesOff +%\end{demohigh} +\end{function} + +\section{Dimension Conditionals} + +\begin{function}{\DimIfExist,\DimIfExistTF} +\begin{syntax} +\cs{DimIfExist} \meta{dimension} +\cs{DimIfExistTF} \meta{dimension} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{dimension} is currently defined. This does +not check that the \meta{dimension} really is a dimension variable. +For example +\begin{demohigh} +\DimIfExistTF \lTmpaDim {\Result{Yes}} {\Result{No}} +\DimIfExistTF \lFooUndefinedDim {\Result{Yes}} {\Result{No}} +\end{demohigh} +\end{function} + +\begin{function}{\DimCompare,\DimCompareTF} +\begin{syntax} +\cs{DimCompare} \Arg{dimexpr_1} \meta{relation} \Arg{dimexpr_2} +\cs{DimCompareTF} \Arg{dimexpr_1} \meta{relation} \Arg{dimexpr_2} \Arg{true code} \Arg{false code} +\end{syntax} +This function first evaluates each of the \meta{dimension expressions} +as described for \cs{DimEval}. The two results are then +compared using the \meta{relation}:\par +{\centering +\begin{tabular}{ll} +Equal & \verb|=| \\ +Greater than & \verb|>| \\ +Less than & \verb|<| \\ +\end{tabular}\par} +For example +\begin{demohigh} +\DimCompareTF {1pt} > {0.9999pt} {\Result{Greater}} {\Result{Less}} +\DimCompareTF {1pt} > {1.0001pt} {\Result{Greater}} {\Result{Less}} +\end{demohigh} +\end{function} + +\section{Dimension Case Functions} + +\begin{function}{\DimCase} +\begin{syntax} +\cs{DimCase} \Arg{test dimension expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{dimexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{dimexpr case_2} \Arg{code case_2} +~ ~ ~ ~ \ldots +~ ~ ~ ~ \Arg{dimexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +\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 +and other cases are discarded. +\end{function} + +\begin{function}{\DimCaseT} +\begin{syntax} +\cs{DimCaseT} \Arg{test dimension expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{dimexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{dimexpr case_2} \Arg{code case_2} +~ ~ ~ ~ \ldots +~ ~ ~ ~ \Arg{dimexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +\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 +and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case). +\end{function} + +\begin{function}{\DimCaseF} +\begin{syntax} +\cs{DimCaseF} \Arg{test dimension expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{dimexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{dimexpr case_2} \Arg{code case_2} +~ ~ ~ ~ \ldots +~ ~ ~ ~ \Arg{dimexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{false code} +\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 +and other cases are discarded. If none of the cases +match then the \meta{false code} is inserted. +For example +\begin{demohigh} +\IgnoreSpacesOn +\DimSet \lTmpaDim {5pt} +\DimCaseF {2\lTmpaDim} { + {5pt} {\Result{Small}} + {4pt+6pt} {\Result{Medium}} + {-10pt} {\Result{Negative}} +}{ + \Result {No Match} +} +\IgnoreSpacesOff +\end{demohigh} +\end{function} + +\begin{function}{\DimCaseTF} +\begin{syntax} +\cs{DimCaseTF} \Arg{test dimension expression} +~ ~ \verb|{| +~ ~ ~ ~ \Arg{dimexpr case_1} \Arg{code case_1} +~ ~ ~ ~ \Arg{dimexpr case_2} \Arg{code case_2} +~ ~ ~ ~ \ldots +~ ~ ~ ~ \Arg{dimexpr case_n} \Arg{code case_n} +~ ~ \verb|}| +~ ~ \Arg{true code} +~ ~ \Arg{false code} +\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 +and other cases are discarded. If any of the +cases are matched, the \meta{true code} is also inserted into the +input stream (after the code for the appropriate case), while if none +match then the \meta{false code} is inserted. +%For example +%\begin{demohigh} +%\IgnoreSpacesOn +%\DimSet\lTmpaDim{5pt} +%\DimCaseTF{2\lTmpaDim}{ +% {5pt} {\Result{Small}} +% {4pt+6pt} {\Result{Medium}} +% {-10pt} {\Result{Negative}} +%}{ +% \Result{[Some Match]} +%}{ +% \Result{[No Match]} +%} +%\IgnoreSpacesOff +%\end{demohigh} +\end{function} + +\chapter{Comma Separated Lists (\texttt{Clist})} + +\section{Constant and Scratch Comma Lists} + +\begin{variable}{\cEmptyClist} +Constant that is always empty. +\end{variable} + +\begin{variable}{\lTmpaClist,\lTmpbClist,\lTmpcClist,\lTmpiClist,\lTmpjClist,\lTmpkClist} +Scratch comma lists for local assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\begin{variable}{\gTmpaClist,\gTmpbClist,\gTmpcClist,\gTmpiClist,\gTmpjClist,\gTmpkClist} +Scratch comma lists for global assignment. These are never used by +the \verb!functional! package, and so are safe for use with any +function. However, they may be overwritten by other +code and so should only be used for short-term storage. +\end{variable} + +\section{Creating and Using Comma Lists} + +\begin{function}{\ClistNew} +\begin{syntax} +\cs{ClistNew} \meta{comma list} +\end{syntax} +Creates a new \meta{comma list} or raises an error if the name is +already taken. The declaration is global. The \meta{comma list} +initially contains no items. +\end{function} + +\begin{function}{\ClistConst} +\begin{syntax} +\cs{ClistConst} \meta{clist var} \Arg{comma list} +\end{syntax} +Creates a new constant \meta{clist var} or raises an error +if the name is already taken. The value of the +\meta{clist var} is set globally to the +\meta{comma list}. +\end{function} + +\begin{function}{\ClistVarJoin} +\begin{syntax} +\cs{ClistVarJoin} \meta{clist var} \Arg{separator} +\end{syntax} +Returns the contents of the \meta{clist var}, +with the \meta{separator} between the items. +%If the comma list has a single item, it is placed in the input stream, +%and a comma list with no items produces no output. +%An error is raised if the variable does not exist or if it is invalid. +For example, +\begin{demohigh} +\ClistSet \lTmpaClist { a , b , , c , {de} , f } +\ClistVarJoin \lTmpaClist { and } +\end{demohigh} +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{items} +%do not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\ClistVarJoinExtended} +\begin{syntax} +\cs{ClistVarJoinExtended} \meta{clist var} \Arg{separator between two} \Arg{separator between more than two} \Arg{separator between final two} +\end{syntax} +Returns the contents of the \meta{clist var}, +with the appropriate \meta{separator} between the items. Namely, if +the comma list has more than two items, the \meta{separator between +more than two} is placed between each pair of items except the +last, for which the \meta{separator between final two} is used. If +the comma list has exactly two items, then they are joined with +the \meta{separator between two} and returns. +%If the comma list has a single item, it is placed in the input stream, +%and a comma list with no items produces no output. +%An error is raised if the variable does not exist or if it is invalid. +For example, +\begin{demohigh} +\ClistSet \lTmpaClist { a , b , , c , {de} , f } +\ClistVarJoinExtended \lTmpaClist { and } {, } {, and } +\end{demohigh} +The first separator argument is not used in this case +because the comma list has more than $2$ items. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{items} +%do not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\ClistJoin,\ClistJoinExtended} +\begin{syntax} +\cs{ClistJoin} \meta{comma list} \Arg{separator} +\cs{ClistJoinExtended} \meta{comma list} \Arg{separator between two} \Arg{separator between more than two} \Arg{separator between final two} +\end{syntax} +Returns the contents of the \meta{comma list}, +with the appropriate \meta{separator} between the items. As for +\cs{ClistSet}, blank items are omitted, spaces are removed from +both sides of each item, then a set of braces is removed if the +resulting space-trimmed item is braced. The \meta{separators} are +then inserted in the same way as for \cs{ClistVarJoin} and +\cs{ClistVarJoinExtended}, respectively. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{items} +%do not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\section{Viewing Comma Lists} + +\begin{function}{\ClistLog} +\begin{syntax} +\cs{ClistLog} \Arg{tokens} +\end{syntax} +Writes the entries in the comma list in the log file. See also +\cs{ClistShow} which displays the result in the terminal. +\end{function} + +\begin{function}{\ClistVarLog} +\begin{syntax} +\cs{ClistVarLog} \meta{comma list} +\end{syntax} +Writes the entries in the \meta{comma list} in the log file. See +also \cs{ClistVarShow} which displays the result in the terminal. +\end{function} + +\begin{function}{\ClistShow} +\begin{syntax} +\cs{ClistShow} \Arg{tokens} +\end{syntax} +Displays the entries in the comma list in the terminal. +\end{function} + +\begin{function}{\ClistVarShow} +\begin{syntax} +\cs{ClistVarShow} \meta{comma list} +\end{syntax} +Displays the entries in the \meta{comma list} in the terminal. +\end{function} + +\section{Setting Comma Lists} + +\begin{function}{\ClistSet} +\begin{syntax} +\cs{ClistSet} \meta{comma list} \verb|{|\meta{item_1},\ldots{},\meta{item_n}\verb|}| +\end{syntax} +Sets \meta{comma list} to contain the \meta{items}, +removing any previous content from the variable. +Blank items are omitted, spaces are removed from both sides of each +item, then a set of braces is removed if the resulting space-trimmed +item is braced. +To store some \meta{tokens} as a single \meta{item} even if the +\meta{tokens} contain commas or spaces, add a set of braces: +\cs{ClistSet} \meta{comma list} \verb|{| \Arg{tokens} \verb|}|. +\end{function} + +\begin{function}{\ClistSetEq} +\begin{syntax} +\cs{ClistSetEq} \meta{comma list_1} \meta{comma list_2} +\end{syntax} +Sets the content of \meta{comma list_1} equal to that of +\meta{comma list_2}. To set a token list variable equal to a comma +list variable, use \cs{TlSetEq}. Conversely, setting a comma +list variable to a token list is unadvisable unless one checks +space-trimming and related issues. +\end{function} + +%\begin{function}{\ClistSetFromSeq} +%\begin{syntax} +%\cs{ClistSetFromSeq} \meta{comma list} \meta{sequence} +%\end{syntax} +%Converts the data in the \meta{sequence} into a \meta{comma list}: +%the original \meta{sequence} is unchanged. +%Items which contain either spaces or commas are surrounded by braces. +%\end{function} + +\begin{function}{\ClistClear} +\begin{syntax} +\cs{ClistClear} \meta{comma list} +\end{syntax} +Clears all items from the \meta{comma list}. +\end{function} + +\begin{function}{\ClistClearNew} +\begin{syntax} +\cs{ClistClearNew} \meta{comma list} +\end{syntax} +Ensures that the \meta{comma list} exists globally by applying +\cs{ClistNew} if necessary, then applies \cs{ClistClear} to leave +the list empty. +\end{function} + +\begin{function}{\ClistConcat} +\begin{syntax} +\cs{ClistConcat} \meta{comma list_1} \meta{comma list_2} \meta{comma list_3} +\end{syntax} +Concatenates the content of \meta{comma list_2} and \meta{comma list_3} +together and saves the result in \meta{comma list_1}. The items in +\meta{comma list_2} are placed at the left side of the new comma list. +\end{function} + +\begin{function}{\ClistPutLeft} +\begin{syntax} +\cs{ClistPutLeft} \meta{comma list} \verb|{|\meta{item_1},\ldots{},\meta{item_n}\verb|}| +\end{syntax} +Appends the \meta{items} to the left of the \meta{comma list}. +Blank items are omitted, spaces are removed from both sides of each +item, then a set of braces is removed if the resulting space-trimmed +item is braced. +To append some \meta{tokens} as a single \meta{item} even if the +\meta{tokens} contain commas or spaces, add a set of braces: +\cs{ClistPutLeft} \meta{comma list} \verb|{| \Arg{tokens} \verb|}|. +\end{function} + +\begin{function}{\ClistPutRight} +\begin{syntax} +\cs{ClistPutRight} \meta{comma list} \verb|{|\meta{item_1},\ldots{},\meta{item_n}\verb|}| +\end{syntax} +Appends the \meta{items} to the right of the \meta{comma list}. +Blank items are omitted, spaces are removed from both sides of each +item, then a set of braces is removed if the resulting space-trimmed +item is braced. +To append some \meta{tokens} as a single \meta{item} even if the +\meta{tokens} contain commas or spaces, add a set of braces: +\cs{ClistPutRight} \meta{comma list} \verb|{| \Arg{tokens} \verb|}|. +\end{function} + +\section{Modifying Comma Lists} + +While comma lists are normally used as ordered lists, it may be +necessary to modify the content. The functions here may be used +to update comma lists, while retaining the order of the unaffected +entries. + +\begin{function}{\ClistRemoveDuplicates} +\begin{syntax} +\cs{ClistRemoveDuplicates} \meta{comma list} +\end{syntax} +Removes duplicate items from the \meta{comma list}, leaving the +left most copy of each item in the \meta{comma list}. The \meta{item} +comparison takes place on a token basis, as for \cs{TlIfEqTF}. +\begin{texnote} +This function iterates through every item in the \meta{comma list} and +does a comparison with the \meta{items} already checked. It is therefore +relatively slow with large comma lists. +Furthermore, it may fail if any of the items in the +\meta{comma list} contains \verb|{|, \verb|}|, or \verb|#| +(assuming the usual \TeX{} category codes apply). +\end{texnote} +\end{function} + +\begin{function}{\ClistRemoveAll} +\begin{syntax} +\cs{ClistRemoveAll} \meta{comma list} \Arg{item} +\end{syntax} +Removes every occurrence of \meta{item} from the \meta{comma list}. +The \meta{item} comparison takes place on a token basis, as for +\cs{TlIfEqTF}. +\begin{texnote} +The function may fail if the \meta{item} contains \verb|{|, \verb|}|, or \verb|#| +(assuming the usual \TeX{} category codes apply). +\end{texnote} +\end{function} + +\begin{function}{\ClistReverse} +\begin{syntax} +\cs{ClistReverse} \meta{comma list} +\end{syntax} +Reverses the order of items stored in the \meta{comma list}. +\end{function} + +%\begin{function}{\ClistSort} +%\begin{syntax} +%\cs{ClistSort} \meta{clist var} \Arg{comparison code} +%\end{syntax} +%Sorts the items in the \meta{clist var} according to the +%\meta{comparison code}, and assigns the result to +%\meta{clist var}. The details of sorting comparison are +%described in Section \ref{sec:l3sort:mech}. +%\end{function} + +\section{Working with the Contents of Comma Lists} + +\begin{function}{\ClistCount,\ClistVarCount} +\begin{syntax} +\cs{ClistCount} \Arg{comma list} +\cs{ClistVarCount} \meta{comma list} +\end{syntax} +Returns the number of items in the \meta{comma list} +as an \meta{integer denotation}. The total number of items +in a \meta{comma list} includes those which are duplicates, +\emph{i.e.} every item in a \meta{comma list} is counted. +\end{function} + +\begin{function}{\ClistItem} +\begin{syntax} +\cs{ClistItem} \Arg{comma list} \Arg{integer expression} +\end{syntax} +Indexing items in the \meta{comma list} from $1$ at the top (left), this +function evaluates the \meta{integer expression} and returns the +appropriate item from the comma list. If the +\meta{integer expression} is negative, indexing occurs from the +bottom (right) of the comma list. When the \meta{integer expression} +is larger than the number of items in the \meta{comma list} (as +calculated by \cs{ClistCount}) then the function returns nothing. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{item} +%does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\ClistVarItem} +\begin{syntax} +\cs{ClistVarItem} \meta{comma list} \Arg{integer expression} +\end{syntax} +Indexing items in the \meta{comma list} from $1$ at the top (left), this +function evaluates the \meta{integer expression} and returns the +appropriate item from the comma list. If the +\meta{integer expression} is negative, indexing occurs from the +bottom (right) of the comma list. When the \meta{integer expression} +is larger than the number of items in the \meta{comma list} (as +calculated by \cs{ClistVarCount}) then the function returns nothing. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{item} +%does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\begin{function}{\ClistRandItem,\ClistVarRandItem} +\begin{syntax} +\cs{ClistRandItem} \Arg{comma list} +\cs{ClistVarRandItem} \meta{clist var} +\end{syntax} +Selects a pseudo-random item of the \meta{comma list}. +If the \meta{comma list} has no item, the result is empty. +%\begin{texnote} +%The result is returned within the \tn{unexpanded} +%primitive (\cs{exp_not:n}), which means that the \meta{item} +%does not expand further when appearing in an \texttt{x}-type +%or \texttt{e}-type argument expansion. +%\end{texnote} +\end{function} + +\section{Comma Lists as Stacks} + +Comma lists can be used as stacks, where data is pushed to and popped +from the top of the comma list. (The left of a comma list is the top, for +performance reasons.) The stack functions for comma lists are not +intended to be mixed with the general ordered data functions detailed +in the previous section: a comma list should either be used as an +ordered data type or as a stack, but not in both ways. + +\begin{function}{\ClistGet,\ClistGetTF} +\begin{syntax} +\cs{ClistGet} \meta{comma list} \meta{token list variable} +\cs{ClistGetT} \meta{comma list} \meta{token list variable} \meta{true code} +\cs{ClistGetF} \meta{comma list} \meta{token list variable} \meta{false code} +\cs{ClistGetTF} \meta{comma list} \meta{token list variable} \meta{true code} \meta{false code} +\end{syntax} +Stores the left-most item from a \meta{comma list} in the +\meta{token list variable} without removing it from the +\meta{comma list}. The \meta{token list variable} is assigned locally. +%In the non-branching version, if the \meta{comma list} is empty the +%\meta{token list variable} is set to the marker value \cs{qNoValue}. +\end{function} + +\begin{function}{\ClistPop,\ClistPopT,\ClistPopF,\ClistPopTF} +\begin{syntax} +\cs{ClistPop} \meta{comma list} \meta{token list variable} +\cs{ClistPopT} \meta{comma list} \meta{token list variable} \Arg{true code} +\cs{ClistPopF} \meta{comma list} \meta{token list variable} \Arg{false code} +\cs{ClistPopTF} \meta{comma list} \meta{token list variable} \Arg{true code} \Arg{false code} +\end{syntax} +Pops the left-most item from a \meta{comma list} into the +\meta{token list variable}, \emph{i.e.} removes the item from the +comma list and stores it in the \meta{token list variable}. +The assignment of the \meta{token list variable} is local. +If the \meta{comma list} is empty, +the value of the \meta{token list variable} is +not defined in this case and should not be relied upon. +\end{function} + +\begin{function}{\ClistPush} +\begin{syntax} +\cs{ClistPush} \meta{comma list} \Arg{items} +\end{syntax} +Adds the \Arg{items} to the top of the \meta{comma list}. +Spaces are removed from both sides of each item as for any +\texttt{n}-type comma list. +\end{function} + +\section{Mapping over Comma Lists} + +%The functions described in this section apply a specified function +%to each item of a comma list. +%All mappings are done at the current group level, \emph{i.e.} any +%local assignments made by the \meta{function} or \meta{code} discussed +%below remain in effect after the loop. + +When the comma list is given explicitly, %as an \texttt{n}-type argument, +spaces are trimmed around each item. +If the result of trimming spaces is empty, the item is ignored. +Otherwise, if the item is surrounded by braces, one set is removed, +and the result is passed to the mapped function. Thus, if the +comma list that is being mapped is \verb*|{a , {{b} }, ,{}, {c},}| +then the arguments passed to the mapped function are +`\verb*|a|', `\verb*|{b} |', an empty argument, and `\verb*|c|'. + +When the comma list is given as a variable, spaces +have already been trimmed on input, and items are simply stripped +of one set of braces if any. This case is more efficient than using +explicit comma lists. + +%\begin{function}{\ClistMapFunction,\ClistVarMapFunction} +%\begin{syntax} +%\cs{ClistMapFunction} \Arg{comma list} \meta{function} +%\cs{ClistVarMapFunction} \meta{comma list} \meta{function} +%\end{syntax} +%Applies \meta{function} to every \meta{item} stored in the +%\meta{comma list}. The \meta{function} receives one argument for +%each iteration. The \meta{items} are returned from left to right. +%The function \cs{ClistMapInline} is in general more efficient +%than \cs{ClistMapFunction}. +%\end{function} + +\begin{function}{\ClistMapInline,\ClistVarMapInline} +\begin{syntax} +\cs{ClistMapInline} \Arg{comma list} \Arg{inline function} +\cs{ClistVarMapInline} \meta{comma list} \Arg{inline function} +\end{syntax} +Applies \meta{inline function} to every \meta{item} stored +within the \meta{comma list}. The \meta{inline function} should +consist of code which receives the \meta{item} as \verb|#1|. +The \meta{items} are returned from left to right. +For example +\begin{codehigh} +\IgnoreSpacesOn +\TlClear \lTmpaTl +\ClistMapInline {one,two,three} { + \TlPutRight \lTmpaTl {(#1)} +} +\Result {\TlUse\lTmpaTl} +\IgnoreSpacesOff +\end{codehigh} +produces (one)(two)(three). +\end{function} + +\begin{function}{\ClistMapVariable,\ClistVarMapVariable} +\begin{syntax} +\cs{ClistMapVariable} \Arg{comma list} \meta{variable} \Arg{code} +\cs{ClistVarMapVariable} \meta{comma list} \meta{variable} \Arg{code} +\end{syntax} +Stores each \meta{item} of the \meta{comma list} in turn in the +(token list) \meta{variable} and applies the \meta{code}. The +\meta{code} will usually make use of the \meta{variable}, but this +is not enforced. The assignments to the \meta{variable} are local. +Its value after the loop is the last \meta{item} in the \meta{comma +list}, or its original value if there were no \meta{item}. The +\meta{items} are returned from left to right. +\end{function} + +%\begin{function}{\ClistMapTokens,\ClistVarMapTokens} +%\begin{syntax} +%\cs{ClistMapTokens} \Arg{comma list} \Arg{code} +%\cs{ClistVarMapTokens} \meta{clist var} \Arg{code} +%\end{syntax} +%Calls \meta{code} \Arg{item} for every \meta{item} stored in the +%\meta{comma list}. The \meta{code} receives each \meta{item} as a +%trailing brace group. If the \meta{code} consists of a single +%function this is equivalent to \cs{ClistMapFunction} or \cs{ClistVarMapFunction}. +%\end{function} + +%\begin{function}{\ClistMapBreak} +%\begin{syntax} +%\cs{ClistMapBreak} +%\end{syntax} +%Used to terminate a clist map function before all +%entries in the \meta{comma list} have been processed. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\clist_map_inline:Nn \l_my_clist +%{ +%\str_if_eq:nnTF { #1 } { bingo } +%{ \clist_map_break: } +%{ +%Do something useful +%} +%} +%\end{verbatim} +%Use outside of a clist map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before further items are taken +%from the input stream. This depends on the design of the mapping +%function. +%\end{texnote} +%\end{function} +% +%\begin{function}{\ClistMapBreakDo} +%\begin{syntax} +%\cs{ClistMapBreakDo} \Arg{code} +%\end{syntax} +%Used to terminate a clist map function before all +%entries in the \meta{comma list} have been processed, inserting +%the \meta{code} after the mapping has ended. This +%normally takes place within a conditional statement, for example +%\begin{verbatim} +%\clist_map_inline:Nn \l_my_clist +%{ +%\str_if_eq:nnTF { #1 } { bingo } +%{ \clist_map_break:n { <code> } } +%{ +%Do something useful +%} +%} +%\end{verbatim} +%Use outside of a clist map scenario leads to low +%level \TeX{} errors. +%\begin{texnote} +%When the mapping is broken, additional tokens may be inserted +%before the \meta{code} is +%inserted into the input stream. +%This depends on the design of the mapping function. +%\end{texnote} +%\end{function} + +\section{Comma List Conditionals} + +\begin{function}{\ClistIfExist,\ClistIfExistTF} +\begin{syntax} +\cs{ClistIfExist} \meta{comma list} +\cs{ClistIfExistTF} \meta{comma list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests whether the \meta{comma list} is currently defined. This does +not check that the \meta{comma list} really is a comma list. +\end{function} + +\begin{function}{\ClistIfEmpty,\ClistIfEmptyTF} +\begin{syntax} +\cs{ClistIfEmpty} \Arg{comma list} +\cs{ClistIfEmptyTF} \Arg{comma list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{comma list} is empty (containing no items). +The rules for space trimming are as for other \texttt{n}-type +comma-list functions, hence the comma list \verb|{ , ,, }| (without +outer braces) is empty, while \verb|{ ,{},}| (without outer braces) +contains one element, which happens to be empty: the comma-list +is not empty. +\end{function} + +\begin{function}{\ClistVarIfEmpty,\ClistVarIfEmptyTF} +\begin{syntax} +\cs{ClistVarIfEmpty} \meta{comma list} +\cs{ClistVarIfEmptyTF} \meta{comma list} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{comma list} is empty (containing no items). +\end{function} + +\begin{function}{\ClistIfIn,\ClistIfInTF} +\begin{syntax} +\cs{ClistIfIn} \Arg{comma list} \Arg{item} +\cs{ClistIfInTF} \Arg{comma list} \Arg{item} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{item} is present in the \meta{comma list}. +In the case of an \texttt{n}-type \meta{comma list}, the usual rules +of space trimming and brace stripping apply. For example +\begin{demohigh} +\ClistIfInTF { a , {b} , {b} , c } {b} {Yes} {No} +\end{demohigh} +%\begin{texnote} +%The function may fail if the \meta{item} contains \verb|{|, \verb|}|, or \verb|#| +%(assuming the usual \TeX{} category codes apply). +%\end{texnote} +\end{function} + +\begin{function}{\ClistVarIfIn,\ClistVarIfInTF} +\begin{syntax} +\cs{ClistVarIfIn} \meta{comma list} \Arg{item} +\cs{ClistVarIfInTF} \meta{comma list} \Arg{item} \Arg{true code} \Arg{false code} +\end{syntax} +Tests if the \meta{item} is present in the \meta{comma list}. +In the case of an \texttt{n}-type \meta{comma list}, the usual rules +of space trimming and brace stripping apply. +%\begin{texnote} +%The function may fail if the \meta{item} contains \verb|{|, \verb|}|, or \verb|#| +%(assuming the usual \TeX{} category codes apply). +%\end{texnote} +\end{function} + \chapter{The Source Code} %\CodeHigh{lite} @@ -991,4 +4363,3 @@ For example \dochighinput[language=latex/latex3]{functional.sty} \end{document} - diff --git a/Master/texmf-dist/tex/latex/functional/functional.sty b/Master/texmf-dist/tex/latex/functional/functional.sty index 95e5ada3dc2..dcc863b3c5d 100644 --- a/Master/texmf-dist/tex/latex/functional/functional.sty +++ b/Master/texmf-dist/tex/latex/functional/functional.sty @@ -1,22 +1,24 @@ %%% -*- coding: utf-8 -*- %% ---------------------------------------------------------------------------- -%% Functional: LaTeX2 functional interfaces to LaTeX3 programming layer +%% Functional: LaTeX2 functional interfaces for LaTeX3 programming layer %% Copyright : 2022 (c) Jianrui Lyu <tolvjr@163.com> %% Repository: https://github.com/lvjr/functional +%% Repository: https://bitbucket.org/lvjr/functional %% License : The LaTeX Project Public License 1.3c %% ---------------------------------------------------------------------------- %%% -------------------------------------------------------- -%%> \section{Interfaces for Function Definitions (l3basics)} +%%> \section{Interfaces for Functional Programming (Prg)} %%% -------------------------------------------------------- \NeedsTeXFormat{LaTeX2e}[2018-04-01] \RequirePackage{expl3} -\ProvidesExplPackage{functional}{2022-03-10}{2022A} - {^^JLaTeX2 functional interfaces to LaTeX3 programming layer} +\ProvidesExplPackage{functional}{2022-03-19}{2022B} + {^^JLaTeX2 functional interfaces for LaTeX3 programming layer} \cs_generate_variant:Nn \iow_log:n { V } +\cs_generate_variant:Nn \tl_log:n { e } \cs_generate_variant:Nn \tl_set:Nn { Ne } \tl_new:N \gResultTl @@ -51,16 +53,6 @@ \tl_const:Nn \c__fun_parameter_called_vii_tl { {#7} } \tl_const:Nn \c__fun_parameter_called_viii_tl { {#8} } \tl_const:Nn \c__fun_parameter_called_ix_tl { {#9} } -\tl_new:N \l__fun_argument_tl -\tl_new:N \l__fun_argument_i_tl -\tl_new:N \l__fun_argument_ii_tl -\tl_new:N \l__fun_argument_iii_tl -\tl_new:N \l__fun_argument_iv_tl -\tl_new:N \l__fun_argument_v_tl -\tl_new:N \l__fun_argument_vi_tl -\tl_new:N \l__fun_argument_vii_tl -\tl_new:N \l__fun_argument_viii_tl -\tl_new:N \l__fun_argument_ix_tl %% #1: function name; #2: argument specification; #3 function body \cs_new_protected:Npn \__fun_new_function:Nnn #1 #2 #3 @@ -113,13 +105,18 @@ \cs_set_eq:NN \PrgNewConditional \__fun_new_conditional:Nnn +\int_new:N \g__fun_nesting_level_int + %% #1: function name; #2: argument specifications; #3 parameters tl defined %% Some times we need to create a function without arguments \cs_new_protected:Npn \__fun_new_with_arg_:Nnn #1 #2 #3 { \cs_new_protected:Npn #1 #3 { + \int_gincr:N \g__fun_nesting_level_int \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_:Nnn { NnV } @@ -129,8 +126,11 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_i:Nnn { NnV } @@ -140,9 +140,12 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_ii:Nnn { NnV } @@ -152,10 +155,13 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_iii:Nnn { NnV } @@ -165,11 +171,14 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_iv:Nnn { NnV } @@ -179,12 +188,15 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } - \tl_set:Nn \l__fun_argument_v_tl { ##5 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } + \__fun_one_argument_gset:nn { 5 } { ##5 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_v:Nnn { NnV } @@ -194,13 +206,16 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } - \tl_set:Nn \l__fun_argument_v_tl { ##5 } - \tl_set:Nn \l__fun_argument_vi_tl { ##6 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } + \__fun_one_argument_gset:nn { 5 } { ##5 } + \__fun_one_argument_gset:nn { 6 } { ##6 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_vi:Nnn { NnV } @@ -210,14 +225,17 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } - \tl_set:Nn \l__fun_argument_v_tl { ##5 } - \tl_set:Nn \l__fun_argument_vi_tl { ##6 } - \tl_set:Nn \l__fun_argument_vii_tl { ##7 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } + \__fun_one_argument_gset:nn { 5 } { ##5 } + \__fun_one_argument_gset:nn { 6 } { ##6 } + \__fun_one_argument_gset:nn { 7 } { ##7 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_vii:Nnn { NnV } @@ -227,15 +245,18 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } - \tl_set:Nn \l__fun_argument_v_tl { ##5 } - \tl_set:Nn \l__fun_argument_vi_tl { ##6 } - \tl_set:Nn \l__fun_argument_vii_tl { ##7 } - \tl_set:Nn \l__fun_argument_viii_tl { ##8 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } + \__fun_one_argument_gset:nn { 5 } { ##5 } + \__fun_one_argument_gset:nn { 6 } { ##6 } + \__fun_one_argument_gset:nn { 7 } { ##7 } + \__fun_one_argument_gset:nn { 8 } { ##8 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_viii:Nnn { NnV } @@ -245,39 +266,39 @@ { \cs_new_protected:Npn #1 #3 { - \tl_set:Nn \l__fun_argument_i_tl { ##1 } - \tl_set:Nn \l__fun_argument_ii_tl { ##2 } - \tl_set:Nn \l__fun_argument_iii_tl { ##3 } - \tl_set:Nn \l__fun_argument_iv_tl { ##4 } - \tl_set:Nn \l__fun_argument_v_tl { ##5 } - \tl_set:Nn \l__fun_argument_vi_tl { ##6 } - \tl_set:Nn \l__fun_argument_vii_tl { ##7 } - \tl_set:Nn \l__fun_argument_viii_tl { ##8 } - \tl_set:Nn \l__fun_argument_ix_tl { ##9 } + \int_gincr:N \g__fun_nesting_level_int + \__fun_one_argument_gset:nn { 1 } { ##1 } + \__fun_one_argument_gset:nn { 2 } { ##2 } + \__fun_one_argument_gset:nn { 3 } { ##3 } + \__fun_one_argument_gset:nn { 4 } { ##4 } + \__fun_one_argument_gset:nn { 5 } { ##5 } + \__fun_one_argument_gset:nn { 6 } { ##6 } + \__fun_one_argument_gset:nn { 7 } { ##7 } + \__fun_one_argument_gset:nn { 8 } { ##8 } + \__fun_one_argument_gset:nn { 9 } { ##9 } \__fun_evaluate:Nn #1 {#2} + \int_gdecr:N \g__fun_nesting_level_int + \__fun_return_result: } } \cs_generate_variant:Nn \__fun_new_with_arg_ix:Nnn { NnV } -\int_new:N \g__fun_nesting_level_int -\int_new:N \l__fun_argtype_number_int \tl_new:N \l__fun_argtype_tl \tl_const:Nn \c__fun_argtype_m_tl { m } \tl_const:Nn \c__fun_argtype_M_tl { M } \tl_const:Nn \c__fun_argtype_n_tl { n } \tl_const:Nn \c__fun_argtype_N_tl { N } +\tl_new:N \l__fun_argument_tl %% #1: function name; #2: argument specifications \cs_new_protected:Npn \__fun_evaluate:Nn #1 #2 { - \int_zero:N \l__fun_argtype_number_int - \int_gincr:N \g__fun_nesting_level_int + \__fun_argtype_index_gzero: \__fun_arguments_gclear: \tl_map_variable:nNn { #2 } \l__fun_argtype_tl % spaces are ignored { - \int_incr:N \l__fun_argtype_number_int - \tl_set_eq:Nc \l__fun_argument_tl - { l__fun_argument_ \int_to_roman:n { \l__fun_argtype_number_int } _tl } + \__fun_argtype_index_gincr: + \__fun_one_argument_get:eN { \__fun_argtype_index_use: } \l__fun_argument_tl \tl_case:Nn \l__fun_argtype_tl { \c__fun_argtype_m_tl @@ -300,19 +321,14 @@ } \__fun_arguments_log:N #1 \__fun_arguments_called:c { __fun_defined_ \cs_to_str:N #1 : w } - \int_gdecr:N \g__fun_nesting_level_int - \__fun_return_result: } -\cs_set_eq:NN \__fun_cs_temp:w \scan_stop: - \cs_new_protected:Npn \__fun_evaluate_and_put_argument:N #1 { - \cs_set_eq:Nc \__fun_cs_temp:w + \cs_if_exist:cTF { __fun_defined_ \exp_last_unbraced:Ne \cs_to_str:N { \tl_head:N #1 } : w } - \cs_if_exist:NTF \__fun_cs_temp:w { #1 \__fun_arguments_gput:e { { \exp_not:V \gResultTl } } @@ -322,6 +338,57 @@ } } +%% #1: argument number; #2: token lists +\cs_new_protected:Npn \__fun_one_argument_gset:nn #1 #2 + { + \tl_gset:cn + { g__fun_one_argument_ \int_use:N \g__fun_nesting_level_int _#1_tl } { #2 } + %\__fun_one_argument_log:nn { #1 } { set } + } + +%% #1: argument number; #2: variable of token lists +\cs_new_protected:Npn \__fun_one_argument_get:nN #1 #2 + { + \tl_set_eq:Nc + #2 { g__fun_one_argument_ \int_use:N \g__fun_nesting_level_int _ #1 _tl } + %\__fun_one_argument_log:nn { #1 } { get } + } +\cs_generate_variant:Nn \__fun_one_argument_get:nN { eN } + +%% #1: argument number; #2: get or set +\cs_new_protected:Npn \__fun_one_argument_log:nn #1 #2 + { + \tl_log:e + { + #2 ~ level _ \int_use:N \g__fun_nesting_level_int _ arg _ #1 ~ = ~ + \exp_not:v + { g__fun_one_argument_ \int_use:N \g__fun_nesting_level_int _#1_tl } + } + } + +\int_new:c { g__fun_argtype_index_ 1 _int } +\int_new:c { g__fun_argtype_index_ 2 _int } +\int_new:c { g__fun_argtype_index_ 3 _int } +\int_new:c { g__fun_argtype_index_ 4 _int } +\int_new:c { g__fun_argtype_index_ 5 _int } + +\cs_new_protected:Npn \__fun_argtype_index_gzero: + { + \int_gzero_new:c + { g__fun_argtype_index_ \int_use:N \g__fun_nesting_level_int _int } + } + +\cs_new_protected:Npn \__fun_argtype_index_gincr: + { + \int_gincr:c + { g__fun_argtype_index_ \int_use:N \g__fun_nesting_level_int _int } + } + +\cs_new:Npn \__fun_argtype_index_use: + { + \int_use:c { g__fun_argtype_index_ \int_use:N \g__fun_nesting_level_int _int } + } + \cs_new_protected:Npn \__fun_arguments_called:N #1 { \exp_last_unbraced:Nv @@ -350,6 +417,12 @@ } \cs_generate_variant:Nn \__fun_arguments_gput:n { e } +\cs_set_eq:NN \Break \prg_break: +\cs_set_eq:NN \PrgBreak \prg_break: + +\cs_set_eq:NN \BreakDo \prg_break:n +\cs_set_eq:NN \PrgBreakDo \prg_break:n + \cs_new_protected:Npn \__fun_put_result:n #1 { \tl_gput_right:Nn \gResultTl { #1 } @@ -361,27 +434,6 @@ \__fun_put_result:n { #1 } } -\PrgNewFunction \Name { m } - { - \exp_args:Nc \__fun_put_result:n { #1 } - } -\cs_set_eq:NN \UseName \Name - -\PrgNewFunction \Expand { m } - { - \__fun_put_result:e { #1 } - } -\cs_set_eq:NN \UseExpand \Expand - -\PrgNewFunction \Value { M } - { - \__fun_put_result:V #1 - } -\cs_set_eq:NN \UseValue \Value - -\cs_set_eq:NN \ExpNot \exp_not:n -\cs_set_eq:NN \ExpValue \exp_not:V - \cs_new_protected:Npn \__fun_return_result: { \int_compare:nNnT { \g__fun_nesting_level_int } = { 0 } @@ -484,28 +536,50 @@ \Functional { scoping = false, tracing = false } -\PrgNewFunction \UseOne { n } +\cs_new_protected:Npn \__fun_ignore_spaces_on: { - \Result { #1 } + \ExplSyntaxOn + \char_set_catcode_math_subscript:N \_ + \char_set_catcode_other:N \: } +\cs_set_eq:NN \IgnoreSpacesOn \__fun_ignore_spaces_on: +\cs_set_eq:NN \IgnoreSpacesOff \ExplSyntaxOff -\PrgNewFunction \GobbleOne { n } +%%% -------------------------------------------------------- +%%> \section{Interfaces for Argument Using (Use)} +%%% -------------------------------------------------------- + +\PrgNewFunction \Name { m } { - \Result { } + \exp_args:Nc \__fun_put_result:n { #1 } } +\cs_set_eq:NN \UseName \Name -\PrgNewFunction \UseGobble { n n } +\PrgNewFunction \Value { M } { - \UseOne { #1 } + \__fun_put_result:V #1 } +\cs_set_eq:NN \UseValue \Value -\PrgNewFunction \GobbleUse { n n } +\PrgNewFunction \Expand { m } { - \UseOne { #2 } + \__fun_put_result:e { #1 } } +\cs_set_eq:NN \UseExpand \Expand + +\cs_set_eq:NN \ExpNot \exp_not:n +\cs_set_eq:NN \ExpValue \exp_not:V + +\PrgNewFunction \UseOne { n } { \Result { #1 } } + +\PrgNewFunction \GobbleOne { n } { \Result { } } + +\PrgNewFunction \UseGobble { n n } { \UseOne { #1 } } + +\PrgNewFunction \GobbleUse { n n } { \UseOne { #2 } } %%% -------------------------------------------------------- -%%> \section{Interfaces for Control Structures (l3prg)} +%%> \section{Interfaces for Control Structures (Bool)} %%% -------------------------------------------------------- \bool_const:Nn \cTrueBool { \c_true_bool } @@ -519,8 +593,14 @@ \bool_new:N \gTmpiBool \bool_new:N \gTmpjBool \bool_new:N \gTmpkBool \bool_new:N \g@Funx@Bool \bool_new:N \g@Funy@Bool \bool_new:N \g@Funz@Bool -\cs_set_eq:NN \BoolNew \bool_new:N -\cs_set_eq:NN \BoolLog \bool_log:N +\PrgNewFunction \BoolNew { M } { \bool_new:N #1 } + +\PrgNewFunction \BoolConst { M m } { \bool_const:Nn #1 { #2 } } + +\PrgNewFunction \BoolSet { M m } { + \__fun_do_assignment:Nnn #1 + { \bool_gset:Nn #1 { #2 } } { \bool_set:Nn #1 { #2 } } +} \PrgNewFunction \BoolSetTrue { M } { @@ -532,15 +612,59 @@ \__fun_do_assignment:Nnn #1 { \bool_gset_false:N #1 } { \bool_set_false:N #1 } } -\PrgNewConditional \BoolIf { N } +\PrgNewFunction \BoolSetEq { M M } { - \Result { #1 } + \__fun_do_assignment:Nnn #1 + { \bool_gset_eq:NN #1 #2 } { \bool_set_eq:NN #1 #2 } + } + +\PrgNewFunction \BoolLog { m } { \bool_log:n { #1 } } + +\PrgNewFunction \BoolVarLog { M } { \bool_log:N #1 } + +\PrgNewFunction \BoolShow { m } { \bool_show:n { #1 } } + +\PrgNewFunction \BoolVarShow { M } { \bool_show:N #1 } + +\PrgNewConditional \BoolIfExist { M } + { + \bool_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \BoolVarIf { M } { \Result { #1 } } + +\PrgNewConditional \BoolVarNot { M } + { + \bool_if:NTF #1 + { \Result { \cFalseBool } } { \Result { \cTrueBool } } + } + +\PrgNewConditional \BoolVarAnd { M M } + { + \bool_lazy_and:nnTF {#1} {#2} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \BoolVarOr { M M } + { + \bool_lazy_or:nnTF {#1} {#2} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \BoolVarXor { M M } + { + \bool_xor:nnTF {#1} {#2} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } } %%% -------------------------------------------------------- -%%> \section{Interfaces for Token Lists (l3tl)} +%%> \section{Interfaces for Token Lists (Tl)} %%% -------------------------------------------------------- +\tl_set_eq:NN \cEmptyTl \c_empty_tl +\tl_set_eq:NN \cSpaceTl \c_space_tl +\tl_set_eq:NN \cNoValueTl \c_novalue_tl + \tl_new:N \lTmpaTl \tl_new:N \lTmpbTl \tl_new:N \lTmpcTl \tl_new:N \lTmpiTl \tl_new:N \lTmpjTl \tl_new:N \lTmpkTl \tl_new:N \l@Funx@Tl \tl_new:N \l@Funy@Tl \tl_new:N \l@Funz@Tl @@ -549,17 +673,48 @@ \tl_new:N \gTmpiTl \tl_new:N \gTmpjTl \tl_new:N \gTmpkTl \tl_new:N \g@Funx@Tl \tl_new:N \g@Funy@Tl \tl_new:N \g@Funz@Tl -\cs_set_eq:NN \TlNew \tl_new:N -\cs_set_eq:NN \TlLog \tl_log:N +\PrgNewFunction \TlNew { M } { \tl_new:N #1 } + +\PrgNewFunction \TlLog { m } { \tl_log:n { #1 } } + +\PrgNewFunction \TlVarLog { M } { \tl_log:N #1 } + +\PrgNewFunction \TlShow { m } { \tl_show:n { #1 } } + +\PrgNewFunction \TlVarShow { M } { \tl_show:N #1 } + +\PrgNewFunction \TlUse { M } { \Result { \Value #1 } } + +\PrgNewFunction \TlToStr { m } { \Expand { \tl_to_str:n { #1 } } } + +\PrgNewFunction \TlVarToStr { M } { \Expand { \tl_to_str:N #1 } } + +\PrgNewFunction \TlConst { M m } { \tl_const:Nn #1 { #2 } } + +\PrgNewFunction \TlSet { M m } + { + \__fun_do_assignment:Nnn #1 { \tl_gset:Nn #1 {#2} } { \tl_set:Nn #1 {#2} } + } + +\PrgNewFunction \TlSetEq { M M } + { + \__fun_do_assignment:Nnn #1 { \tl_gset_eq:NN #1 #2 } { \tl_set_eq:NN #1 #2 } + } + +\PrgNewFunction \TlConcat { M M M } + { + \__fun_do_assignment:Nnn #1 + { \tl_gconcat:NNN #1 #2 #3 } { \tl_concat:NNN #1 #2 #3 } + } \PrgNewFunction \TlClear { M } { \__fun_do_assignment:Nnn #1 { \tl_gclear:N #1 } { \tl_clear:N #1 } } -\PrgNewFunction \TlSet { M m } +\PrgNewFunction \TlClearNew { M } { - \__fun_do_assignment:Nnn #1 { \tl_gset:Nn #1 {#2} } { \tl_set:Nn #1 {#2} } + \__fun_do_assignment:Nnn #1 { \tl_gclear_new:N #1 } { \tl_clear_new:N #1 } } \PrgNewFunction \TlPutLeft { M m } @@ -574,29 +729,339 @@ { \tl_gput_right:Nn #1 {#2} } { \tl_put_right:Nn #1 {#2} } } -\PrgNewFunction \TlUse { M } +\PrgNewFunction \TlReplaceOnce { M m m } { - \Result { \Value #1 } + \__fun_do_assignment:Nnn #1 + { \tl_greplace_once:Nnn #1 {#2} {#3} } { \tl_replace_once:Nnn #1 {#2} {#3} } } -\PrgNewConditional \TlIfEmpty { N } +\PrgNewFunction \TlReplaceAll { M m m } { - \tl_if_empty:NTF #1 - { \Result { \cTrueBool } } - { \Result { \cFalseBool } } + \__fun_do_assignment:Nnn #1 + { \tl_greplace_all:Nnn #1 {#2} {#3} } { \tl_replace_all:Nnn #1 {#2} {#3} } + } + +\PrgNewFunction \TlRemoveOnce { M m } + { + \__fun_do_assignment:Nnn #1 + { \tl_gremove_once:Nn #1 {#2} } { \tl_remove_once:Nn #1 {#2} } + } + +\PrgNewFunction \TlRemoveAll { M m } + { + \__fun_do_assignment:Nnn #1 + { \tl_gremove_all:Nn #1 {#2} } { \tl_remove_all:Nn #1 {#2} } + } + +\PrgNewFunction \TlTrimSpaces { m } { \Expand { \tl_trim_spaces:n { #1 } } } + +\PrgNewFunction \TlVarTrimSpaces { M } + { + \__fun_do_assignment:Nnn #1 { \tl_gtrim_spaces:N #1 } { \tl_trim_spaces:N #1 } + } + +\PrgNewFunction \TlCount { m } { \Expand { \tl_count:n { #1 } } } + +\PrgNewFunction \TlVarCount { M } { \Expand { \tl_count:N #1 } } + +\PrgNewFunction \TlHead { m } { \Expand { \tl_head:n { #1 } } } + +\PrgNewFunction \TlVarHead { M } { \Expand { \tl_head:N #1 } } + +\PrgNewFunction \TlTail { m } { \Expand { \tl_tail:n { #1 } } } + +\PrgNewFunction \TlVarTail { M } { \Expand { \tl_tail:N #1 } } + +\PrgNewFunction \TlItem { m m } { \Expand { \tl_item:nn {#1} {#2} } } + +\PrgNewFunction \TlVarItem { M m } { \Expand { \tl_item:Nn #1 {#2} } } + +\PrgNewFunction \TlRandItem { m } { \Expand { \tl_rand_item:n {#1} } } + +\PrgNewFunction \TlVarRandItem { M } { \Expand { \tl_rand_item:N #1 } } + +\PrgNewFunction \TlVarCase { M m } { \tl_case:Nn {#1} {#2} } +\PrgNewFunction \TlVarCaseT { M m n } { \tl_case:NnT {#1} {#2} {#3} } +\PrgNewFunction \TlVarCaseF { M m n } { \tl_case:NnF {#1} {#2} {#3} } +\PrgNewFunction \TlVarCaseTF { M m n n } { \tl_case:NnTF {#1} {#2} {#3} {#4} } + +\PrgNewFunction \TlMapInline { m n } + { + \tl_map_inline:nn {#1} {#2} + } + +\PrgNewFunction \TlVarMapInline { M n } + { + \tl_map_inline:Nn #1 {#2} + } + +\PrgNewFunction \TlMapVariable { m M n } + { + \tl_map_variable:nNn {#1} #2 {#3} + } + +\PrgNewFunction \TlVarMapVariable { M M n } + { + \tl_map_variable:NNn #1 #2 {#3} + } + +\PrgNewConditional \TlIfExist { M } + { + \tl_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlIfEmpty { m } + { + \tl_if_empty:nTF {#1} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlVarIfEmpty { M } + { + \tl_if_empty:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlIfBlank { m } + { + \tl_if_blank:nTF {#1} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlIfEq { m m } + { + \tl_if_eq:nnTF {#1} {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlVarIfEq { M M } + { + \tl_if_eq:NNTF #1 #2 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlIfIn { m m } + { + \tl_if_in:nnTF {#1} {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlVarIfIn { M m } + { + \tl_if_in:NnTF #1 {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlIfSingle { m } + { + \tl_if_single:nTF {#1} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \TlVarIfSingle { M } + { + \tl_if_single:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +%%% -------------------------------------------------------- +%%> \section{Interfaces for Strings (Str)} +%%% -------------------------------------------------------- + +\str_set_eq:NN \cAmpersandStr \c_ampersand_str +\str_set_eq:NN \cAttignStr \c_atsign_str +\str_set_eq:NN \cBackslashStr \c_backslash_str +\str_set_eq:NN \cLeftBraceStr \c_left_brace_str +\str_set_eq:NN \cRightBraceStr \c_right_brace_str +\str_set_eq:NN \cCircumflexStr \c_circumflex_str +\str_set_eq:NN \cColonStr \c_colon_str +\str_set_eq:NN \cDollarStr \c_dollar_str +\str_set_eq:NN \cHashStr \c_hash_str +\str_set_eq:NN \cPercentStr \c_percent_str +\str_set_eq:NN \cTildeStr \c_tilde_str +\str_set_eq:NN \cUnderscoreStr \c_underscore_str +\str_set_eq:NN \cZeroStr \c_zero_str + +\str_new:N \lTmpaStr \str_new:N \lTmpbStr \str_new:N \lTmpcStr +\str_new:N \lTmpiStr \str_new:N \lTmpjStr \str_new:N \lTmpkStr +\str_new:N \l@Funx@Str \str_new:N \l@Funy@Str \str_new:N \l@Funz@Str + +\str_new:N \gTmpaStr \str_new:N \gTmpbStr \str_new:N \gTmpcStr +\str_new:N \gTmpiStr \str_new:N \gTmpjStr \str_new:N \gTmpkStr +\str_new:N \g@Funx@Str \str_new:N \g@Funy@Str \str_new:N \g@Funz@Str + +\PrgNewFunction \StrNew { M } { \str_new:N #1 } + +\PrgNewFunction \StrLog { m } { \str_log:n { #1 } } + +\PrgNewFunction \StrVarLog { M } { \str_log:N #1 } + +\PrgNewFunction \StrShow { m } { \str_show:n { #1 } } + +\PrgNewFunction \StrVarShow { M } { \str_show:N #1 } + +\PrgNewFunction \StrUse { M } { \Result { \Value #1 } } + +\PrgNewFunction \StrConst { M m } { \str_const:Nn #1 {#2} } + +\PrgNewFunction \StrSet { M m } + { + \__fun_do_assignment:Nnn #1 { \str_gset:Nn #1 {#2} } { \str_set:Nn #1 {#2} } + } + +\PrgNewFunction \StrSetEq { M M } + { + \__fun_do_assignment:Nnn #1 { \str_gset_eq:NN #1 #2 } { \str_set_eq:NN #1 #2 } + } + +\PrgNewFunction \StrConcat { M M M } + { + \__fun_do_assignment:Nnn #1 + { \str_gconcat:NNN #1 #2 #3 } { \str_concat:NNN #1 #2 #3 } + } + +\PrgNewFunction \StrClear { M } + { + \__fun_do_assignment:Nnn #1 { \str_gclear:N #1 } { \str_clear:N #1 } + } + +\PrgNewFunction \StrClearNew { M } + { + \__fun_do_assignment:Nnn #1 { \str_gclear_new:N #1 } { \str_clear_new:N #1 } + } + +\PrgNewFunction \StrPutLeft { M m } + { + \__fun_do_assignment:Nnn #1 + { \str_gput_left:Nn #1 {#2} } { \str_put_left:Nn #1 {#2} } + } + +\PrgNewFunction \StrPutRight { M m } + { + \__fun_do_assignment:Nnn #1 + { \str_gput_right:Nn #1 {#2} } { \str_put_right:Nn #1 {#2} } + } + +\PrgNewFunction \StrReplaceOnce { M m m } + { + \__fun_do_assignment:Nnn #1 + { \str_greplace_once:Nnn #1 {#2} {#3} } { \str_replace_once:Nnn #1 {#2} {#3} } + } + +\PrgNewFunction \StrReplaceAll { M m m } + { + \__fun_do_assignment:Nnn #1 + { \str_greplace_all:Nnn #1 {#2} {#3} } { \str_replace_all:Nnn #1 {#2} {#3} } + } + +\PrgNewFunction \StrRemoveOnce { M m } + { + \__fun_do_assignment:Nnn #1 + { \str_gremove_once:Nn #1 {#2} } { \str_remove_once:Nn #1 {#2} } } -\PrgNewConditional \TlIfEq { N N } +\PrgNewFunction \StrRemoveAll { M m } { - \tl_if_eq:NNTF #1 #2 + \__fun_do_assignment:Nnn #1 + { \str_gremove_all:Nn #1 {#2} } { \str_remove_all:Nn #1 {#2} } + } + +%% Avoid naming confict with xstring package +\cs_if_exist:NF \StrCount + { \PrgNewFunction \StrCount { m } { \Expand { \str_count:n { #1 } } } } + +%% Provide another name for \StrCount function +\PrgNewFunction \StrSize { m } { \Expand { \str_count:n { #1 } } } + +\PrgNewFunction \StrVarCount { M } { \Expand { \str_count:N #1 } } + +\PrgNewFunction \StrHead { m } { \Expand { \str_head:n { #1 } } } + +\PrgNewFunction \StrVarHead { M } { \Expand { \str_head:N #1 } } + +\PrgNewFunction \StrTail { m } { \Expand { \str_tail:n { #1 } } } + +\PrgNewFunction \StrVarTail { M } { \Expand { \str_tail:N #1 } } + +\PrgNewFunction \StrItem { m m } { \Expand { \str_item:nn {#1} {#2} } } + +\PrgNewFunction \StrVarItem { M m } { \Expand { \str_item:Nn #1 {#2} } } + +\PrgNewFunction \StrCase { m m } { \str_case:nn {#1} {#2} } +\PrgNewFunction \StrCaseT { m m n } { \str_case:nnT {#1} {#2} {#3} } +\PrgNewFunction \StrCaseF { m m n } { \str_case:nnF {#1} {#2} {#3} } +\PrgNewFunction \StrCaseTF { m m n n } { \str_case:nnTF {#1} {#2} {#3} {#4} } + +\PrgNewFunction \StrMapInline { m n } + { + \str_map_inline:nn {#1} {#2} + } + +\PrgNewFunction \StrVarMapInline { M n } + { + \str_map_inline:Nn #1 {#2} + } + +\PrgNewFunction \StrMapVariable { m M n } + { + \str_map_variable:nNn {#1} #2 {#3} + } + +\PrgNewFunction \StrVarMapVariable { M M n } + { + \str_map_variable:NNn #1 #2 {#3} + } + +\PrgNewConditional \StrIfExist { M } + { + \str_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \StrVarIfEmpty { M } + { + \str_if_empty:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \StrIfEq { m m } + { + \str_if_eq:nnTF {#1} {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \StrVarIfEq { M M } + { + \str_if_eq:NNTF #1 #2 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \StrIfIn { m m } + { + \str_if_in:nnTF {#1} {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \StrVarIfIn { M m } + { + \str_if_in:NnTF #1 {#2} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +%% Avoid naming confict with xstring package +\cs_if_exist:NF \StrCompare + { + \PrgNewConditional \StrCompare { m N m } + { + \str_compare:nNnTF {#1} #2 {#3} + { \Result { \cTrueBool } } + { \Result { \cFalseBool } } + } + } + +%% Provide another name for \StrCompare function +\PrgNewConditional \StrIfCompare { m N m } + { + \str_compare:nNnTF {#1} #2 {#3} { \Result { \cTrueBool } } { \Result { \cFalseBool } } } %%% -------------------------------------------------------- -%%> \section{Interfaces for Integers (l3int)} +%%> \section{Interfaces for Integers (Int)} %%% -------------------------------------------------------- +\cs_set_eq:NN \cZeroInt \c_zero_int +\cs_set_eq:NN \cOneInt \c_one_int +\cs_set_eq:NN \cMaxInt \c_max_int +\cs_set_eq:NN \cMaxRegisterInt \c_max_register_int +\cs_set_eq:NN \cMaxCharInt \c_max_char_in + \int_new:N \lTmpaInt \int_new:N \lTmpbInt \int_new:N \lTmpcInt \int_new:N \lTmpiInt \int_new:N \lTmpjInt \int_new:N \lTmpkInt \int_new:N \l@Funx@Int \int_new:N \l@Funy@Int \int_new:N \l@Funz@Int @@ -605,14 +1070,85 @@ \int_new:N \gTmpiInt \int_new:N \gTmpjInt \int_new:N \gTmpkInt \int_new:N \g@Funx@Int \int_new:N \g@Funy@Int \int_new:N \g@Funz@Int -\cs_set_eq:NN \IntNew \int_new:N -\cs_set_eq:NN \IntLog \int_log:N +\PrgNewFunction \IntEval { m } + { + \Result { \Expand { \int_eval:n { #1 } } } + } + +\PrgNewFunction \IntMathAdd { m m } + { + \int_set:Nn \l@Funx@Int { \int_eval:n { (#1) + (#2) } } + \Result { \Value \l@Funx@Int } + } + +\PrgNewFunction \IntMathSub { m m } + { + \int_set:Nn \l@Funx@Int { \int_eval:n { (#1) - (#2) } } + \Result { \Value \l@Funx@Int } + } + +\PrgNewFunction \IntMathMult { m m } + { + \int_set:Nn \l@Funx@Int { \int_eval:n { (#1) * (#2) } } + \Result { \Value \l@Funx@Int } + } + +\PrgNewFunction \IntMathDiv { m m } + { + \Expand { \int_div_round:nn { #1 } { #2 } } + } + +\PrgNewFunction \IntMathDivTruncate { m m } + { + \Expand { \int_div_truncate:nn { #1 } { #2 } } + } + +\PrgNewFunction \IntMathSign { m } { \Expand { \int_sign:n { #1 } } } + +\PrgNewFunction \IntMathAbs { m } { \Expand { \int_abs:n { #1 } } } + +\PrgNewFunction \IntMathMax { m m } { \Expand { \int_max:nn { #1 } { #2 } } } + +\PrgNewFunction \IntMathMin { m m } { \Expand { \int_min:nn { #1 } { #2 } } } + +\PrgNewFunction \IntMathMod { m m } { \Expand { \int_mod:nn { #1 } { #2 } } } + +\PrgNewFunction \IntMathRand { m m } { \Expand { \int_rand:nn { #1 } { #2 } } } + +\PrgNewFunction \IntNew { M } { \int_new:N #1 } + +\PrgNewFunction \IntConst { M m } { \int_const:Nn #1 { #2 } } + +\PrgNewFunction \IntLog { m } { \int_log:n { #1 } } + +\PrgNewFunction \IntVarLog { M } { \int_log:N #1 } + +\PrgNewFunction \IntShow { m } { \int_show:n { #1 } } + +\PrgNewFunction \IntVarShow { M } { \int_show:N #1 } + +\PrgNewFunction \IntUse { M } { \Result { \Value #1 } } + +\PrgNewFunction \IntSet { M m } + { + \__fun_do_assignment:Nnn #1 { \int_gset:Nn #1 {#2} } { \int_set:Nn #1 {#2} } + } \PrgNewFunction \IntZero { M } { \__fun_do_assignment:Nnn #1 { \int_gzero:N #1 } { \int_zero:N #1 } } +\PrgNewFunction \IntZeroNew { M } + { + \__fun_do_assignment:Nnn #1 { \int_gzero_new:N #1 } { \int_zero_new:N #1 } + } + +\PrgNewFunction \IntSetEq { M M } + { + \__fun_do_assignment:Nnn #1 { \int_gset_eq:NN #1 #2 } { \int_set_eq:NN #1 #2 } + } + \PrgNewFunction \IntIncr { M } { \__fun_do_assignment:Nnn #1 { \int_gincr:N #1 } { \int_incr:N #1 } @@ -623,11 +1159,6 @@ \__fun_do_assignment:Nnn #1 { \int_gdecr:N #1 } { \int_decr:N #1 } } -\PrgNewFunction \IntSet { M m } - { - \__fun_do_assignment:Nnn #1 { \int_gset:Nn #1 {#2} } { \int_set:Nn #1 {#2} } - } - \PrgNewFunction \IntAdd { M m } { \__fun_do_assignment:Nnn #1 { \int_gadd:Nn #1 {#2} } { \int_add:Nn #1 {#2} } @@ -638,49 +1169,524 @@ \__fun_do_assignment:Nnn #1 { \int_gsub:Nn #1 {#2} } { \int_sub:Nn #1 {#2} } } -\PrgNewFunction \IntUse { M } +\PrgNewFunction \IntStepInline { m m m n } { - \Result { \Value #1 } + \int_step_inline:nnnn { #1 } { #2 } { #3 } { #4 } } -\PrgNewFunction \IntEval { m } +\PrgNewFunction \IntStepVariable { m m m M n } { - \Result { \Expand { \int_eval:n { #1 } } } + \int_step_variable:nnnNn { #1 } { #2 } { #3 } #4 { #5 } } -\PrgNewFunction \IntMathAdd { m m } +\PrgNewConditional \IntIfExist { M } { - \int_set:Nn \l@Funx@Int { \int_eval:n { #1 + #2 } } - \Result { \Value \l@Funx@Int } + \int_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } } -\PrgNewFunction \IntMathSub { m m } +\PrgNewConditional \IntIfOdd { m } { - \int_set:Nn \l@Funx@Int { \int_eval:n { #1 - #2 } } - \Result { \Value \l@Funx@Int } + \int_if_odd:nTF { #1 } { \Result { \cTrueBool } } { \Result { \cFalseBool } } } -\PrgNewFunction \IntMathMult { m m } +\PrgNewConditional \IntIfEven { m } { - \int_set:Nn \l@Funx@Int { \int_eval:n { #1 * #2 } } - \Result { \Value \l@Funx@Int } + \int_if_even:nTF { #1 } { \Result { \cTrueBool } } { \Result { \cFalseBool } } } -\PrgNewFunction \IntMathDiv { m m } +\PrgNewConditional \IntCompare { m N m } { - \int_set:Nn \l@Funx@Int { \int_eval:n { #1 / #2 } } - \Result { \Value \l@Funx@Int } + \int_compare:nNnTF {#1} #2 {#3} + { \Result { \cTrueBool } } + { \Result { \cFalseBool } } } -\PrgNewFunction \IntStepVariable { m m m M n } +\PrgNewFunction \IntCase { m m } { \int_case:nn {#1} {#2} } +\PrgNewFunction \IntCaseT { m m n } { \int_case:nnT {#1} {#2} {#3} } +\PrgNewFunction \IntCaseF { m m n } { \int_case:nnF {#1} {#2} {#3} } +\PrgNewFunction \IntCaseTF { m m n n } { \int_case:nnTF {#1} {#2} {#3} {#4} } + +%%% -------------------------------------------------------- +%%> \section{Interfaces for Floating Point Numbers (Fp)} +%%% -------------------------------------------------------- + +\fp_set_eq:NN \cZeroFp \c_zero_fp +\fp_set_eq:NN \cMinusZeroFp \c_minus_zero_fp +\fp_set_eq:NN \cOneFp \c_one_fp +\fp_set_eq:NN \cInfFp \c_inf_fp +\fp_set_eq:NN \cMinusInfFp \c_minus_inf_fp +\fp_set_eq:NN \cEFp \c_e_fp +\fp_set_eq:NN \cPiFp \c_pi_fp +\fp_set_eq:NN \cOneDegreeFp \c_one_degree_fp + +\fp_new:N \lTmpaFp \fp_new:N \lTmpbFp \fp_new:N \lTmpcFp +\fp_new:N \lTmpiFp \fp_new:N \lTmpjFp \fp_new:N \lTmpkFp +\fp_new:N \l@Funx@Fp \fp_new:N \l@Funy@Fp \fp_new:N \l@Funz@Fp + +\fp_new:N \gTmpaFp \fp_new:N \gTmpbFp \fp_new:N \gTmpcFp +\fp_new:N \gTmpiFp \fp_new:N \gTmpjFp \fp_new:N \gTmpkFp +\fp_new:N \g@Funx@Fp \fp_new:N \g@Funy@Fp \fp_new:N \g@Funz@Fp + +\PrgNewFunction \FpEval { m } { - \int_step_variable:nnnNn { #1 } { #2 } { #3 } #4 { #5 } + \Result { \Expand { \fp_eval:n { #1 } } } } -\PrgNewConditional \IntCompare { m N m } +\PrgNewFunction \FpMathAdd { m m } { - \int_compare:nNnTF {#1} #2 {#3} + \fp_set:Nn \l@Funx@Fp { \fp_eval:n { (#1) + (#2) } } + \Result { \FpUse \l@Funx@Fp } + } + +\PrgNewFunction \FpMathSub { m m } + { + \fp_set:Nn \l@Funx@Fp { \fp_eval:n { (#1) - (#2) } } + \Result { \FpUse \l@Funx@Fp } + } + +\PrgNewFunction \FpMathMult { m m } + { + \fp_set:Nn \l@Funx@Fp { \fp_eval:n { (#1) * (#2) } } + \Result { \FpUse \l@Funx@Fp } + } + +\PrgNewFunction \FpMathDiv { m m } + { + \fp_set:Nn \l@Funx@Fp { \fp_eval:n { (#1) / (#2) } } + \Result { \FpUse \l@Funx@Fp } + } + +\PrgNewFunction \FpMathSign { m } + { + \Result { \Expand { \fp_sign:n { #1 } } } + } + +\PrgNewFunction \FpMathAbs { m } + { + \Result { \Expand { \fp_abs:n { #1 } } } + } + +\PrgNewFunction \FpMathMax { m m } + { + \Result { \Expand { \fp_max:nn { #1 } { #2 } } } + } + +\PrgNewFunction \FpMathMin { m m } + { + \Result { \Expand { \fp_min:nn { #1 } { #2 } } } + } + +\PrgNewFunction \FpNew { M } { \fp_new:N #1 } + +\PrgNewFunction \FpConst { M m } { \fp_const:Nn #1 {#2} } + +\PrgNewFunction \FpUse { M } { \Result { \Expand { \fp_use:N #1 } } } + +\PrgNewFunction \FpLog { m } { \fp_log:n { #1 } } + +\PrgNewFunction \FpVarLog { M } { \fp_log:N #1 } + +\PrgNewFunction \FpShow { m } { \fp_show:n { #1 } } + +\PrgNewFunction \FpVarShow { M } { \fp_show:N #1 } + +\PrgNewFunction \FpSet { M m } + { + \__fun_do_assignment:Nnn #1 { \fp_gset:Nn #1 {#2} } { \fp_set:Nn #1 {#2} } + } + +\PrgNewFunction \FpSetEq { M M } + { + \__fun_do_assignment:Nnn #1 { \fp_gset_eq:NN #1 #2 } { \fp_set_eq:NN #1 #2 } + } + +\PrgNewFunction \FpZero { M } + { + \__fun_do_assignment:Nnn #1 { \fp_gzero:N #1 } { \fp_zero:N #1 } + } + +\PrgNewFunction \FpZeroNew { M } + { + \__fun_do_assignment:Nnn #1 { \fp_gzero_new:N #1 } { \fp_zero_new:N #1 } + } + +\PrgNewFunction \FpAdd { M m } + { + \__fun_do_assignment:Nnn #1 { \fp_gadd:Nn #1 {#2} } { \fp_add:Nn #1 {#2} } + } + +\PrgNewFunction \FpSub { M m } + { + \__fun_do_assignment:Nnn #1 { \fp_gsub:Nn #1 {#2} } { \fp_sub:Nn #1 {#2} } + } + +\PrgNewFunction \FpStepInline { m m m n } + { + \fp_step_inline:nnnn { #1 } { #2 } { #3 } { #4 } + } + +\PrgNewFunction \FpStepVariable { m m m M n } + { + \fp_step_variable:nnnNn { #1 } { #2 } { #3 } #4 { #5 } + } + +\PrgNewConditional \FpIfExist { M } + { + \fp_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \FpCompare { m N m } + { + \fp_compare:nNnTF {#1} #2 {#3} { \Result { \cTrueBool } } { \Result { \cFalseBool } } } +%%% -------------------------------------------------------- +%%> \section{Interfaces for Dimensions (Dim)} +%%% -------------------------------------------------------- + +\cs_set_eq:NN \cMaxDim \c_max_dim +\cs_set_eq:NN \cZeroDim \c_zero_dim + +\dim_new:N \lTmpaDim \dim_new:N \lTmpbDim \dim_new:N \lTmpcDim +\dim_new:N \lTmpiDim \dim_new:N \lTmpjDim \dim_new:N \lTmpkDim +\dim_new:N \l@Funx@Dim \dim_new:N \l@Funy@Dim \dim_new:N \l@Funz@Dim + +\dim_new:N \gTmpaDim \dim_new:N \gTmpbDim \dim_new:N \gTmpcDim +\dim_new:N \gTmpiDim \dim_new:N \gTmpjDim \dim_new:N \gTmpkDim +\dim_new:N \g@Funx@Dim \dim_new:N \g@Funy@Dim \dim_new:N \g@Funz@Dim + +\PrgNewFunction \DimEval { m } + { + \Result { \Expand { \dim_eval:n { #1 } } } + } + +\PrgNewFunction \DimMathAdd { m m } + { + \dim_set:Nn \l@Funx@Dim { \dim_eval:n { (#1) + (#2) } } + \Result { \Value \l@Funx@Dim } + } + +\PrgNewFunction \DimMathSub { m m } + { + \dim_set:Nn \l@Funx@Dim { \dim_eval:n { (#1) - (#2) } } + \Result { \Value \l@Funx@Dim } + } + +\PrgNewFunction \DimMathSign { m } + { + \Result { \Expand { \dim_sign:n { #1 } } } + } + +\PrgNewFunction \DimMathAbs { m } + { + \Result { \Expand { \dim_abs:n { #1 } } } + } + +\PrgNewFunction \DimMathMax { m m } + { + \Result { \Expand { \dim_max:nn { #1 } { #2 } } } + } + +\PrgNewFunction \DimMathMin { m m } + { + \Result { \Expand { \dim_min:nn { #1 } { #2 } } } + } + +\PrgNewFunction \DimMathRatio { m m } + { + \Result { \Expand { \dim_ratio:nn { #1 } { #2 } } } + } + +\PrgNewFunction \DimNew { M } { \dim_new:N #1 } + +\PrgNewFunction \DimConst { M m } { \dim_const:Nn #1 {#2} } + +\PrgNewFunction \DimUse { M } { \Result { \Value #1 } } + +\PrgNewFunction \DimLog { m } { \dim_log:n { #1 } } + +\PrgNewFunction \DimVarLog { M } { \dim_log:N #1 } + +\PrgNewFunction \DimShow { m } { \dim_show:n { #1 } } + +\PrgNewFunction \DimVarShow { M } { \dim_show:N #1 } + +\PrgNewFunction \DimSet { M m } + { + \__fun_do_assignment:Nnn #1 { \dim_gset:Nn #1 {#2} } { \dim_set:Nn #1 {#2} } + } + +\PrgNewFunction \DimSetEq { M M } + { + \__fun_do_assignment:Nnn #1 { \dim_gset_eq:NN #1 #2 } { \dim_set_eq:NN #1 #2 } + } + +\PrgNewFunction \DimZero { M } + { + \__fun_do_assignment:Nnn #1 { \dim_gzero:N #1 } { \dim_zero:N #1 } + } + +\PrgNewFunction \DimZeroNew { M } + { + \__fun_do_assignment:Nnn #1 { \dim_gzero_new:N #1 } { \dim_zero_new:N #1 } + } + +\PrgNewFunction \DimAdd { M m } + { + \__fun_do_assignment:Nnn #1 { \dim_gadd:Nn #1 {#2} } { \dim_add:Nn #1 {#2} } + } + +\PrgNewFunction \DimSub { M m } + { + \__fun_do_assignment:Nnn #1 { \dim_gsub:Nn #1 {#2} } { \dim_sub:Nn #1 {#2} } + } + +\PrgNewFunction \DimStepInline { m m m n } + { + \dim_step_inline:nnnn { #1 } { #2 } { #3 } { #4 } + } + +\PrgNewFunction \DimStepVariable { m m m M n } + { + \dim_step_variable:nnnNn { #1 } { #2 } { #3 } #4 { #5 } + } + +\PrgNewConditional \DimIfExist { M } + { + \dim_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \DimCompare { m N m } + { + \dim_compare:nNnTF {#1} #2 {#3} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewFunction \DimCase { m m } { \dim_case:nn {#1} {#2} } +\PrgNewFunction \DimCaseT { m m n } { \dim_case:nnT {#1} {#2} {#3} } +\PrgNewFunction \DimCaseF { m m n } { \dim_case:nnF {#1} {#2} {#3} } +\PrgNewFunction \DimCaseTF { m m n n } { \dim_case:nnTF {#1} {#2} {#3} {#4} } + +%%% -------------------------------------------------------- +%%> \section{Interfaces for Sorting Functions (Sort)} +%%% -------------------------------------------------------- + +\cs_set_eq:NN \SortReturnSame \sort_return_same: +\cs_set_eq:NN \SortReturnSwapped \sort_return_swapped: + +%%% -------------------------------------------------------- +%%> \section{Interfaces for Comma Separated Lists (Clist)} +%%% -------------------------------------------------------- + +\clist_new:N \lTmpaClist \clist_new:N \lTmpbClist \clist_new:N \lTmpcClist +\clist_new:N \lTmpiClist \clist_new:N \lTmpjClist \clist_new:N \lTmpkClist +\clist_new:N \l@Funx@Clist \clist_new:N \l@Funy@Clist \clist_new:N \l@Funz@Clist + +\clist_new:N \gTmpaClist \clist_new:N \gTmpbClist \clist_new:N \gTmpcClist +\clist_new:N \gTmpiClist \clist_new:N \gTmpjClist \clist_new:N \gTmpkClist +\clist_new:N \g@Funx@Clist \clist_new:N \g@Funy@Clist \clist_new:N \g@Funz@Clist + +\clist_set_eq:NN \cEmptyClist \c_empty_clist + +\PrgNewFunction \ClistNew { M } { \clist_new:N #1 } + +\PrgNewFunction \ClistLog { m } { \clist_log:n { #1 } } + +\PrgNewFunction \ClistVarLog { M } { \clist_log:N #1 } + +\PrgNewFunction \ClistShow { m } { \clist_show:n { #1 } } + +\PrgNewFunction \ClistVarShow { M } { \clist_show:N #1 } + +\PrgNewFunction \ClistVarJoin { M m } + { + \Expand { \clist_use:Nn #1 { #2 } } + } + +\PrgNewFunction \ClistVarJoinExtended { M m m m } + { + \Expand { \clist_use:Nnnn #1 { #2 } { #3 } { #4 } } + } + +\PrgNewFunction \ClistJoin { m m } + { + \Expand { \clist_use:nn { #1 } { #2 } } + } + +\PrgNewFunction \ClistJoinExtended { m m m m } + { + \Expand { \clist_use:nnnn { #1 } { #2 } { #3 } { #4 } } + } + +\PrgNewFunction \ClistConst { M m } { \clist_const:Nn #1 { #2 } } + +\PrgNewFunction \ClistSet { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gset:Nn #1 {#2} } { \clist_set:Nn #1 {#2} } + } + +\PrgNewFunction \ClistSetEq { M M } + { + \__fun_do_assignment:Nnn #1 + { \clist_gset_eq:NN #1 #2 } { \clist_set_eq:NN #1 #2 } + } + +\PrgNewFunction \ClistSetFromSeq { M M } + { + \__fun_do_assignment:Nnn #1 + { \clist_gset_from_seq:NN #1 #2 } { \clist_set_from_seq:NN #1 #2 } + } + +\PrgNewFunction \ClistConcat { M M M } + { + \__fun_do_assignment:Nnn #1 + { \clist_gconcat:NNN #1 #2 #3 } { \clist_concat:NNN #1 #2 #3 } + } + +\PrgNewFunction \ClistClear { M } + { + \__fun_do_assignment:Nnn #1 { \clist_gclear:N #1 } { \clist_clear:N #1 } + } + +\PrgNewFunction \ClistClearNew { M } + { + \__fun_do_assignment:Nnn #1 { \clist_gclear_new:N #1 } { \clist_clear_new:N #1 } + } + +\PrgNewFunction \ClistPutLeft { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gput_left:Nn #1 {#2} } { \clist_put_left:Nn #1 {#2} } + } + +\PrgNewFunction \ClistPutRight { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gput_right:Nn #1 {#2} } { \clist_put_right:Nn #1 {#2} } + } + +\PrgNewFunction \ClistRemoveDuplicates { M } + { + \__fun_do_assignment:Nnn #1 + { \clist_gremove_duplicates:N #1 } { \clist_remove_duplicates:N #1 } + } + +\PrgNewFunction \ClistRemoveAll { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gremove_all:Nn #1 {#2} } { \clist_remove_all:Nn #1 {#2} } + } + +\PrgNewFunction \ClistReverse { M } + { + \__fun_do_assignment:Nnn #1 { \clist_greverse:N #1 } { \clist_reverse:N #1 } + } + +\PrgNewFunction \ClistSort { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gsort:Nn #1 {#2} } { \clist_sort:Nn #1 {#2} } + } + +\PrgNewFunction \ClistCount { m } + { + \Result { \Expand { \clist_count:n { #1 } } } + } + +\PrgNewFunction \ClistVarCount { m } + { + \Result { \Expand { \clist_count:N #1 } } + } + +\PrgNewFunction \ClistGet { M M } { \clist_get:NN #1 #2 } +\PrgNewFunction \ClistGetT { M M n } { \clist_get:NNT #1 #2 {#3} } +\PrgNewFunction \ClistGetF { M M n } { \clist_get:NNF #1 #2 {#3} } +\PrgNewFunction \ClistGetTF { M M n n } { \clist_get:NNTF #1 #2 {#3} {#4} } + +\PrgNewFunction \ClistPop { M M } + { + \__fun_do_assignment:Nnn #1 + { \clist_gpop:NN #1 #2 } { \clist_pop:NN #1 #2 } + } +\PrgNewFunction \ClistPopT { M M n } + { + \__fun_do_assignment:Nnn #1 + { \clist_gpop:NNT #1 #2 {#3} } { \clist_pop:NNT #1 #2 {#3} } + } +\PrgNewFunction \ClistPopF { M M n } + { + \__fun_do_assignment:Nnn #1 + { \clist_gpop:NNF #1 #2 {#3} } { \clist_pop:NNF #1 #2 {#3} } + } +\PrgNewFunction \ClistPopTF { M M n n } + { + \__fun_do_assignment:Nnn #1 + { \clist_gpop:NNTF #1 #2 {#3} {#4} } { \clist_pop:NNTF #1 #2 {#3} {#4} } + } + +\PrgNewFunction \ClistPush { M m } + { + \__fun_do_assignment:Nnn #1 + { \clist_gpush:Nn #1 {#2} } { \clist_push:Nn #1 {#2} } + } + +\PrgNewFunction \ClistItem { m m } { \Expand { \clist_item:nn {#1} {#2} } } + +\PrgNewFunction \ClistVarItem { M m } { \Expand { \clist_item:Nn #1 {#2} } } + +\PrgNewFunction \ClistRandItem { m } { \Expand { \clist_rand_item:n {#1} } } + +\PrgNewFunction \ClistVarRandItem { M } { \Expand { \clist_rand_item:N #1 } } + +\PrgNewFunction \ClistMapInline { m n } + { + \clist_map_inline:nn {#1} {#2} + } + +\PrgNewFunction \ClistVarMapInline { M n } + { + \clist_map_inline:Nn #1 {#2} + } + +\PrgNewFunction \ClistMapVariable { m M n } + { + \clist_map_variable:nNn {#1} #2 {#3} + } + +\PrgNewFunction \ClistVarMapVariable { M M n } + { + \clist_map_variable:NNn #1 #2 {#3} + } + +\cs_set_eq:NN \ClistMapBreak \clist_map_break: + +\PrgNewConditional \ClistIfExist { M } + { + \clist_if_exist:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \ClistIfEmpty { m } + { + \clist_if_empty:nTF {#1} { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \ClistVarIfEmpty { m } + { + \clist_if_empty:NTF #1 { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \ClistIfIn { m m } + { + \clist_if_in:nnTF {#1} {#2} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + +\PrgNewConditional \ClistVarIfIn { M m } + { + \clist_if_in:NnTF #1 {#2} + { \Result { \cTrueBool } } { \Result { \cFalseBool } } + } + |