[doc] functional
[doc] functional
This package provides an intuitive functional programming interface for LaTeX2, which
is an alternative choice to expl3 or LuaTeX, if you want to do programming in LaTeX.
Although there are functions in LaTeX3 programming layer (expl3), the evaluation of
them is from outside to inside. With this package, the evaluation of functions is from
inside to outside, which is the same as other programming languages such as Lua. In this
way, it is rather easy to debug code too.
Note that many paragraphs in this manual are copied from the documentation of expl3.
Contents
1 Overview of Features 6
1.1 Evaluation from Inside to Outside . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Group Scoping of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Tracing Evaluation of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4 Definitions of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5 Variants of Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2
CONTENTS 3
6 Strings (Str) 32
6.1 Constant and Scratch Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6.2 Creating and Using Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.3 Viewing Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.4 Setting String Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.5 Modifying String Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.6 Working with the Content of Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
6.7 Mapping over Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.8 String Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6.9 String Case Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7 Integers (Int) 42
7.1 Constant and Scratch Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
7.2 The Syntax of Integer Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
7.3 Using Integer Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7.4 Creating and Using Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
7.5 Viewing Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
7.6 Setting Integer Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
7.7 Integer Step Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.8 Integer Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7.9 Integer Case Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
9 Dimensions (Dim) 56
9.1 Constant and Scratch Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9.2 Dimension Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9.3 Creating and Using Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
9.4 Viewing Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9.5 Setting Dimension Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9.6 Dimension Step Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9.7 Dimension Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
9.8 Dimension Case Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Overview of Features
Both examples calculate first the square of 5 and produce 25, then calculate the square of 25 and pro-
duce 625. In contrast to expl3, this functional package does evaluation of functions from inside to
outside, which means composition of functions works like other programming languages such as Lua or
JavaScript.
You can define new functions with \prgNewFunction command. To make composition of functions work
as expected, every function must not insert directly any token to the input stream. Instead, a function
must pass the result (if any) to functional package with \prgReturn command. And 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 \IgnoreSpacesOn and \IgnoreSpacesOff block. Within this block, ~ is used to input a space.
At the end of this section, we will compare our factorial example with a similar Lua example:
6
CHAPTER 1. OVERVIEW OF FEATURES 7
Same as expl3, the names of local variables must start with l, while names of global variables must start
with g. The difference is that functional package provides only one function for setting both local and
global variables of the same type, by checking leading letters of their names. So for integer variables, you
can write \intSet\lTmpaInt{1} and \intSet\gTmpbInt{2}.
The previous example will produce different result if we change variable from \lTmpaInt to \gTmpaInt.
As you can see, the values of global variables will never be reset after a group.
[I] \mathSquare{5}
[I] \intEval{5*5}
[I] \expWhole{\int_eval:n {5*5}}
[O] 25
[I] \prgReturn{25}
[O] 25
[O] 25
[I] \intSet{\lTmpaInt }{25}
[O]
[I] \expValue{\lTmpaInt }
[O] 25
[I] \prgReturn{25}
[O] 25
[O] 25
[I] \mathSquare{25}
[I] \intEval{25*25}
[I] \expWhole{\int_eval:n {25*25}}
[O] 625
[I] \prgReturn{625}
[O] 625
[O] 625
[I] \intSet{\lTmpaInt }{625}
[O]
[I] \expValue{\lTmpaInt }
[O] 625
[I] \prgReturn{625}
[O] 625
[O] 625
\cs_new:Npn \cs_new:Nn
\cs_new_nopar:Npn \cs_new_nopar:Nn
\cs_new_protected:Npn \cs_new_protected:Nn
\cs_new_protected_nopar:Npn \cs_new_protected_nopar:Nn
Within functional package, there is only one command (\prgNewFunction) for defining new functions,
which is good for regular users. The created functions are always protected and accept \par in their
arguments.
Since functional package gets the results of functions by evaluation (including expansion and execution
by TEX), it is natural to protect all functions.
\module_foo:c \exp_args:Nc
\module_bar:e \exp_args:Ne
\module_bar:x \exp_args:Nx
\module_bar:f \exp_args:Nf
\module_bar:o \exp_args:No
\module_bar:V \exp_args:NV
\module_bar:v \exp_args:Nv
CHAPTER 1. OVERVIEW OF FEATURES 9
Within functional package, there are only three variants (c, e, V) are provided, and these variants are
defined as functions (\expName, \expWhole, \expValue, respectively), which are easier to use for regular
users.
\newcommand\test{uvw}
uvw
\expName{test}
\newcommand\test{uvw}
111uvw222
\expWhole{111\test222}
\intSet\lTmpaInt{123}
123
\expValue\lTmpaInt
The most interesting feature is that you can compose these functions. For example, you can easily get
the v variant of expl3 by simply composing \expName and \expValue functions:
\intSet\lTmpaInt{123}
123
\expValue{\expName{lTmpaInt}}
Chapter 2
Creates protected hfunctioni for evaluating the hcodei. Within the hcodei, the parameters (#1, #2, etc.)
will be replaced by those absorbed by the function. The returned value must be passed with \prgReturn
function. The definition is global and an error results if the hfunctioni is already defined.
The {hargument specificationi} in a list of letters, where each letter is one of the following argument
specifiers (nearly all of them are M or m for functions provided by this package):
M single-token argument, which will be manipulated first
m multi-token argument, which will be manipulated first
N single-token argument, which will not be manipulated first
n multi-token argument, which will not be manipulated first
The argument manipulation for argument type M or m is: if the argument starts with a function defined
with \prgNewFunction, the argument will be evaluated and replaced with the returned value.
Creates protected conditional hfunctioni for evaluating the hcodei. The returned value of the hfunctioni
must be either \cTrueBool or \cFalseBool and be passed with \prgReturn function.. The definition is
global and an error results if the hfunctioni is already defined.
Assume the hfunctioni is \fooIfBar, then another three functions are also created at the same time:
\fooIfBarT, \fooIfBarF, and \fooIfBarTF. They have extra arguments which are {htrue codei} or/and
{hfalse codei}. For example, if you write
10
CHAPTER 2. FUNCTIONAL PROGARMMING (PRG) 11
\prgReturn {htokensi}
Returns htokensi as result of current function or conditional. This function is normally used in the hcodei
of \prgNewFunction or \prgNewConditional, and it must be the last function evaluated in the hcodei.
If it is missing, the return value of the last function evaluated in the hcodei is returned. Therefore, the
following two examples produce the same output:
\IgnoreSpacesOn
\prgNewFunction \mathSquare { m } {
\intSet \lTmpaInt {\intEval {#1 * #1}}
\prgReturn {\expValue \lTmpaInt}
}
\IgnoreSpacesOff
\mathSquare{5}
\IgnoreSpacesOn
\prgNewFunction \mathSquare { m } {
\intSet \lTmpaInt {\intEval {#1 * #1}}
\expValue \lTmpaInt
}
\IgnoreSpacesOff
\mathSquare{5}
Functional package takes care of return values, and only print them to the input stream if the outer most
functions are evaluated.
\prgPrint {htokensi}
Prints htokensi directly to the input stream. If there is no function defined with \prgNewFunction in
htokensi, you can omit \prgPrint and write only htokensi. But if there is any function defined with
\prgNewFunction in htokensi, you have to use \prgPrint function.
\prgDo {hcodei}
Treats hcodei as an anonymous function with one to four arguments respectively, and evaluates it. In
evaluating the hcodei, functional package first evaluates harg1 i to harg4 i, then replaces #1 to #4 in hcodei
with the return values respectively.
Chapter 3
\evalWhole {htokensi}
Evaluates all functions (defined with \prgNewFunction) in htokensi and replaces them with their return
values, then returns the resulting tokens.
In the above example, \lTmpaTl contains a\intEval{2*3}b, while \lTmpbTl contains a6b.
\evalNone {htokensi}
Prevents the evaluation of its argument, returning htokensi without touching them.
Expands the hcontrol sequence namei until only characters remain, then converts this into a control
sequence and returns it. The hcontrol sequence namei must consist of character tokens when exhaustively
expanded.
\expValue hvariablei
Recovers the content of a hvariablei and returns the value. An error is raised if the variable does not
exist or if it is invalid. Note that it is the same as \tlUse for htl vari, or \intUse for hint vari.
\expWhole {htokensi}
12
CHAPTER 3. ARGUMENT USING (USE) 13
\unExpand {htokensi}
Prevents expansion of the htokensi inside the argument of \expWhole function. The argument of
\unExpand must be surrounded by braces.
\onlyName {htokensi}
Expands the htokensi until only characters remain, and then converts this into a control sequence. Further
expansion of this control sequence is then inhibited inside the argument of \expWhole function.
\onlyValue hvariablei
Recovers the content of the hvariablei, then prevents expansion of this material inside the argument of
\expWhole function.
\useOne {hargumenti}
\gobbleOne {hargumenti}
The function \useOne absorbs one argument and returns it. \gobbleOne absorbs one argument and
returns nothing. For example
\useOne{abc}\gobbleOne{ijk}\useOne{xyz} abcxyz
These functions absorb two arguments. The function \useGobble discards the second argument, and
returns the content of the first argument. \gobbleUse discards the first argument, and returns the
content of the second argument. For example
\useGobble{abc}{uvw}\gobbleUse{abc}{uvw} abcuvw
Chapter 4
\cTrueBool \cFalseBool
Constants that represent true and false, respectively. Used to implement predicates. For example
Scratch booleans for local assignment. These are never used by the 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.
Scratch booleans for global assignment. These are never used by the 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.
14
CHAPTER 4. CONTROL STRUCTURES (BOOL) 15
Contrarily to some other programming languages, the operators && and || evaluate both operands in all
cases, even when the first operand is enough to determine the result.
\boolNew hbooleani
Creates a new hbooleani or raises an error if the name is already taken. The declaration is global. The
hbooleani is initially false.
Creates a new constant hbooleani or raises an error if the name is already taken. The value of the
hbooleani is set globally to the result of evaluating the hboolexpri. For example
Evaluates the hboolean expressioni and sets the hbooleani variable to the logical truth of this evaluation.
For example
\boolSetTrue hbooleani
\boolSetFalse hbooleani
\boolSetTrue \lTmpaBool
\boolSetEq \lTmpbBool \lTmpaBool
\boolVarLog \lTmpbBool
Writes the logical truth of the hboolean expressioni in the log file.
CHAPTER 4. CONTROL STRUCTURES (BOOL) 16
\boolVarLog hbooleani
\boolVarShow hbooleani
\boolIfExist hbooleani
\boolIfExistT hbooleani {htrue codei}
\boolIfExistF hbooleani {hfalse codei}
\boolIfExistTF hbooleani {htrue codei} {hfalse codei}
Tests whether the hbooleani is currently defined. This does not check that the hbooleani really is a
boolean variable. For example
\boolVarIf hbooleani
\boolVarIfT hbooleani {htrue codei}
\boolVarIfF hbooleani {hfalse codei}
\boolVarIfTF hbooleani {htrue codei} {hfalse codei}
Tests the current truth of hbooleani, and continues evaluation based on this result. For example
\boolSetTrue \lTmpaBool
\boolVarIfTF \lTmpaBool {\prgReturn{True!}} {\prgReturn{False!}}
\boolSetFalse \lTmpaBool True! False!
\boolVarIfTF \lTmpaBool {\prgReturn{True!}} {\prgReturn{False!}}
\boolVarNot hbooleani
\boolVarNotT hbooleani {htrue codei}
\boolVarNotF hbooleani {hfalse codei}
\boolVarNotTF hbooleani {htrue codei} {hfalse codei}
Evaluates htrue codei if hbooleani is false, and hfalse codei if hbooleani is true. For example
Implements the “And” operation between two booleans, hence is true if both are true. For example
CHAPTER 4. CONTROL STRUCTURES (BOOL) 17
No
Implements the “Or” operation between two booleans, hence is true if either one is true. For example
Yes
Yes
Places the hcodei in the input stream for TEX to process, and then checks the logical value of the
hbooleani. If it is false then the hcodei is inserted into the input stream again and the process loops
until the hbooleani is true.
\IgnoreSpacesOn
\boolSetFalse \lTmpaBool
\intZero \lTmpaInt
\clistClear \lTmpaClist
\boolVarDoUntil \lTmpaBool {
\intIncr \lTmpaInt 1:2:3:4:5:6:7:8:9:10
\clistPutRight \lTmpaClist {\expValue\lTmpaInt}
\intCompareT {\lTmpaInt} = {10} {\boolSetTrue \lTmpaBool}
}
\clistVarJoin \lTmpaClist {:}
\IgnoreSpacesOff
Places the hcodei in the input stream for TEX to process, and then checks the logical value of the
hbooleani. If it is true then the hcodei is inserted into the input stream again and the process loops until
the hbooleani is false.
CHAPTER 4. CONTROL STRUCTURES (BOOL) 18
\IgnoreSpacesOn
\boolSetTrue \lTmpaBool
\intZero \lTmpaInt
\clistClear \lTmpaClist
\boolVarDoWhile \lTmpaBool {
\intIncr \lTmpaInt 1:2:3:4:5:6:7:8:9:10
\clistPutRight \lTmpaClist {\expValue\lTmpaInt}
\intCompareT {\lTmpaInt} = {10} {\boolSetFalse \lTmpaBool}
}
\clistVarJoin \lTmpaClist {:}
\IgnoreSpacesOff
This function firsts checks the logical value of the hbooleani. If it is false the hcodei is placed in the
input stream and expanded. After the completion of the hcodei the truth of the hbooleani is re-evaluated.
The process then loops until the hbooleani is true.
\IgnoreSpacesOn
\boolSetFalse \lTmpaBool
\intZero \lTmpaInt
\clistClear \lTmpaClist
\boolVarUntilDo \lTmpaBool {
\intIncr \lTmpaInt 1:2:3:4:5:6:7:8:9:10
\clistPutRight \lTmpaClist {\expValue\lTmpaInt}
\intCompareT {\lTmpaInt} = {10} {\boolSetTrue \lTmpaBool}
}
\clistVarJoin \lTmpaClist {:}
\IgnoreSpacesOff
This function firsts checks the logical value of the hbooleani. If it is true the hcodei is placed in the input
stream and expanded. After the completion of the hcodei the truth of the hbooleani is re-evaluated. The
process then loops until the hbooleani is false.
\IgnoreSpacesOn
\boolSetTrue \lTmpaBool
\intZero \lTmpaInt
\clistClear \lTmpaClist
\boolVarWhileDo \lTmpaBool {
\intIncr \lTmpaInt 1:2:3:4:5:6:7:8:9:10
\clistPutRight \lTmpaClist {\expValue\lTmpaInt}
\intCompareT {\lTmpaInt} = {10} {\boolSetFalse \lTmpaBool}
}
\clistVarJoin \lTmpaClist {:}
\IgnoreSpacesOff
Chapter 5
TEX works with tokens, and LATEX3 therefore provides a number of functions to deal with lists of tokens.
Token lists may be present directly in the argument to a function:
or may be stored in a so-called “token list variable”, which have the suffix Tl: a token list variable can
also be used as the argument to a function, for example
\tlVarFoo \lSomeTl
In both cases, functions are available to test and manipulate the lists of tokens, and these have the module
prefix Tl. In many cases, functions which can be applied to token list variables are paired with similar
functions for application to explicit lists of tokens: the two “views” of a token list are therefore collected
together here.
A token list (explicit, or stored in a variable) can be seen either as a list of “items”, or a list of “tokens”.
An item is whatever \useOne would grab as its argument: a single non-space token or a brace group,
with optional leading explicit space characters (each item is thus itself a token list). A token is either a
normal N argument, or , {, or } (assuming normal TEX category codes). Thus for example
{Hello} world
contains 6 items (Hello, w, o, r, l and d), but 13 tokens ({, H, e, l, l, o, }, , w, o, r, l and d). Functions
which act on items are often faster than their analogue acting directly on tokens.
\cSpaceTl
An explicit space character contained in a token list. For use where an explicit space is required.
\cEmptyTl
Scratch token lists for local assignment. These are never used by the 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.
19
CHAPTER 5. TOKEN LISTS (TL) 20
Scratch token lists for global assignment. These are never used by the 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.
Creates a new htl vari or raises an error if the name is already taken. The declaration is global. The
htl vari is initially empty.
\tlNew \lFooSomeTl
Creates a new constant htl vari or raises an error if the name is already taken. The value of the htl vari
is set globally to the htoken listi.
Recovers the content of a htl vari 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 htl vari directly without an accessor function.
\tlUse \lTmpbTl
Converts the htoken listi to a hstringi, returning the resulting character tokens. A hstringi is a series of
tokens with category code 12 (other) with the exception of spaces, which retain category code 10 (space).
Converts the content of the htl vari to a string, returning the resulting character tokens. A hstringi is a
series of tokens with category code 12 (other) with the exception of spaces, which retain category code
10 (space).
Writes the htoken listi in the log file. See also \tlShow which displays the result in the terminal.
\tlLog {123\abc456}
Writes the content of the htl vari in the log file. See also \tlVarShow which displays the result in the
terminal.
\tlShow {123\abc456}
Sets htl vari to contain htokensi, removing any previous content from the variable.
Ensures that the htl vari exists globally by applying \tlNew if necessary, then applies \tlClear to leave
the htl vari empty.
\tlClearNew \lFooSomeTl
Concatenates the content of htl var2 i and htl var3 i together and saves the result in htl var1 i. The htl var2 i
is placed at the left side of the new token list.
Appends htokensi to the left side of the current content of htl vari.
Appends htokensi to the right side of the current content of htl vari.
Replaces the first (leftmost) occurrence of hold tokensi in the htl vari with hnew tokensi. hOld tokensi
cannot contain {, } or # (more precisely, explicit character tokens with category code 1 (begin-group) or
2 (end-group), and tokens with category code 6).
CHAPTER 5. TOKEN LISTS (TL) 23
Replaces all occurrences of hold tokensi in the htl vari with hnew tokensi. hOld tokensi cannot contain
{, } or # (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 hold tokensi
may remain after the replacement (see \tlVarRemoveAll for an example).
Removes the first (leftmost) occurrence of htokensi from the htl vari. hTokensi cannot contain {, } or
# (more precisely, explicit character tokens with category code 1 (begin-group) or 2 (end-group), and
tokens with category code 6).
Removes all occurrences of htokensi from the htl vari. hTokensi cannot contain {, } or # (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 htokensi may remain after the removal,
for instance,
Removes any leading and trailing explicit space characters (explicit tokens with character code 32 and
category code 10) from the htoken listi and returns the result.
Sets the htl vari 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.
\tlSet \lTmpaTl { 12 34 }
\tlVarTrimSpaces \lTmpaTl Foo12 34Bar
Foo\tlUse \lTmpaTl Bar
CHAPTER 5. TOKEN LISTS (TL) 24
\tlCount {htokensi}
Counts the number of hitemsi in htokensi and returns this information. Unbraced tokens count as one
element as do each token group ({· · · }). This process ignores any unprotected spaces within htokensi.
\tlCount {12\abc34} 5
Counts the number of hitemsi in the htl vari and returns this information. Unbraced tokens count as one
element as do each token group ({· · · }). This process ignores any unprotected spaces within the htl vari.
Returns the first hitemi in the htoken listi, discarding the rest of the htoken listi. All leading explicit
space characters (explicit tokens with character code 32 and category code 10) are discarded; for example
If the “head” is a brace group, rather than a single token, the braces are removed, and so
\tlHead { { ab} c }
yields ab. A blank htoken listi (see \tlIfBlank) results in \tlHead returning nothing.
Returns the first hitemi in the htl vari, discarding the rest of the htl vari. All leading explicit space
characters (explicit tokens with character code 32 and category code 10) are discarded.
Discards all leading explicit space characters (explicit tokens with character code 32 and category code
10) and the first hitemi in the htoken listi, and returns the remaining tokens. Thus for example
\tlTail { a {bc} d }
and
\tlTail { a {bc} d }
both return {bc} d . A blank htoken listi (see \tlIfBlank) results in \tlTail returning nothing.
CHAPTER 5. TOKEN LISTS (TL) 25
Discards all leading explicit space characters (explicit tokens with character code 32 and category code
10) and the first hitemi in the htl vari, and returns the remaining tokens.
Indexing items in the htoken listi from 1 on the left, this function evaluates the hinteger expressioni and
returns the appropriate item from the htoken listi. If the hinteger expressioni 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.
Selects and returns a pseudo-random item of the htoken listi. If the htoken listi is blank, the result is
empty.
\tlRandItem {abcdef}
ae
\tlRandItem {abcdef}
Applies the hinline functioni to every hitemi stored within the htoken listi. The hinline functioni should
consist of code which receives the hitemi as #1.
\IgnoreSpacesOn
\tlClear \lTmpaTl
\tlMapInline {one} {
\tlPutRight \lTmpaTl {[#1]} [o][n][e]
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
Applies the hinline functioni to every hitemi stored within the htl vari. The hinline functioni should
consist of code which receives the hitemi as #1.
CHAPTER 5. TOKEN LISTS (TL) 26
\IgnoreSpacesOn
\tlClear \lTmpaTl
\tlSet \lTmpkTl {one}
\tlVarMapInline \lTmpkTl {
[o][n][e]
\tlPutRight \lTmpaTl {[#1]}
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
Stores each hitemi of the htoken listi in turn in the (token list) hvariablei and applies the hcodei. The
hcodei will usually make use of the hvariablei, but this is not enforced. The assignments to the hvariablei
are local. Its value after the loop is the last hitemi in the htl vari, or its original value if the htl vari is
blank.
\IgnoreSpacesOn
\tlClear \lTmpaTl
\tlMapVariable {one} \lTmpiTl {
\tlPutRight \lTmpaTl {\expWhole {[\lTmpiTl]}} [o][n][e]
}
\prgReturn{\tlUse\lTmpaTl}
\IgnoreSpacesOff
Stores each hitemi of the htl vari in turn in the (token list) hvariablei and applies the hcodei. The hcodei
will usually make use of the hvariablei, but this is not enforced. The assignments to the hvariablei are
local. Its value after the loop is the last hitemi in the htl vari, or its original value if the htl vari is blank.
\IgnoreSpacesOn
\tlClear \lTmpaTl
\tlSet \lTmpkTl {one}
\tlVarMapVariable \lTmpkTl \lTmpiTl {
[o][n][e]
\tlPutRight \lTmpaTl {\expWhole {[\lTmpiTl]}}
}
\prgReturn{\tlUse\lTmpaTl}
\IgnoreSpacesOff
Tests whether the htl vari is currently defined. This does not check that the htl vari really is a token list
variable.
Tests if the htoken listi is entirely empty (i.e. contains no tokens at all). For example
Tests if the htoken list variablei is entirely empty (i.e. contains no tokens at all). For example
Tests if the htoken listi consists only of blank spaces (i.e. contains no item). The test is true if htoken
listi is zero or more explicit space characters (explicit tokens with character code 32 and category code
10), and is false otherwise.
Tests if htoken list1 i and htoken list2 i contain the same list of tokens, both in respect of character codes
and category codes. See \strIfEq if category codes are not important. For example
Compares the content of two htoken list variablesi and is logically true if the two contain the same
list of tokens (i.e. identical in both the list of characters they contain and the category codes of those
characters). For example
CHAPTER 5. TOKEN LISTS (TL) 28
Tests if htoken list2 i is found inside htoken list1 i. The htoken list2 i cannot contain the tokens {, } or
# (more precisely, explicit character tokens with category code 1 (begin-group) or 2 (end-group), and
tokens with category code 6). The search does not enter brace (category code 1/2) groups.
Tests if the htoken listi is found in the content of the htl vari. The htoken listi cannot contain the tokens
{, } or # (more precisely, explicit character tokens with category code 1 (begin-group) or 2 (end-group),
and tokens with category code 6).
Tests if the htoken listi has exactly one hitemi, 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 \tlCount.
Tests if the content of the htl vari consists of a single hitemi, 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 \tlVarCount.
CHAPTER 5. TOKEN LISTS (TL) 29
This function compares the htest token list variablei in turn with each of the htoken list variable casesi.
If the two are equal (as described for \tlVarIfEq) then the associated hcodei is left in the input stream
and other cases are discarded. The function does nothing if there is no match.
\IgnoreSpacesOn
\tlSet \lTmpaTl {a}
\tlSet \lTmpbTl {b}
\tlSet \lTmpcTl {c}
\tlSet \lTmpkTl {b}
\tlVarCase \lTmpkTl { Second
\lTmpaTl {\prgReturn {First}}
\lTmpbTl {\prgReturn {Second}}
\lTmpcTl {\prgReturn {Third}}
}
\IgnoreSpacesOff
This function compares the htest token list variablei in turn with each of the htoken list variable casesi.
If the two are equal (as described for \tlVarIfEq) then the associated hcodei is left in the input stream
and other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the
input stream (after the code for the appropriate case).
CHAPTER 5. TOKEN LISTS (TL) 30
\IgnoreSpacesOn
\tlSet \lTmpaTl {a}
\tlSet \lTmpbTl {b}
\tlSet \lTmpcTl {c}
\tlSet \lTmpkTl {b}
\tlVarCaseT \lTmpkTl {
\lTmpaTl {\intSet \lTmpkInt {1}} 2
\lTmpbTl {\intSet \lTmpkInt {2}}
\lTmpcTl {\intSet \lTmpkInt {3}}
}{
\prgReturn {\intUse \lTmpkInt}
}
\IgnoreSpacesOff
This function compares the htest token list variablei in turn with each of the htoken list variable casesi.
If the two are equal (as described for \tlVarIfEq) then the associated hcodei is left in the input stream
and other cases are discarded. If none match then the hfalse codei is inserted into the input stream (after
the code for the appropriate case).
\IgnoreSpacesOn
\tlSet \lTmpaTl {a}
\tlSet \lTmpbTl {b}
\tlSet \lTmpcTl {c}
\tlSet \lTmpkTl {b}
\tlVarCaseF \lTmpkTl{
\lTmpaTl {\prgReturn {First}} Second
\lTmpbTl {\prgReturn {Second}}
\lTmpcTl {\prgReturn {Third}}
}{
\prgReturn {No~Match!}
}
\IgnoreSpacesOff
This function compares the htest token list variablei in turn with each of the htoken list variable casesi. If
the two are equal (as described for \tlVarIfEq) then the associated hcodei is left in the input stream and
other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input
stream (after the code for the appropriate case), while if none match then the hfalse codei is inserted.
CHAPTER 5. TOKEN LISTS (TL) 31
The function \tlVarCase, which does nothing if there is no match, is also available.
\IgnoreSpacesOn
\tlSet \lTmpaTl {a}
\tlSet \lTmpbTl {b}
\tlSet \lTmpcTl {c}
\tlSet \lTmpkTl {b}
\tlVarCaseTF \lTmpkTl {
\lTmpaTl {\intSet \lTmpkInt {1}}
\lTmpbTl {\intSet \lTmpkInt {2}} 2
\lTmpcTl {\intSet \lTmpkInt {3}}
}{
\prgReturn {\intUse \lTmpkInt}
}{
\prgReturn {0}
}
\IgnoreSpacesOff
Chapter 6
Strings (Str)
TEX associates each character with a category code: as such, there is no concept of a “string” as com-
monly understood in many other programming languages. However, there are places where we wish to
manipulate token lists while in some sense “ignoring” category codes: this is done by treating token lists
as strings in a TEX sense.
A TEX string (and thus an expl3 string) is a series of characters which have category code 12 (“other”)
with the exception of space characters which have category code 10 (“space”). Thus at a technical level,
a TEX string is a token list with the appropriate category codes. In this documentation, these are simply
referred to as strings.
String variables are simply specialised token lists, but by convention should be named with the suffix
Str. Such variables should contain characters with category code 12 (other), except spaces, which have
category code 10 (blank space). All the functions in this module which accept a token list argument
first convert it to a string using \tlToStr for internal processing, and do not treat a token list or the
corresponding string representation differently.
As a string is a subset of the more general token list, it is sometimes unclear when one should be used
over the other. Use a string variable for data that isn’t primarily intended for typesetting and for which a
level of protection from unwanted expansion is suitable. This data type simplifies comparison of variables
since there are no concerns about expansion of their contents.
Constant strings, containing a single character token, with category code 12.
Scratch strings for local assignment. These are never used by the 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.
Scratch strings for global assignment. These are never used by the 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.
32
CHAPTER 6. STRINGS (STR) 33
Creates a new hstr vari or raises an error if the name is already taken. The declaration is global. The
hstr vari is initially empty.
\strNew \lFooSomeStr
Creates a new constant hstr vari or raises an error if the name is already taken. The value of the hstr
vari is set globally to the htoken listi, converted to a string.
Recovers the content of a hstr vari 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 hstri directly without an accessor function.
\strUse \lTmpaStr
\strLog {1234\abcd5678}
\strShow {1234\abcd5678}
Converts the htoken listi to a hstringi, and stores the result in hstr vari.
Ensures that the hstr vari exists globally by applying \strNew if necessary, then applies \strClear to
leave the hstr vari empty.
\strClearNew \lFooSomeStr
\strUse \lFooSomeStr
Concatenates the content of hstr var2 i and hstr var3 i together and saves the result in hstr var1 i. The hstr
var2 i is placed at the left side of the new string variable. The hstr var2 i and hstr var3 i must indeed be
strings, as this function does not convert their contents to a string.
Converts the htoken listi to a hstringi, and prepends the result to hstr vari. The current contents of the
hstr vari are not automatically converted to a string.
Converts the htoken listi to a hstringi, and appends the result to hstr vari. The current contents of the
hstr vari are not automatically converted to a string.
Converts the holdi and hnewi token lists to strings, then replaces the first (leftmost) occurrence of hold
stringi in the hstr vari with hnew stringi.
Converts the holdi and hnewi token lists to strings, then replaces all occurrences of hold stringi in the hstr
vari with hnew stringi. As this function operates from left to right, the pattern hold stringi may remain
after the replacement.
Converts the htoken listi to a hstringi then removes the first (leftmost) occurrence of hstringi from the
hstr vari.
Converts the htoken listi to a hstringi then removes all occurrences of hstringi from the hstr vari. As this
CHAPTER 6. STRINGS (STR) 36
function operates from left to right, the pattern hstringi may remain after the removal, for instance,
Returns the number of characters in the string representation of htoken listi, as an integer denotation.
All characters including spaces are counted.
\strCount {12\abc34} 9
Returns the number of characters in the string representation of the htl vari, as an integer denotation.
All characters including spaces are counted.
Converts the htoken listi into a hstringi. The first character in the hstringi is then returned, with category
code “other”. If the first character is a space, it returns a space token with category code 10 (blank space).
If the hstringi is empty, then nothing is returned.
\strHead {HELLO} H
Converts the htl vari into a hstringi. The first character in the hstringi is then returned, with category
code “other”. If the first character is a space, it returns a space token with category code 10 (blank
space). If the hstringi is empty, then nothing is returned.
Converts the htoken listi to a hstringi, 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 htoken listi is empty, then nothing is left on the input stream.
Converts the htl vari to a hstringi, 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 htoken listi is empty, then nothing is left on the input stream.
Converts the htoken listi to a hstringi, and returns the character in position hinteger expressioni of the
hstringi, starting at 1 for the first (left-most) character. All characters including spaces are taken into
account. If the hinteger expressioni is negative, characters are counted from the end of the hstringi.
Hence, −1 is the right-most character, etc.
Converts the htl vari to a hstringi, and returns the character in position hinteger expressioni of the hstringi,
starting at 1 for the first (left-most) character. All characters including spaces are taken into account.
If the hinteger expressioni is negative, characters are counted from the end of the hstringi. Hence, −1 is
the right-most character, etc.
Converts the htoken listi to a hstringi then applies the hinline functioni to every hcharacteri in the hstr
vari including spaces. The hinline functioni should consist of code which receives the hcharacteri as #1.
\IgnoreSpacesOn
\strClear \lTmpaStr
\strMapInline {one} {
\strPutRight \lTmpaStr {[#1]} [o][n][e]
}
\strUse \lTmpaStr
\IgnoreSpacesOff
Converts the htoken listi to a hstringi then stores each hcharacteri in the hstringi (including spaces) in
turn in the (string or token list) hvariablei and applies the hcodei. The hcodei will usually make use of
the hvariablei, but this is not enforced. The assignments to the hvariablei are local. Its value after the
loop is the last hcharacteri in the hstringi, or its original value if the hstringi is empty.
CHAPTER 6. STRINGS (STR) 38
\IgnoreSpacesOn
\strClear \lTmpaStr
\strMapVariable {one} \lTmpiStr {
\strPutRight \lTmpaStr {\expWhole {[\lTmpiStr]}} [o][n][e]
}
\strUse \lTmpaStr
\IgnoreSpacesOff
Tests whether the hstr vari is currently defined. This does not check that the hstr vari really is a string.
Tests if the hstring variablei is entirely empty (i.e. contains no characters at all).
NonEmpty Empty
Compares the two htoken listsi on a character by character basis (namely after converting them to strings),
and is true if the two hstringsi contain the same characters in the same order. See \tlIfEq to compare
tokens (including their category codes) rather than characters. For example
Compares the content of two hstr variablesi and is logically true if the two contain the same characters
CHAPTER 6. STRINGS (STR) 39
in the same order. See \tlVarIfEq to compare tokens (including their category codes) rather than
characters.
Converts both htoken listsi to hstringsi and tests whether hstring2 i is found inside hstring1 i.
Converts the htoken listi to a hstringi and tests if that hstringi is found in the content of the hstr vari.
Compares the two htoken listsi 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 hrelationi can be <, =,
or > and the test is true under the following conditions:
• for <, if the first string is earlier than the second in lexicographic order;
• for =, if the two strings have exactly the same characters;
• for >, if the first string is later than the second in lexicographic order.
For example:
Compares the htest stringi in turn with each of the hstring casesi (all token lists are converted to strings).
If the two are equal (as described for \strIfEq) then the associated hcodei is left in the input stream
and other cases are discarded.
\IgnoreSpacesOn
\strCase {bbb} {
{aaa} {\prgReturn{First}}
{bbb} {\prgReturn{Second}} Second
{ccb} {\prgReturn{Third}}
}
\IgnoreSpacesOff
Compares the htest stringi in turn with each of the hstring casesi (all token lists are converted to strings).
If the two are equal (as described for \strIfEq) then the associated hcodei is left in the input stream
and other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the
input stream (after the code for the appropriate case).
\IgnoreSpacesOn
\strCaseT {bbb} {
{aaa} {\tlSet\lTmpkTl{First}}
{bbb} {\tlSet\lTmpkTl{Second}}
{ccb} {\tlSet\lTmpkTl{Third}} Second
}{
\prgReturn{\tlUse\lTmpkTl}
}
\IgnoreSpacesOff
Compares the htest stringi in turn with each of the hstring casesi (all token lists are converted to strings).
CHAPTER 6. STRINGS (STR) 41
If the two are equal (as described for \strIfEq) then the associated hcodei is left in the input stream
and other cases are discarded. If none match then the hfalse codei is inserted.
\IgnoreSpacesOn
\strCaseF {bbb} {
{aaa} {\prgReturn{First}}
{bbb} {\prgReturn{Second}}
{ccb} {\prgReturn{Third}} Second
}{
\prgReturn{No~Match!}
}
\IgnoreSpacesOff
Compares the htest stringi in turn with each of the hstring casesi (all token lists are converted to strings).
If the two are equal (as described for \strIfEq) then the associated hcodei is left in the input stream and
other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input
stream (after the code for the appropriate case), while if none match then the hfalse codei is inserted.
\IgnoreSpacesOn
\strCaseTF {bbb} {
{aaa} {\tlSet\lTmpkTl{First}}
{bbb} {\tlSet\lTmpkTl{Second}}
{ccb} {\tlSet\lTmpkTl{Third}}
}{ Second
\prgReturn{\tlUse\lTmpkTl}
}{
\prgReturn{No~Match!}
}
\IgnoreSpacesOff
Chapter 7
Integers (Int)
\cZeroInt \cOneInt
Integer values used with primitive tests and assignments: their self-terminating nature makes these more
convenient and faster than literal numbers.
\cMaxInt
\cMaxRegisterInt
\cMaxCharInt
Scratch integer for local assignment. These are never used by the 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.
Scratch integer for global assignment. These are never used by the 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.
• / denotes division rounded to the closest integer with ties rounded away from zero;
42
CHAPTER 7. INTEGERS (INT) 43
• there is an error and the overall expression evaluates to zero whenever the absolute value of any
intermediate result exceeds 231 − 1, except in the case of scaling operations a*b/c, for which a*b
may be arbitrarily large (but the operands a, b, c are still constrained to an absolute value at most
231 − 1);
• parentheses may not appear after unary + or -, namely placing +( or -( at the start of an expression
or after +, -, *, / or ( leads to an error.
Each integer operand can be either an integer variable (with no need for \intUse) or an integer denotation.
For example both of the following give the same result because \lFooSomeTl expands to the integer
denotation 5 while the integer variable \lFooSomeInt takes the value 4.
\intEval {5 + 4 * 3 - (3 + 4 * 5)} -6
\tlNew \lFooSomeTl
\tlSet \lFooSomeTl {5}
\intNew \lFooSomeInt -6
\intSet \lFooSomeInt {4}
\intEval {\lFooSomeTl + \lFooSomeInt * 3 - (3 + 4 * 5)}
Evaluates the hinteger expressioni and returns the result: for positive results an explicit sequence of
decimal digits not starting with 0, for negative results - followed by such a sequence, and 0 for zero. For
example
\intEval {(1+4)*(2-3)/5} -1
Adds {hinteger expression1 i} and {hinteger expression2 i}, and returns the result. For example
Subtracts {hinteger expression2 i} from {hinteger expression1 i}, and returns the result. For example
Multiplies {hinteger expression1 i} by {hinteger expression2 i}, and returns the result. For example
Evaluates the two hinteger expressionsi 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 / directly in an hinteger expressioni. The result is returned as an hinteger denotationi. For
example
Evaluates the two hinteger expressionsi as described earlier, then divides the first value by the second,
and rounds the result towards zero. Note that division using / rounds to the closest integer instead. The
result is returned as an hinteger denotationi. For example
\intMathSign {hintexpri}
Evaluates the hinteger expressioni then leaves 1 or 0 or −1 in the input stream according to the sign of
the result.
Evaluates the hinteger expressioni as described for \intEval and leaves the absolute value of the result
in the input stream as an hinteger denotationi after two expansions.
Evaluates the hinteger expressionsi as described for \intEval and leaves either the larger or smaller value
in the input stream as an hinteger denotationi after two expansions.
Evaluates the two hinteger expressionsi as described earlier, then calculates the integer remainder of divid-
ing the first expression by the second. This is obtained by subtracting \intMathDivTruncate {hintexpr1 i}
{hintexpr2 i} times hintexpr2 i from hintexpr1 i. Thus, the result has the same sign as hintexpr1 i and its
absolute value is strictly less than that of hintexpr2 i. The result is left in the input stream as an hinteger
denotationi after two expansions.
Evaluates the two hinteger expressionsi and produces a pseudo-random number between the two (with
bounds included).
\intNew hintegeri
Creates a new hintegeri or raises an error if the name is already taken. The declaration is global. The
hintegeri is initially equal to 0.
CHAPTER 7. INTEGERS (INT) 45
Creates a new constant hintegeri or raises an error if the name is already taken. The value of the hintegeri
is set globally to the hinteger expressioni.
\intUse hintegeri
Recovers the content of an hintegeri and returns the value. An error is raised if the variable does not
exist or if it is invalid.
Writes the result of evaluating the hinteger expressioni in the log file.
\intVarLog hintegeri
\intVarShow hintegeri
Sets hintegeri to the value of hinteger expressioni, which must evaluate to an integer (as described for
\intEval). For example
\intZero hintegeri
\intZeroNew hintegeri
Ensures that the hintegeri exists globally by applying \intNew if necessary, then applies \intZero to
leave the hintegeri set to zero.
\intIncr hintegeri
\intDecr hintegeri
Adds the result of the hinteger expressioni to the current content of the hintegeri. For example
Subtracts the result of the hinteger expressioni from the current content of the hintegeri. For example
Evaluates the hinteger expressioni (which should be zero or positive) and returns the resulting number
of copies of the htokensi.
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be integer
expressions. Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using hstepi between
CHAPTER 7. INTEGERS (INT) 47
each hvaluei), the hcodei is inserted into the input stream with #1 replaced by the current hvaluei. Thus
the hcodei should define a function of one argument (#1).
\IgnoreSpacesOn
\tlClear \lTmpaTl
\intStepInline {1} {3} {30} {
\tlPutRight \lTmpaTl {[#1]} [1][4][7][10][13][16][19][22][25][28]
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
This function first evaluates the hinitial valuei and hfinal valuei, all of which should be integer expressions.
Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using a fixed step of 1 between
each hvaluei), the hcodei is inserted into the input stream with #1 replaced by the current hvaluei. Thus
the hcodei should define a function of one argument (#1).
\IgnoreSpacesOn
\tlClear \lTmpaTl
\intStepOneInline {1} {10} {
\tlPutRight \lTmpaTl {[#1]} [1][2][3][4][5][6][7][8][9][10]
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be integer
expressions. Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using hstepi
between each hvaluei), the hcodei is evaluated, with the htl vari defined as the current hvaluei. Thus the
hcodei should make use of the htl vari.
This function first evaluates the hinitial valuei and hfinal valuei, all of which should be integer expressions.
Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using a fixed stop of 1 between
each hvaluei), the hcodei is evaluated, with the htl vari defined as the current hvaluei. Thus the hcodei
should make use of the htl vari.
\intIfExist hintegeri
\intIfExistT hintegeri {htrue codei}
\intIfExistF hintegeri {hfalse codei}
\intIfExistTF hintegeri {htrue codei} {hfalse codei}
Tests whether the hintegeri is currently defined. This does not check that the hintegeri really is an integer
variable.
CHAPTER 7. INTEGERS (INT) 48
This function first evaluates the hinteger expressioni as described for \intEval. It then evaluates if this
is odd or even, as appropriate.
This function first evaluates the hinteger expressioni as described for \intEval. It then evaluates if this
is even or odd, as appropriate.
This function first evaluates each of the hinteger expressionsi as described for \intEval. The two results
are then compared using the hrelationi:
Equal =
Greater than >
Less than <
For example
This function evaluates the htest integer expressioni and compares this in turn to each of the hinteger
expression casesi. If the two are equal then the associated hcodei is left in the input stream and other
cases are discarded.
This function evaluates the htest integer expressioni and compares this in turn to each of the hinteger
CHAPTER 7. INTEGERS (INT) 49
expression casesi. If the two are equal then the associated hcodei is left in the input stream and other
cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input stream
(after the code for the appropriate case).
This function evaluates the htest integer expressioni and compares this in turn to each of the hinteger
expression casesi. If the two are equal then the associated hcodei is left in the input stream and other
cases are discarded. If none match then the hfalse codei is into the input stream (after the code for the
appropriate case). For example
\IgnoreSpacesOn
\intCaseF { 2 * 5 }
{
{ 5 } { Small }
{ 4 + 6 } { Medium } Medium
{ -2 * 10 } { Negative }
}
{ No idea! }
\IgnoreSpacesOff
This function evaluates the htest integer expressioni and compares this in turn to each of the hinteger
expression casesi. If the two are equal then the associated hcodei is left in the input stream and other
cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input stream
(after the code for the appropriate case), while if none match then the hfalse codei is inserted.
Chapter 8
\cZeroFp \cMinusZeroFp
\cOneFp
\cInfFp \cMinusInfFp
Infinity, with either sign. These can be input directly in a floating point expression as inf and -inf.
\cEFp
\cPiFp
The value of π. This can be input directly in a floating point expression as pi.
\cOneDegreeFp
The value of 1◦ 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 deg.
Scratch floating point numbers for local assignment. These are never used by the 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.
Scratch floating point numbers for global assignment. These are never used by the 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.
50
CHAPTER 8. FLOATING POINT NUMBERS (FP) 51
• Basic
√ arithmetic: addition x + y, subtraction x − y, multiplication x ∗ y, division x/y, square root
x, and parentheses.
• Comparison operators: x < y, x <= y, x >? y, x ! = y etc.
• Boolean logic: sign sign x, negation ! x, conjunction x && y, disjunction x || y, ternary operator
x ? y : z.
• Exponentials: exp x, ln x, xy , logb x.
• Integer factorial: fact x.
• Trigonometry: sin x, cos x, tan x, cot x, sec x, csc x expecting their arguments in radians, and sind x,
cosd x, tand x, cotd x, secd x, cscd x expecting their arguments in degrees.
• Inverse trigonometric functions: asin x, acos x, atan x, acot x, asec x, acsc x giving a result in radians,
and asind x, acosd x, atand x, acotd x, asecd x, acscd x giving a result in degrees.
• Extrema: max(x1 , x2 , . . .), min(x1 , x2 , . . .), abs(x).
• Rounding functions, controlled by two optional values, n (number of places, 0 by default) and t
(behavior on a tie, NaN by default):
– trunc(x, n) rounds towards zero,
– floor(x, n) rounds towards −∞,
– ceil(x, n) rounds towards +∞,
– round(x, n, t) rounds to the closest value, with ties rounded to an even value by default, towards
zero if t = 0, towards +∞ if t > 0 and towards −∞ if t < 0.
• Random numbers: rand(), randint(m, n).
• Tuples: (x1 , . . . , xn ) that can be stored in variables, added together, multiplied or divided by a
floating point number, and nested.
Floating point numbers can be given either explicitly (in a form such as 1.234e-34, or -.0001), or as a
stored floating point variable, which is automatically replaced by its current value. A “floating point” is
a floating point number or a tuple thereof.
An example of use could be the following.
The operation round can be used to limit the result’s precision. Adding +0 avoids the possibly undesirable
output -0, replacing it by +0.
CHAPTER 8. FLOATING POINT NUMBERS (FP) 52
Evaluates the hfloating point expressioni 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 ±∞ and NaN trigger an
“invalid operation” exception. For a tuple, each item is converted using \fpEval and they are combined
as (hfp1 i, hfp2 i, …hfpn i) if n > 1 and (hfp1 i,) or () for fewer items. For example
Adds {hfpexpr1 i} and {hfpexpr2 i}, and returns the result. For example
Subtracts {hfpexpr2 i} from {hfpexpr1 i}, and returns the result. For example
Multiplies {hfpexpr1 i} by {hfpexpr2 i}, and returns the result. For example
Divides {hfpexpr1 i} by {hfpexpr2 i}, and returns the result. For example
\fpMathSign {hfpexpri}
Evaluates the hfpexpri and returns the value using \fpEval{sign(hresulti)}: +1 for positive numbers
and for +∞, −1 for negative numbers and for −∞, ±0 for ±0. If the operand is a tuple or is NaN, then
“invalid operation” occurs and the result is 0. For example
\fpMathSign {3.5}
\fpMathSign {-2.7} 1 -1
CHAPTER 8. FLOATING POINT NUMBERS (FP) 53
Evaluates the hfloating point expressioni as described for \fpEval and returns the absolute value. If the
argument is ±∞, NaN or a tuple, “invalid operation” occurs. Within floating point expressions, abs()
can be used; it accepts ±∞ and NaN as arguments.
Evaluates the hfloating point expressionsi as described for \fpEval and returns the resulting larger (max)
or smaller (min) value. If the argument is a tuple, “invalid operation” occurs, but no other case raises
exceptions. Within floating point expressions, max() and min() can be used.
Creates a new hfp vari or raises an error if the name is already taken. The declaration is global. The
hfp vari is initially +0.
Creates a new constant hfp vari or raises an error if the name is already taken. The hfp vari is set globally
equal to the result of evaluating the hfloating point expressioni. For example
Recovers the value of the hfp vari and returns the value as a decimal number with no exponent.
Evaluates the hfloating point expressioni and writes the result in the log file.
Evaluates the hfloating point expressioni and displays the result in the terminal.
Sets hfp vari equal to the result of computing the hfloating point expressioni. For example
Sets the floating point variable hfp var1 i equal to the current value of hfp var2 i.
Ensures that the hfp vari exists globally by applying \fpNew if necessary, then applies \fpZero to leave
the hfp vari set to +0.
Adds the result of computing the hfloating point expressioni to the hfp vari. This also applies if hfp vari
and hfloating point expressioni evaluate to tuples of the same size. For example
Subtracts the result of computing the hfloating point expressioni from the hfp vari. This also applies if
hfp vari and hfloating point expressioni evaluate to tuples of the same size. For example
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be floating
point expressions evaluating to a floating point number, not a tuple. Then for each hvaluei from the
CHAPTER 8. FLOATING POINT NUMBERS (FP) 55
hinitial valuei to the hfinal valuei in turn (using hstepi between each hvaluei), the hcodei is inserted into
the input stream with #1 replaced by the current hvaluei. Thus the hcodei should define a function of
one argument (#1).
\IgnoreSpacesOn
\tlClear \lTmpaTl
\fpStepInline {1} {0.1} {1.5} {
\tlPutRight \lTmpaTl {[#1]} [1][1.1][1.2][1.3][1.4][1.5]
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be floating
point expressions evaluating to a floating point number, not a tuple. Then for each hvaluei from the
hinitial valuei to the hfinal valuei in turn (using hstepi between each hvaluei), the hcodei is inserted into
the input stream, with the htl vari defined as the current hvaluei. Thus the hcodei should make use of
the htl vari.
Tests whether the hfp vari is currently defined. This does not check that the hfp vari really is a floating
point variable. For example
Compares the hfpexpr1 i and the hfpexpr2 i, and returns true if the hrelationi is obeyed. For example
Two floating points x and y may obey four mutually exclusive relations: x < y, x = y, x > y, or x?y
(“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 “not ordered” relation.
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 ? (not ordered). This is experimental.
Chapter 9
Dimensions (Dim)
\cMaxDim
The maximum value that can be stored as a dimension. This can also be used as a component of a skip.
\cZeroDim
Scratch dimensions for local assignment. These are never used by the 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.
Scratch dimensions for global assignment. These are never used by the 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.
Evaluates the hdimension expressioni, expanding any dimensions and token list variables within the
hexpressioni to their content (without requiring \dimUse/\tlUse) and applying the standard mathemat-
ical rules. The result of the calculation is returned as a hdimension denotationi. For example
Adds {hdimexpr1 i} and {hdimexpr2 i}, and returns the result. For example
56
CHAPTER 9. DIMENSIONS (DIM) 57
Subtracts {hdimexpr2 i} from {hdimexpr1 i}, and returns the result. For example
Parses the two hdimension expressionsi, 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
The returned value is suitable for use inside a hdimension expressioni such as
\dimMathSign {hdimexpri}
Evaluates the hdimexpri then returns 1 or 0 or −1 according to the sign of the result. For example
\dimMathSign {3.5pt}
\dimMathSign {-2.7pt} 1 -1
\dimMathAbs {hdimexpri}
Converts the hdimexpri to its absolute value, returning the result as a hdimension denotationi. For
example
\dimMathAbs {3.5pt}
3.5pt 2.7pt
\dimMathAbs {-2.7pt}
Evaluates the two hdimension expressionsi and returns either the maximum or minimum value as appro-
priate as a hdimension denotationi. For example
\dimNew hdimensioni
Creates a new hdimensioni or raises an error if the name is already taken. The declaration is global. The
hdimensioni is initially equal to 0 pt.
CHAPTER 9. DIMENSIONS (DIM) 58
Creates a new constant hdimensioni or raises an error if the name is already taken. The value of the
hdimensioni is set globally to the hdimension expressioni. For example
\dimUse hdimensioni
Recovers the content of a hdimensioni and returns the value. An error is raised if the variable does not
exist or if it is invalid.
Writes the result of evaluating the hdimension expressioni in the log file. For example
\dimLog {\lFooSomeDim+1cm}
\dimVarLog hdimensioni
Writes the value of the hdimensioni in the log file. For example
\dimVarLog \lFooSomeDim
Displays the result of evaluating the hdimension expressioni on the terminal. For example
\dimShow {\lFooSomeDim+1cm}
\dimVarShow hdimensioni
\dimVarShow \lFooSomeDim
Sets hdimensioni to the value of hdimension expressioni, which must evaluate to a length with units.
\dimZero hdimensioni
\dimZeroNew hdimensioni
Ensures that the hdimensioni exists globally by applying \dimNew if necessary, then applies \dimZero to
set the hdimensioni to zero. For example
\dimZeroNew \lFooSomeDim
0.0pt
\dimUse \lFooSomeDim
Adds the result of the hdimension expressioni to the current content of the hdimensioni. For example
Subtracts the result of the hdimension expressioni from the current content of the hdimensioni. For
example
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be dimension
expressions. Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using hstepi between
each hvaluei), the hcodei is inserted into the input stream with #1 replaced by the current hvaluei. Thus
the hcodei should define a function of one argument (#1).
CHAPTER 9. DIMENSIONS (DIM) 60
\IgnoreSpacesOn
\tlClear \lTmpaTl
\dimStepInline {1pt} {0.1pt} {1.5pt} {
\tlPutRight \lTmpaTl {[#1]} [1.0pt][1.1pt][1.20001pt][1.30002pt][1.40002pt]
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
This function first evaluates the hinitial valuei, hstepi and hfinal valuei, all of which should be dimension
expressions. Then for each hvaluei from the hinitial valuei to the hfinal valuei in turn (using hstepi between
each hvaluei), the hcodei is inserted into the input stream, with the htl vari defined as the current hvaluei.
Thus the hcodei should make use of the htl vari.
\dimIfExist hdimensioni
\dimIfExistT hdimensioni {htrue codei}
\dimIfExistF hdimensioni {hfalse codei}
\dimIfExistTF hdimensioni {htrue codei} {hfalse codei}
Tests whether the hdimensioni is currently defined. This does not check that the hdimensioni really is a
dimension variable. For example
This function first evaluates each of the hdimension expressionsi as described for \dimEval. The two
results are then compared using the hrelationi:
Equal =
Greater than >
Less than <
For example
Greater Less
CHAPTER 9. DIMENSIONS (DIM) 61
This function evaluates the htest dimension expressioni and compares this in turn to each of the hdimen-
sion expression casesi. If the two are equal then the associated hcodei is left in the input stream and
other cases are discarded.
This function evaluates the htest dimension expressioni and compares this in turn to each of the hdimen-
sion expression casesi. If the two are equal then the associated hcodei is left in the input stream and
other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input
stream (after the code for the appropriate case).
This function evaluates the htest dimension expressioni and compares this in turn to each of the hdimen-
sion expression casesi. If the two are equal then the associated hcodei is left in the input stream and
other cases are discarded. If none of the cases match then the hfalse codei is inserted. For example
\IgnoreSpacesOn
\dimSet \lTmpaDim {5pt}
\dimCaseF {2\lTmpaDim} {
{5pt} {\prgReturn{Small}}
{4pt+6pt} {\prgReturn{Medium}}
{-10pt} {\prgReturn{Negative}} Medium
}{
\prgReturn {No Match}
}
\IgnoreSpacesOff
CHAPTER 9. DIMENSIONS (DIM) 62
This function evaluates the htest dimension expressioni and compares this in turn to each of the hdimen-
sion expression casesi. If the two are equal then the associated hcodei is left in the input stream and
other cases are discarded. If any of the cases are matched, the htrue codei is also inserted into the input
stream (after the code for the appropriate case), while if none match then the hfalse codei is inserted.
Chapter 10
\cEmptyClist
Scratch comma lists for local assignment. These are never used by the 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.
Scratch comma lists for global assignment. These are never used by the 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.
Creates a new hcomma listi or raises an error if the name is already taken. The declaration is global.
The hcomma listi initially contains no items.
\clistNew \lFooSomeClist
Creates a new constant hclist vari or raises an error if the name is already taken. The value of the hclist
vari is set globally to the hcomma listi.
Returns the contents of the hclist vari, with the hseparatori between the items.
63
CHAPTER 10. COMMA SEPARATED LISTS (CLIST) 64
\clistVarJoinExtended hclist vari {hseparator between twoi} {hseparator between more than twoi}
{hseparator between final twoi}
Returns the contents of the hclist vari, with the appropriate hseparatori between the items. Namely, if
the comma list has more than two items, the hseparator between more than twoi is placed between each
pair of items except the last, for which the hseparator between final twoi is used. If the comma list has
exactly two items, then they are joined with the hseparator between twoi and returns.
\clistSet \lTmpaClist { a , b }
\clistVarJoinExtended \lTmpaClist { and } {, } {, and } a and b
Returns the contents of the hcomma listi, with the appropriate hseparatori between the items. As for
\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 hseparatorsi are then inserted in the same
way as for \clistVarJoin and \clistVarJoinExtended, respectively.
\clistLog {htokensi}
Writes the entries in the comma list in the log file. See also \clistShow which displays the result in the
terminal.
\clistLog {one,two,three}
Writes the entries in the hcomma listi in the log file. See also \clistVarShow which displays the result
in the terminal.
\clistShow {htokensi}
\clistShow {one,two,three}
Sets hcomma listi to contain the hitemsi, 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 htokensi as a single hitemi even if the htokensi
contain commas or spaces, add a set of braces: \clistSet hcomma listi { {htokensi} }.
Sets the content of hcomma list1 i equal to that of hcomma list2 i. To set a token list variable equal to a
comma list variable, use \tlSetEq. Conversely, setting a comma list variable to a token list is unadvisable
unless one checks space-trimming and related issues.
Converts the data in the hsequencei into a hcomma listi: the original hsequencei is unchanged. Items
which contain either spaces or commas are surrounded by braces.
Ensures that the hcomma listi exists globally by applying \clistNew if necessary, then applies \clistClear
to leave the list empty.
\clistClearNew \lFooSomeClist
\clistSet \lFooSomeClist {one,two,three} one and two and three
\clistVarJoin \lFooSomeClist { and }
Concatenates the content of hcomma list2 i and hcomma list3 i together and saves the result in hcomma
list1 i. The items in hcomma list2 i are placed at the left side of the new comma list.
Appends the hitemsi to the left of the hcomma listi. 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 htokensi as a single hitemi even if the htokensi contain commas or spaces, add a set of
braces: \clistPutLeft hcomma listi { {htokensi} }.
Appends the hitemsi to the right of the hcomma listi. 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 htokensi as a single hitemi even if the htokensi contain commas or spaces, add a set of
braces: \clistPutRight hcomma listi { {htokensi} }.
Removes duplicate items from the hcomma listi, leaving the left most copy of each item in the hcomma
listi. The hitemi comparison takes place on a token basis, as for \tlIfEqTF.
CHAPTER 10. COMMA SEPARATED LISTS (CLIST) 67
Removes every occurrence of hitemi from the hcomma listi. The hitemi comparison takes place on a token
basis, as for \tlIfEqTF.
Returns the number of items in the hcomma listi as an hinteger denotationi. The total number of items
in a hcomma listi includes those which are duplicates, i.e. every item in a hcomma listi is counted.
Indexing items in the hcomma listi from 1 at the top (left), this function evaluates the hinteger expressioni
and returns the appropriate item from the comma list. If the hinteger expressioni is negative, indexing
occurs from the bottom (right) of the comma list. When the hinteger expressioni is larger than the
number of items in the hcomma listi (as calculated by \clistCount) then the function returns nothing.
Indexing items in the hcomma listi from 1 at the top (left), this function evaluates the hinteger expressioni
and returns the appropriate item from the comma list. If the hinteger expressioni is negative, indexing
occurs from the bottom (right) of the comma list. When the hinteger expressioni is larger than the number
of items in the hcomma listi (as calculated by \clistVarCount) then the function returns nothing.
CHAPTER 10. COMMA SEPARATED LISTS (CLIST) 68
Selects a pseudo-random item of the hcomma listi. If the hcomma listi has no item, the result is empty.
Stores the left-most item from the hcomma listi in the htoken list variablei without removing it from the
hcomma listi. The htoken list variablei is assigned locally. If the hcomma listi is empty the htoken list
variablei is set to the marker value \qNoValue.
If the hcomma listi is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hcomma listi is non-empty,
stores the left-most item from the hcomma listi in the htoken list variablei without removing it from the
hcomma listi. The htoken list variablei is assigned locally.
Pops the left-most item from a hcomma listi into the htoken list variablei, i.e. removes the item from the
comma list and stores it in the htoken list variablei. The assignment of the htoken list variablei is local.
If the hcomma listi is empty the htoken list variablei is set to the marker value \qNoValue.
If the hcomma listi is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hcomma listi is non-empty, pops
the top item from the hcomma listi in the htoken list variablei, i.e. removes the item from the hcomma
listi. The htoken list variablei is assigned locally.
Adds the {hitemsi} to the top of the hcomma listi. Spaces are removed from both sides of each item as
for any n-type comma list.
Applies hinline functioni to every hitemi stored within the hcomma listi. The hinline functioni should
consist of code which receives the hitemi as #1. The hitemsi are returned from left to right.
\IgnoreSpacesOn
\tlClear \lTmpaTl
\clistMapInline {one,two,three} {
\tlPutRight \lTmpaTl {(#1)} (one)(two)(three)
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
Stores each hitemi of the hcomma listi in turn in the (token list) hvariablei and applies the hcodei. The
hcodei will usually make use of the hvariablei, but this is not enforced. The assignments to the hvariablei
are local. Its value after the loop is the last hitemi in the hcomma listi, or its original value if there were
no hitemi. The hitemsi are returned from left to right.
CHAPTER 10. COMMA SEPARATED LISTS (CLIST) 70
\IgnoreSpacesOn
\clistMapVariable {one,two,three} \lTmpiTl {
\tlPutRight \gTmpaTl {\expWhole {(\lTmpiTl)}}
(one)(two)(three)
}
\tlUse \gTmpaTl
\IgnoreSpacesOff
Tests whether the hcomma listi is currently defined. This does not check that the hcomma listi really is
a comma list.
Tests if the hcomma listi is empty (containing no items). The rules for space trimming are as for other
n-type comma-list functions, hence the comma list { , ,, } (without outer braces) is empty, while
{ ,{},} (without outer braces) contains one element, which happens to be empty: the comma-list is not
empty.
NonEmpty Empty
NonEmpty Empty
CHAPTER 10. COMMA SEPARATED LISTS (CLIST) 71
Tests if the hitemi is present in the hcomma listi. In the case of an n-type hcomma listi, the usual rules
of space trimming and brace stripping apply. For example
Tests if the hitemi is present in the hcomma listi. In the case of an n-type hcomma listi, the usual rules
of space trimming and brace stripping apply.
\cEmptySeq
Scratch sequences for local assignment. These are never used by the 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.
Scratch sequences for global assignment. These are never used by the 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.
\seqNew hsequencei
Creates a new hsequencei or raises an error if the name is already taken. The declaration is global. The
hsequencei initially contains no items.
\seqNew \lFooSomeSeq
Creates a new constant hseq vari or raises an error if the name is already taken. The hseq vari is set
globally to contain the items in the hcomma listi.
Returns the contents of the hseq vari, with the hseparatori between the items. If the sequence has a single
72
CHAPTER 11. SEQUENCES AND STACKS (SEQ) 73
item, it is returned with no hseparatori, and an empty sequence returns nothing. An error is raised if the
variable does not exist or if it is invalid.
\seqVarJoinExtended hseq vari {hseparator between twoi} {hseparator between more than twoi}
{hseparator between final twoi}
Returns the contents of the hseq vari, with the appropriate hseparatori between the items. Namely, if the
sequence has more than two items, the hseparator between more than twoi is placed between each pair
of items except the last, for which the hseparator between final twoi is used. If the sequence has exactly
two items, then they are joined with the hseparator between twoi and returned. If the sequence has a
single item, it is returned, and an empty sequence returns nothing. An error is raised if the variable does
not exist or if it is invalid.
The first separator argument is not used in this case because the sequence has more than 2 items.
\seqVarLog hsequencei
\seqVarLog \lFooSomeSeq
\seqVarShow hsequencei
\seqVarShow \lFooSomeSeq
Converts the data in the hcomma listi into a hsequencei: the original hcomma listi is unchanged.
Splits the htoken listi into hitemsi separated by hdelimiteri, and assigns the result to the hsequencei.
Spaces on both sides of each hitemi are ignored, then one set of outer braces is removed (if any); this space
trimming behaviour is identical to that of Clist functions. Empty hitemsi are preserved by \seqSetSplit,
CHAPTER 11. SEQUENCES AND STACKS (SEQ) 74
and can be removed afterwards using \seqVarRemoveAll hsequencei {}. The hdelimiteri may not contain
{, } or # (assuming TEX’s normal category code régime). If the hdelimiteri is empty, the htoken listi is
split into hitemsi as a htoken listi.
\seqClear hsequencei
\seqClear \lTmpaSeq
\seqClearNew hsequencei
Ensures that the hsequencei exists globally by applying \seqNew if necessary, then applies \seqClear to
leave the hsequencei empty.
\seqClearNew \lFooSomeSeq
\seqSetFromClist \lFooSomeSeq {one,two,three} one and two and three
\seqVarJoin \lFooSomeSeq { and }
Concatenates the content of hsequence2 i and hsequence3 i together and saves the result in hsequence1 i.
The items in hsequence2 i are placed at the left side of the new sequence.
\seqVarRemoveDuplicates hsequencei
Removes duplicate items from the hsequencei, leaving the left most copy of each item in the hsequencei.
The hitemi comparison takes place on a token basis, as for \tlIfEqTF.
Removes every occurrence of hitemi from the hsequencei. The hitemi comparison takes place on a token
basis, as for \tlIfEqTF.
\seqVarReverse hsequencei
\seqVarCount hsequencei
Returns the number of items in the hsequencei as an hinteger denotationi. The total number of items in
a hsequencei includes those which are empty and duplicates, i.e. every item in a hsequencei is unique.
Indexing items in the hsequencei from 1 at the top (left), this function evaluates the hinteger expressioni
and returns the appropriate item from the sequence. If the hinteger expressioni is negative, indexing
CHAPTER 11. SEQUENCES AND STACKS (SEQ) 76
occurs from the bottom (right) of the sequence. If the hinteger expressioni is larger than the number of
items in the hsequencei (as calculated by \seqVarCount) then the function returns nothing.
Selects a pseudo-random item of the hsequencei. If the hsequencei is empty the result is empty.
Reads the top item from a hsequencei into the htoken list variablei without removing it from the
hsequencei. The htoken list variablei is assigned locally. If hsequencei is empty the htoken list variablei
is set to the special marker \qNoValue.
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty, stores
the top item from a hsequencei in the htoken list variablei without removing it from the hsequencei. The
htoken list variablei is assigned locally.
Pops the top item from a hsequencei into the htoken list variablei. the htoken list variablei is assigned
locally. If hsequencei is empty the htoken list variablei is set to the special marker \qNoValue.
CHAPTER 11. SEQUENCES AND STACKS (SEQ) 77
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty, pops
the top item from the hsequencei in the htoken list variablei, i.e. removes the item from the hsequencei.
The htoken list variablei is assigned locally.
You can only push one item to the hsequencei with \seqPush, which is different from \ClistPush.
Stores the left-most item from a hsequencei in the htoken list variablei without removing it from the
hsequencei. The htoken list variablei is assigned locally. If hsequencei is empty the htoken list variablei
is set to the special marker \qNoValue.
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty,
stores the left-most item from the hsequencei in the htoken list variablei without removing it from the
hsequencei, then leaves the htrue codei in the input stream. The htoken list variablei is assigned locally.
Stores the right-most item from a hsequencei in the htoken list variablei without removing it from the
hsequencei. The htoken list variablei is assigned locally. If hsequencei is empty the htoken list variablei
is set to the special marker \qNoValue.
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty,
stores the right-most item from the hsequencei in the htoken list variablei without removing it from the
hsequencei, then leaves the htrue codei in the input stream. The htoken list variablei is assigned locally.
Pops the left-most item from a hsequencei into the htoken list variablei, i.e. removes the item from the
sequence and stores it in the htoken list variablei. The assignment of the htoken list variablei is local. If
hsequencei is empty the htoken list variablei is set to the special marker \qNoValue.
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty,
pops the left-most item from the hsequencei in the htoken list variablei, i.e. removes the item from the
hsequencei, then leaves the htrue codei in the input stream. The htoken list variablei is assigned locally.
Pops the right-most item from a hsequencei into the htoken list variablei, i.e. removes the item from the
sequence and stores it in the htoken list variablei. The assignment of the htoken list variablei is local. If
hsequencei is empty the htoken list variablei is set to the special marker \qNoValue.
If the hsequencei is empty, leaves the hfalse codei in the input stream. The value of the htoken list
variablei is not defined in this case and should not be relied upon. If the hsequencei is non-empty,
pops the right-most item from the hsequencei in the htoken list variablei, i.e. removes the item from the
hsequencei, then leaves the htrue codei in the input stream. The htoken list variablei is assigned locally.
Applies hinline functioni to every hitemi stored within the hsequencei. The hinline functioni should consist
of code which will receive the hitemi as #1. The hitemsi are returned from left to right.
\IgnoreSpacesOn
\seqSetFromClist \lTmpkSeq {one,two,three}
\tlClear \lTmpaTl
\seqVarMapInline \lTmpkSeq {
(one)(two)(three)
\tlPutRight \lTmpaTl {(#1)}
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
Stores each hitemi of the hsequencei in turn in the (token list) hvariablei and applies the hcodei. The
hcodei will usually make use of the hvariablei, but this is not enforced. The assignments to the hvariablei
are local. Its value after the loop is the last hitemi in the hsequencei, or its original value if the hsequencei
is empty. The hitemsi are returned from left to right.
\IgnoreSpacesOn
\intZero \lTmpaInt
\seqSetFromClist \lTmpaSeq {1,3,7}
\seqVarMapVariable \lTmpaSeq \lTmpiTl {
59
\intAdd \lTmpaInt {\lTmpiTl*\lTmpiTl}
}
\intUse \lTmpaInt
\IgnoreSpacesOff
\seqIfExist hsequencei
\seqIfExistT hsequencei {htrue codei}
\seqIfExistF hsequencei {hfalse codei}
\seqIfExistTF hsequencei {htrue codei} {hfalse codei}
Tests whether the hsequencei is currently defined. This does not check that the hsequencei really is a
sequence variable.
CHAPTER 11. SEQUENCES AND STACKS (SEQ) 80
\seqVarIfEmpty hsequencei
\seqVarIfEmptyT hsequencei {htrue codei}
\seqVarIfEmptyF hsequencei {hfalse codei}
\seqVarIfEmptyTF hsequencei {htrue codei} {hfalse codei}
NonEmpty Empty
LATEX3 implements a “property list” data type, which contain an unordered list of entries each of which
consists of a hkeyi and an associated hvaluei. The hkeyi and hvaluei may both be any hbalanced texti,
the hkeyi is processed using \tlToStr, meaning that category codes are ignored. It is possible to map
functions to property lists such that the function is applied to every key–value pair within the list.
Each entry in a property list must have a unique hkeyi: if an entry is added to a property list which
already contains the hkeyi then the new entry overwrites the existing one. The hkeysi are compared on
a string basis, using the same method as \strIfEq.
\cEmptyProp
Scratch property lists for local assignment. These are never used by the 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.
Scratch property lists for global assignment. These are never used by the 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.
Creates a new hproperty listi or raises an error if the name is already taken. The declaration is global.
The hproperty listi initially contains no entries.
\propNew \lFooSomeProp
81
CHAPTER 12. PROPERTY LISTS (PROP) 82
Creates a new constant hprop vari or raises an error if the name is already taken. The hprop vari is
set globally to contain key–value pairs given in the second argument, processed in the way described for
\propSetFromKeyval. If duplicate keys appear only the last of the values is kept. This function correctly
detects the = and , signs provided they have the standard category code 12 or they are active.
Returns the hproperty listi in a key–value notation. Keep in mind that a hproperty listi is unordered,
while key–value interfaces don’t necessarily are, so this can’t be used for arbitrary interfaces.
\propToKeyval \lTmpaProp
\propVarLog \lTmpaProp
\propVarShow \lTmpaProp
Sets hprop vari to contain key–value pairs given in the second argument. If duplicate keys appear only
the last of the values is kept.
Spaces are trimmed around every hkeyi and every hvaluei, and if the result of trimming spaces consists of
a single brace group then a set of outer braces is removed. This enables both the hkeyi and the hvaluei to
contain spaces, commas or equal signs. The hkeyi is then processed by \tlToStr. This function correctly
detects the = and , signs provided they have the standard category code 12 or they are active.
\propClear \lTmpaProp
Ensures that the hproperty listi exists globally by applying \propNew if necessary, then applies \propClear
to leave the list empty.
\propClearNew \lFooSomeProp
Combines the key–value pairs of hprop var2 i and hprop var3 i, and saves the result in hprop var1 i. If a key
appears in both hprop var2 i and hprop var3 i then the last value, namely the value in hprop var3 i is kept.
Adds an entry to the hproperty listi which may be accessed using the hkeyi and which has hvaluei. If
the hkeyi is already present in the hproperty listi, the existing entry is overwritten by the new hvaluei.
Both the hkeyi and hvaluei may contain any hbalanced texti. The hkeyi is stored after processing with
\tlToStr, meaning that category codes are ignored.
If the hkeyi is present in the hproperty listi then no action is taken. Otherwise, a new entry is added as
described for \propPut.
Updates the hprop vari by adding entries for each key–value pair given in the second argument. The
addition is done through \propPut, hence if the hprop vari already contains some of the keys, the
corresponding values are discarded and replaced by those given in the key–value list. If duplicate keys
appear in the key–value list then only the last of the values is kept.
Removes the entry listed under hkeyi from the hproperty listi. If the hkeyi is not found in the hproperty
listi no change occurs, i.e there is no need to test for the existence of a key before deleting it.
Returns the number of key–value pairs in the hproperty listi as an hinteger denotationi.
Returns the hvaluei corresponding to the hkeyi in the hproperty listi. If the hkeyi is missing, nothing is
returned.
Recovers the hvaluei stored with hkeyi from the hproperty listi, and places this in the htoken list variablei.
If the hkeyi is not found in the hproperty listi then the htoken list variablei is set to the special marker
\qNoValue. The assignment of the htoken list variablei is local.
If the hkeyi is not present in the hproperty listi, leaves the hfalse codei in the input stream. The value of
the htoken list variablei is not defined in this case and should not be relied upon. If the hkeyi is present in
the hproperty listi, stores the corresponding hvaluei in the htoken list variablei without removing it from
the hproperty listi, then leaves the htrue codei in the input stream. The htoken list variablei is assigned
locally.
Recovers the hvaluei stored with hkeyi from the hproperty listi, and places this in the htoken list variablei.
If the hkeyi is not found in the hproperty listi then the htoken list variablei is set to the special marker
\qNoValue. The hkeyi and hvaluei are then deleted from the property list. The assignment of the htoken
list variablei is local.
If the hkeyi is not present in the hproperty listi, leaves the hfalse codei in the input stream. The value of
the htoken list variablei is not defined in this case and should not be relied upon. If the hkeyi is present
in the hproperty listi, pops the corresponding hvaluei in the htoken list variablei, i.e. removes the item
from The htoken list variablei is assigned locally.
Applies hinline functioni to every hentryi stored within the hproperty listi. The hinline functioni should
consist of code which receives the hkeyi as #1 and the hvaluei as #2. The order in which hentriesi are
returned is not defined and should not be relied upon.
CHAPTER 12. PROPERTY LISTS (PROP) 86
\IgnoreSpacesOn
\propSetFromKeyval \lTmpkProp {key1=one,key2=two,key3=three}
\tlClear \lTmpaTl
\propVarMapInline \lTmpkProp {
\tlPutRight \lTmpaTl {(#1=#2)}
}
\tlUse \lTmpaTl
\IgnoreSpacesOff
(key1=one)(key2=two)(key3=three)
Tests whether the hproperty listi is currently defined. This does not check that the hproperty listi really
is a property list variable.
NonEmpty Empty
Tests if the hkeyi is present in the hproperty listi, making the comparison using the method described by
\strIfEqTF.
This module provides regular expression testing, extraction of submatches, splitting, and replacement, all
acting on token lists. The syntax of regular expressions is mostly a subset of the pcre syntax (and very
close to posix), with some additions due to the fact that TEX manipulates tokens rather than characters.
For performance reasons, only a limited set of features are implemented. Notably, back-references are
not supported.
Let us give a few examples. The following example replace the first occurrence of “at” with “is” in the
token list variable \lTmpaTl.
A more complicated example is a pattern to emphasize each word and add a comma after it:
The \w sequence represents any “word” character, and + indicates that the \w sequence should be re-
peated as many times as possible (at least once), hence matching a word in the input token list. In the
replacement text, \0 denotes the full match (here, a word). The command \underline is inserted using
\c{underline}, and its argument \0 is put between braces \cB\{ and \cE\}.
If a regular expression is to be used several times, it can be compiled once, and stored in a regex variable
using \regexSet. For example,
\regexNew \lFooRegex
\regexSet \lFooRegex {\c{begin} \cB. (\c[^BE].*) \cE.}
stores in \lFooRegex a regular expression which matches the starting marker for an environment: \begin,
followed by a begin-group token (\cB.), then any number of tokens which are neither begin-group nor
end-group character tokens (\c[^BE].*), ending with an end-group token (\cE.). As explained later,
the parentheses “capture” the result of \c[^BE].*, giving us access to the name of the environment when
doing replacements.
87
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 88
Scratch regex variables for local assignment. These are never used by function package, and so are safe
for use with any function. However, they may be overwritten by other non-kernel code and so should
only be used for short-term storage.
Scratch regex variables for global assignment. These are never used by function package, and so are
safe for use with any function. However, they may be overwritten by other non-kernel code and so should
only be used for short-term storage.
Creates a new hregex vari or raises an error if the name is already taken. The declaration is global. The
hregex vari is initially such that it never matches.
Stores a compiled version of the hregular expressioni in the hregex vari. For instance, this function can
be used as
\regexNew \lMyRegex
\regexSet \lMyRegex {my\ (simple\ )? reg(ex|ular\ expression)}
Creates a new constant hregex vari or raises an error if the name is already taken. The value of the hregex
vari is set globally to the compiled version of the hregular expressioni.
\regexLog {hregexi}
\regexVarLog hregex vari
\regexShow {hregexi}
\regexVarShow hregex vari
Displays in the terminal or writes in the log file (respectively) how l3regex interprets the hregexi. For
instance, \regexShow {\A X|Y} shows
+-branch
anchor at start (\A)
char code 88 (X)
+-branch
char code 89 (Y)
indicating that the anchor \A only applies to the first branch: the second branch is not anchored to the
beginning of the match.
Tests whether the hregular expressioni matches any part of the htoken listi. For instance,
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 89
Tests whether the hregex vari matches any part of the htoken listi.
Sets hint vari within the current TEX group level equal to the number of times hregular expressioni
appears in htoken listi. The search starts by finding the left-most longest match, respecting greedy and
lazy (non-greedy) operators. Then the search starts again from the character following the last character
of the previous match, until reaching the end of the token list. Infinite loops are prevented in the case
where the regular expression can match an empty token list: then we count one match between each pair
of characters. For instance,
\intNew \lFooInt
\regexCount {(b+|c)} {abbababcbb} \lFooInt 5
\intUse \lFooInt
\regexMatchCase
{
{hregex1 i} {hcode case1 i}
{hregex2 i} {hcode case2 i}
…
{hregexn i} {hcode casen i}
} {htoken listi}
Determines which of the hregular expressionsi matches at the earliest point in the htoken listi, and leaves
the corresponding hcodei i. If several hregexi match starting at the same point, then the first one in the
list is selected and the others are discarded. Each hregexi can either be given as a regex variable or as
an explicit regular expression.
In detail, for each starting position in the htoken listi, each of the hregexi is searched in turn. If one of
them matches then the corresponding hcodei is used and everything else is discarded, while if none of
the hregexi match at a given position then the next starting position is attempted. If none of the hregexi
match anywhere in the htoken listi then nothing is left in the input stream. Note that this differs from
nested \regexMatch statements since all hregexi are attempted at each position rather than attempting
to match hregex1 i at every position before moving on to hregex2 i.
\regexMatchCaseT
{
{hregex1 i} {hcode case1 i}
{hregex2 i} {hcode case2 i}
…
{hregexn i} {hcode casen i}
} {htoken listi}
{htrue codei}
Determines which of the hregular expressionsi matches at the earliest point in the htoken listi, and leaves
the corresponding hcodei i followed by the htrue codei in the input stream. If several hregexi match
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 90
starting at the same point, then the first one in the list is selected and the others are discarded. Each
hregexi can either be given as a regex variable or as an explicit regular expression.
\regexMatchCaseF
{
{hregex1 i} {hcode case1 i}
{hregex2 i} {hcode case2 i}
…
{hregexn i} {hcode casen i}
} {htoken listi}
{hfalse codei}
Determines which of the hregular expressionsi matches at the earliest point in the htoken listi, and leaves
the corresponding hcodei i. If several hregexi match starting at the same point, then the first one in the
list is selected and the others are discarded. If none of the hregexi match, the hfalse codei is left in the
input stream. Each hregexi can either be given as a regex variable or as an explicit regular expression.
\regexMatchCaseTF
{
{hregex1 i} {hcode case1 i}
{hregex2 i} {hcode case2 i}
…
{hregexn i} {hcode casen i}
} {htoken listi}
{htrue codei} {hfalse codei}
Determines which of the hregular expressionsi matches at the earliest point in the htoken listi, and leaves
the corresponding hcodei i followed by the htrue codei in the input stream. If several hregexi match
starting at the same point, then the first one in the list is selected and the others are discarded. If none
of the hregexi match, the hfalse codei is left in the input stream. Each hregexi can either be given as a
regex variable or as an explicit regular expression.
Finds the first match of the hregular expressioni in the htoken listi. If it exists, the match is stored as the
first item of the hseq vari, and further items are the contents of capturing groups, in the order of their
opening parenthesis. The hseq vari is assigned locally. If there is no match, the hseq vari is cleared. The
testing versions insert the htrue codei into the input stream if a match was found, and the hfalse codei
otherwise.
For instance, assume that you type
Then the regular expression (anchored at the start with \A and at the end with \Z) must match the whole
token list. The first capturing group, (La)?, matches La, and the second capturing group, (!*), matches
!!!. Thus, \lTmpaSeq contains as a result the items {LaTeX!!!}, {La}, and {!!!}. Note that the n-th
item of \lTmpaSeq, as obtained using \seqVarItem, correspond to the submatch numbered (n − 1) in
functions such as \regexReplaceOnce.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 91
Finds the first match of the hregex vari in the htoken listi. If it exists, the match is stored as the first
item of the hseq vari, and further items are the contents of capturing groups, in the order of their opening
parenthesis. The hseq vari is assigned locally. If there is no match, the hseq vari is cleared. The testing
versions insert the htrue codei into the input stream if a match was found, and the hfalse codei otherwise.
Finds all matches of the hregular expressioni in the htoken listi, and stores all the submatch information
in a single sequence (concatenating the results of multiple \regexExtractOnce calls). The hseq vari is
assigned locally. If there is no match, the hseq vari is cleared. The testing versions insert the htrue codei
into the input stream if a match was found, and the hfalse codei otherwise. For instance, assume that
you type
Then the regular expression matches twice, the resulting sequence contains the two items {Hello} and
{world}.
Finds all matches of the hregex vari in the htoken listi, and stores all the submatch information in a single
sequence (concatenating the results of multiple \regexVarExtractOnce calls). The hseq vari is assigned
locally. If there is no match, the hseq vari is cleared. The testing versions insert the htrue codei into the
input stream if a match was found, and the hfalse codei otherwise.
Splits the htoken listi into a sequence of parts, delimited by matches of the hregular expressioni. If the
hregular expressioni has capturing groups, then the token lists that they match are stored as items of
the sequence as well. The assignment to hseq vari is local. If no match is found the resulting hseq vari
has the htoken listi as its sole item. If the hregular expressioni matches the empty token list, then the
htoken listi is split into single tokens. The testing versions insert the htrue codei into the input stream if
a match was found, and the hfalse codei otherwise. For example, after
\seqNew \lPathSeq
\regexSplit {/} {the/path/for/this/file.tex} \lPathSeq
the sequence \lPathSeq contains the items {the}, {path}, {for}, {this}, and {file.tex}.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 92
Splits the htoken listi into a sequence of parts, delimited by matches of the hregular expressioni. If the
hregex vari has capturing groups, then the token lists that they match are stored as items of the sequence
as well. The assignment to hseq vari is local. If no match is found the resulting hseq vari has the htoken
listi as its sole item. If the hregular expressioni matches the empty token list, then the htoken listi is
split into single tokens. The testing versions insert the htrue codei into the input stream if a match was
found, and the hfalse codei otherwise.
Searches for the hregular expressioni in the contents of the htl vari and replaces the first match with the
hreplacementi. In the hreplacementi, \0 represents the full match, \1 represent the contents of the first
capturing group, \2 of the second, etc. The result is assigned locally to htl vari.
Searches for the hregex vari in the contents of the htl vari and replaces the first match with the
hreplacementi. In the hreplacementi, \0 represents the full match, \1 represent the contents of the
first capturing group, \2 of the second, etc. The result is assigned locally to htl vari.
Replaces all occurrences of the hregex vari in the contents of the htl vari by the hreplacementi, where
\0 represents the full match, \1 represent the contents of the first capturing group, \2 of the second,
etc. Every match is treated independently, and matches cannot overlap. The result is assigned locally to
htl vari.
Replaces all occurrences of the hregular expressioni in the contents of the htl vari by the hreplacementi,
where \0 represents the full match, \1 represent the contents of the first capturing group, \2 of the
second, etc. Every match is treated independently, and matches cannot overlap. The result is assigned
locally to htl vari.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 93
\regexReplaceCaseOnce
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
Replaces the earliest match of the regular expression (?|hregex1 i|…|hregexn i) in the htoken list variablei
by the hreplacementi corresponding to which hregexi i matched. If none of the hregexi match, then the
htl vari is not modified. Each hregexi can either be given as a regex variable or as an explicit regular
expression.
In detail, for each starting position in the htoken listi, each of the hregexi is searched in turn. If one of them
matches then it is replaced by the corresponding hreplacementi as described for \regexReplaceOnce. This
is equivalent to checking with \regexMatchCase which hregexi matches, then performing the replacement
with \regexReplaceOnce.
\regexReplaceCaseOnceT
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{htrue codei}
Replaces the earliest match of the regular expression (?|hregex1 i|…|hregexn i) in the htoken list variablei
by the hreplacementi corresponding to which hregexi i matched, then leaves the htrue codei in the input
stream. If none of the hregexi match, then the htl vari is not modified. Each hregexi can either be given
as a regex variable or as an explicit regular expression.
\regexReplaceCaseOnceF
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{hfalse codei}
Replaces the earliest match of the regular expression (?|hregex1 i|…|hregexn i) in the htoken list variablei
by the hreplacementi corresponding to which hregexi i matched. If none of the hregexi match, then the
htl vari is not modified, and the hfalse codei is left in the input stream. Each hregexi can either be given
as a regex variable or as an explicit regular expression.
\regexReplaceCaseOnceTF
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{htrue codei} {hfalse codei}
Replaces the earliest match of the regular expression (?|hregex1 i|…|hregexn i) in the htoken list variablei
by the hreplacementi corresponding to which hregexi i matched, then leaves the htrue codei in the input
stream. If none of the hregexi match, then the htl vari is not modified, and the hfalse codei is left in the
input stream. Each hregexi can either be given as a regex variable or as an explicit regular expression.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 94
\regexReplaceCaseAll
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
Replaces all occurrences of all hregexi in the htoken listi by the corresponding hreplacementi. Every
match is treated independently, and matches cannot overlap. The result is assigned locally to htl vari.
In detail, for each starting position in the htoken listi, each of the hregexi is searched in turn. If one
of them matches then it is replaced by the corresponding hreplacementi, and the search resumes at the
position that follows this match (and replacement). For instance
results in \lTmpaTl having the contents ``Hello''---[,][ ]``world''---[!]. Note in particular that
the word-boundary assertion \b did not match at the start of words because the case [A-Za-z]+ matched
at these positions. To change this, one could simply swap the order of the two cases in the argument of
\regexReplaceCaseAll.
\regexReplaceCaseAllT
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{htrue codei}
Replaces all occurrences of all hregexi in the htoken listi by the corresponding hreplacementi. Every
match is treated independently, and matches cannot overlap. The result is assigned locally to htl vari,
and the htrue codei is left in the input stream if any replacement was made.
\regexReplaceCaseAllF
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{hfalse codei}
Replaces all occurrences of all hregexi in the htoken listi by the corresponding hreplacementi. Every
match is treated independently, and matches cannot overlap. The result is assigned locally to htl vari,
and the hfalse codei is left in the input stream if not any replacement was made.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 95
\regexReplaceCaseAllTF
{
{hregex1 i} {hreplacement1 i}
{hregex2 i} {hreplacement2 i}
…
{hregexn i} {hreplacementn i}
} htl vari
{htrue codei} {hfalse codei}
Replaces all occurrences of all hregexi in the htoken listi by the corresponding hreplacementi. Every
match is treated independently, and matches cannot overlap. The result is assigned locally to htl vari,
and the htrue codei or hfalse codei is left in the input stream depending on whether any replacement was
made or not.
• Cat matches the word “Cat” capitalized in this way, but also matches the beginning of the word
“Cattle”: use \bCat\b to match a complete word only.
• [abc] matches one letter among “a”, “b”, “c”; the pattern (a|b|c) matches the same three possible
letters (but see the discussion of submatches below).
• [A-Za-z]* matches any number (due to the quantifier *) of Latin letters (not accented).
• \c{[A-Za-z]*} matches a control sequence made of Latin letters.
• \_[^\_]*\_ matches an underscore, any number of characters other than underscore, and another
underscore; it is equivalent to \_.*?\_ where . matches arbitrary characters and the lazy quantifier
*? means to match as few characters as possible, thus avoiding matching underscores.
• [\+\-]?\d+ matches an explicit integer with at most one sign.
• [\+\-\ ]*\d+\ * matches an explicit integer with any number of + and − signs, with spaces
allowed except within the mantissa, and surrounded by spaces.
• [\+\-\ ]*(\d+|\d*\.\d+)\ * matches an explicit integer or decimal number; using [.,] instead
of \. would allow the comma as a decimal marker.
• [\+\-\ ]*(\d+|\d*\.\d+)\ *((?i)pt|in|[cem]m|ex|[bs]p|[dn]d|[pcn]c)\ * matches an ex-
plicit dimension with any unit that TEX knows, where (?i) means to treat lowercase and uppercase
letters identically.
• [\+\-\ ]*((?i)nan|inf|(\d+|\d*\.\d+)(\ *e[\+\-\ ]*\d+)?)\ * matches an explicit floating
point number or the special values nan and inf (with signs and spaces allowed).
• [\+\-\ ]*(\d+|\cC.)\ * matches an explicit integer or control sequence (without checking whether
it is an integer variable).
• \G.*?\K at the beginning of a regular expression matches and discards (due to \K) everything be-
tween the end of the previous match (\G) and what is matched by the rest of the regular expression;
this is useful in \regexReplaceAll when the goal is to extract matches or submatches in a finer
way than with \regexExtractAll.
• every alphanumeric character (A–Z, a–z, 0–9) matches exactly itself, and should not be escaped,
because \A, \B, … have special meanings;
• non-alphanumeric printable ASCII characters can (and should) always be escaped: many of them
have special meanings (e.g., use \(, \), \?, \., \^);
• spaces should always be escaped (even in character classes);
• any other character may be escaped or not, without any effect: both versions match exactly that
character.
Note that these rules play nicely with the fact that many non-alphanumeric characters are difficult to
input into TEX under normal category codes. For instance, \\abc\% matches the characters \abc% (with
arbitrary category codes), but does not match the control sequence \abc followed by a percent character.
Matching control sequences can be done using the \c{hregexi} syntax (see below).
Any special character which appears at a place where its special behaviour cannot apply matches itself
instead (for instance, a quantifier appearing at the beginning of a string), after raising a warning.
Characters.
Of those, ., \D, \H, \N, \S, \V, and \W match arbitrary control sequences.
Character classes match exactly one token in the subject.
[^…] Negative character class. Matches any token other than the specified characters.
x-y Within a character class, this denotes a range (can be used with escaped characters).
[:hnamei:] Within a character class (one more set of brackets), this denotes the posix character class hnamei,
which can be alnum, alpha, ascii, blank, cntrl, digit, graph, lower, print, punct, space,
upper, word, or xdigit.
[:^hnamei:] Negative posix character class.
For instance, [a-oq-z\cC.] matches any lowercase latin letter except p, as well as control sequences (see
below for a description of \c).
In character classes, only [, ^, -, ], \ and spaces are special, and should be escaped. Other non-
alphanumeric characters can still be escaped without harm. Any escape sequence which matches a single
character (\d, \D, etc.) is supported in character classes. If the first character is ^, then the meaning of
the character class is inverted; ^ appearing anywhere else in the range is not special. If the first character
(possibly following a leading ^) is ] then it does not need to be escaped since ending the range there
would make it empty. Ranges of characters can be expressed using -, for instance, [\D 0-5] and [^6-9]
are equivalent.
? 0 or 1, greedy.
?? 0 or 1, lazy.
* 0 or more, greedy.
*? 0 or more, lazy.
+ 1 or more, greedy.
+? 1 or more, lazy.
{n} Exactly n.
{n,} n or more, greedy.
{n,}? n or more, lazy.
For greedy quantifiers the regex code will first investigate matches that involve as many repetitions as
possible, while for lazy quantifiers it investigates matches with as few repetitions as possible first.
Alternation and capturing groups.
Capturing groups are a means of extracting information about the match. Parenthesized groups are la-
belled in the order of their opening parenthesis, starting at 1. The contents of those groups corresponding
to the “best” match (leftmost longest) can be extracted and stored in a sequence of token lists using for
instance \regexExtractOnceTF.
The \K escape sequence resets the beginning of the match to the current position in the token list. This
only affects what is reported as the full match. For instance,
results in \lFooSeq containing the items {1} and {a}: the true matches are {a1} and {aa}, but they are
trimmed by the use of \K. The \K command does not affect capturing groups: for instance,
results in \lFooSeq containing the items {c3} and {bc}: the true match is {acbc3}, with first submatch
{bc}, but \K resets the beginning of the match to the last position where it appears.
\c{hregexi} A control sequence whose csname matches the hregexi, anchored at the beginning and end, so that
\c{begin} matches exactly \begin, and nothing else.
\cX Applies to the next object, which can be a character, escape character sequence such as \x{0A},
character class, or group, and forces this object to only match tokens with category X (any of
CBEMTPUDSLOA. For instance, \cL[A-Z\d] matches uppercase letters and digits of category code
letter, \cC. matches any control sequence, and \cO(abc) matches abc where each character has
category other.1
1 This last example also captures “abc” as a regex group; to avoid this use a non-capturing group \cO(?:abc).
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 99
\c[XYZ] Applies to the next object, and forces it to only match tokens with category X, Y, or Z (each being
any of CBEMTPUDSLOA). For instance, \c[LSO](..) matches two tokens of category letter, space, or
other.
\c[^XYZ] Applies to the next object and prevents it from matching any token with category X, Y, or Z
(each being any of CBEMTPUDSLOA). For instance, \c[^O]\d matches digits which have any category
different from other.
The category code tests can be used inside classes; for instance, [\cO\d \c[LO][A-F]] matches what
TEX considers as hexadecimal digits, namely digits with category other, or uppercase letters from A to F
with category either letter or other. Within a group affected by a category code test, the outer test can
be overridden by a nested test: for instance, \cL(ab\cO\*cd) matches ab*cd where all characters are of
category letter, except * which has category other.
The \u escape sequence allows to insert the contents of a token list directly into a regular expression
or a replacement, avoiding the need to escape special characters. Namely, \u{hvar namei} matches the
exact contents (both character codes and category codes) of the variable \hvar namei. Within a \c{...}
control sequence matching, the \u escape sequence only expands its argument once. Quantifiers are
supported.
The \ur escape sequence allows to insert the contents of a regex variable into a larger regular ex-
pression. For instance, A\ur{lTmpaRegex}D matches the tokens A and D separated by something that
matches the regular expression \lTmpaRegex. This behaves as if a non-capturing group were surrounding
\lTmpaRegex, and any group contained in \lTmpaRegex is converted to a non-capturing group. Quanti-
fiers are supported.
For instance, if \lTmpaRegex has value B|C, then A\ur{l_tmpa_regex}D is equivalent to A(?:B|C)D
(matching ABD or ACD) and not to AB|CD (matching AB or CD). To get the latter effect, it is simplest to
use TEX’s expansion machinery directly: if \lTmpaTl contains B|C then the following two lines show the
same result:
\regexShow {A \u{lTmpaTl} D}
\regexShow {A B | C D}
13.5.6 Miscellaneous
Anchors and simple assertions.
\b Word boundary: either the previous token is matched by \w and the next by \W, or the opposite.
For this purpose, the ends of the token list are considered as \W.
\B Not a word boundary: between two \w tokens or two \W tokens (including the boundary).
^or \A Start of the subject token list.
$, \Z or \z End of the subject token list.
\G Start of the current match. This is only different from ^ in the case of multiple matches: for
instance \regexCount {\G a} {aaba} \lTmpaInt yields 2, but replacing \G by ^ would result in
\lTmpaInt holding the value 1.
The option (?i) makes the match case insensitive (identifying A–Z with a–z; no Unicode support yet).
This applies until the end of the group in which it appears, and can be reverted using (?-i). For instance,
in (?i)(a(?-i)b|c)d, the letters a and d are affected by the i option. Characters within ranges and
classes are affected individually: (?i)[Y-\\] is equivalent to [YZ\[\\yz], and (?i)[^aeiou] matches
any character which is not a vowel. Neither character properties, nor \c{...} nor \u{...} are affected
by the i option.
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 100
• \1 is the submatch that was matched by the first (capturing) group (...); similarly for \2, …, \9
and \g{hnumberi};
• \ inserts a space (spaces are ignored when not escaped);
• \a, \e, \f, \n, \r, \t, \xhh, \x{hhh} correspond to single characters as in regular expressions;
Characters other than backslash and space are simply inserted in the result (but since the replacement
text is first converted to a string, one should also escape characters that are special for TEX, for instance
use \#). Non-alphanumeric characters can always be safely escaped with a backslash.
For instance,
The submatches are numbered according to the order in which the opening parenthesis of capturing
groups appear in the regular expression to match. The n-th submatch is empty if there are fewer than n
capturing groups or for capturing groups that appear in alternatives that were not used for the match.
In case a capturing group matches several times during a match (due to quantifiers) only the last match
is used in the replacement text. Submatches always keep the same category codes as in the original token
list.
By default, the category code of characters inserted by the replacement are determined by the prevailing
category code regime at the time where the replacement is made, with two exceptions:
• space characters (with character code 32) inserted with \ or \x20 or \x{20} have category code
10 regardless of the prevailing category code regime;
• if the category code would be 0 (escape), 5 (newline), 9 (ignore), 14 (comment) or 15 (invalid), it
is replaced by 12 (other) instead.
The escape sequence \c allows to insert characters with arbitrary category codes, as well as control
sequences.
\cX(…) Produces the characters “…” with category X, which must be one of CBEMTPUDSLOA as in regular
expressions. Parentheses are optional for a single character (which can be an escape sequence).
When nested, the innermost category code applies, for instance \cL(Hello\cS\ world)! gives this
text with standard category codes.
\c{htexti} Produces the control sequence with csname htexti. The htexti may contain references to the sub-
matches \0, \1, and so on, as in the example for \u below.
The escape sequence \u{hvar namei} allows to insert the contents of the variable with name hvar namei
directly into the replacement, giving an easier control of category codes. When nested in \c{…} and
\u{…} constructions, the \u and \c escape sequences extract the value of the control sequence and turn
it into a string. Matches can also be used within the arguments of \c and \u. For instance,
CHAPTER 13. REGULAR EXPRESSIONS (REGEX) 101
Regex replacement is also a convenient way to produce token lists with arbitrary category codes. For
instance
\tlClear \lTmpaTl
\regexReplaceAll { } {\cU\% \cA\~} \lTmpaTl
results in \lTmpaTl containing the percent character with category code 7 (superscript) and an active
tilde character.
Chapter 14
\charLowercase hchari
\charUppercase hchari
\charTitlecase hchari
\charFoldcase hchari
Converts the hchari to the equivalent case-changed character as detailed by the function name (see
\textTitlecase for details of these terms). The case mapping is carried out with no context-dependence
(cf. \textUppercase, etc.) These functions generate characters with the category code of the hchari (i.e.
only the character code changes).
\charStrLowercase hchari
\charStrUppercase hchari
\charStrTitlecase hchari
\charStrFoldcase hchari
Converts the hchari to the equivalent case-changed character as detailed by the function name (see
\textTitlecase for details of these terms). The case mapping is carried out with no context-dependence
(cf. \textUppercase, etc.) These functions generate “other” (category code 12) characters.
Sets up the behaviour of the hcharacteri when found inside \textLowercase, such that hcharacter1 i will
be converted into hcharacter2 i. The two hcharactersi may be specified using an hinteger expressioni for
the character code concerned. This may include the TEX `hcharacteri method for converting a single
character into its character code:
Sets up the behaviour of the hcharacteri when found inside \textUppercase, such that hcharacter1 i will
be converted into hcharacter2 i. The two hcharactersi may be specified using an hinteger expressioni for
the character code concerned. This may include the TEX `hcharacteri method for converting a single
character into its character code:
102
CHAPTER 14. TOKEN MANIPULATION (TOKEN) 103
Returns the current lower case code of the hcharacteri with character code given by the hinteger expressioni.
Returns the current upper case code of the hcharacteri with character code given by the hinteger expressioni.
Chapter 15
This module deals with manipulation of (formatted) text; such material is comprised of a restricted set
of token list content. The functions provided here concern conversion of textual content for example
in case changing, Begin-group and end-group tokens in the htexti are normalized and become { and },
respectively.
\textExpand {htexti}
Takes user input htexti and expands the content. Protected commands (typically formatting) are left in
place, and no processing takes place of math mode material. Commands which are neither engine- nor
LATEX protected are expanded exhaustively.
\textLowercase {htokensi}
\textUppercase {htokensi}
\textTitlecase {htokensi}
\textTitlecaseFirst {htokensi}
Takes user input htexti first applies \textExpand, then transforms the case of character tokens as specified
by the function name. The category code of letters are not changed by this process (at least where they
can be represented by the engine as a single token: 8-bit engines may require active characters).
Upper- and lowercase have the obvious meanings. Titlecasing may be regarded informally as converting
the first character of the htokensi to uppercase and the rest to lowercase. However, the process is more
complex than this as there are some situations where a single lowercase character maps to a special form,
for example ij in Dutch which becomes IJ.
For titlecasing, note that there are two functions available. The function \textTitlecase applies
(broadly) uppercasing to the first letter of the input, then lowercasing to the remainder. In contrast,
\textTitlecaseFirst only carries out the uppercasing operation, and leaves the balance of the input
unchanged.
Case changing does not take place within math mode material. For example:
104
CHAPTER 15. TEXT PROCESSING (TEXT) 105
Takes user input htexti first applies \textExpand, then transforms the case of character tokens as specified
by the function name. The category code of letters are not changed by this process (at least where they
can be represented by the engine as a single token: 8-bit engines may require active characters).
These conversions are language-sensitive, and follow Unicode Consortium guidelines. Currently, the
languages recognised for special handling are as follows.
• Azeri and Turkish (az and tr). The case pairs I/i-dotless and I-dot/i are activated for these
languages. The combining dot mark is removed when lowercasing I-dot and introduced when upper
casing i-dotless.
• German (de-alt). An alternative mapping for German in which the lowercase Eszett maps to a
großes Eszett. Since there is a T1 slot for the großes Eszett in T1, this tailoring is available with
pdfTeX as well as in the Unicode TEX engines.
• Greek (el). Removes accents from Greek letters when uppercasing; titlecasing leaves accents in
place. (At present this is implemented only for Unicode engines.)
• Lithuanian (lt). The lowercase letters i and j should retain a dot above when the accents grave,
acute or tilde are present. This is implemented for lowercasing of the relevant uppercase letters
both when input as single Unicode codepoints and when using combining accents. The combining
dot is removed when uppercasing in these cases. Note that only the accents used in Lithuanian are
covered: the behaviour of other accents are not modified.
• Dutch (nl). Capitalisation of ij at the beginning of titlecased input produces IJ rather than Ij.
The output retains two separate letters, thus this transformation is available using pdfTeX.
Chapter 16
Files (File)
Searches for hfile namei in the path as detailed for \fileIfExistTF, and if found reads in the file and
returns the contents. All files read are recorded for information and the file name stack is updated by
this function. An error is raised if the file is not found.
Searches for hfile namei using the current TEX search path. If found then reads in the file and returns
the contents as described for \fileInput, otherwise inserts the hfalse codei. Note that these functions
do not raise an error if the file is not found, in contrast to \fileInput.
Defines htli to the contents of hfilenamei. Category codes may need to be set appropriately via the hsetupi
argument. The non-branching version sets the htli to \qNoValue if the file is not found. The branching
version runs the htrue codei after the assignment to htli if the file is found, and hfalse codei otherwise.
106
CHAPTER 16. FILES (FILE) 107
Searches for hfile namei using the current TEX search path.
Chapter 17
Quarks (Quark)
Quarks are control sequences (and in fact, token lists) that expand to themselves and should therefore
never be executed directly in the code. This would result in an endless loop!
Quarks can be used as error return values for functions that receive erroneous input. For example, in the
function \propGet to retrieve a value stored in some key of a property list, if the key does not exist then
the return value is the quark \qNoValue. As mentioned above, such quarks are extremely fragile and it
is imperative when using such functions that code is carefully written to check for pathological cases to
avoid leakage of a quark into an uncontrolled environment.
\qNoValue
A canonical value for a missing value, when one is requested from a data structure. This is therefore used
as a “return” value by functions such as \propGet if there is no data to return.
\quarkVarIfNoValue htokeni
\quarkVarIfNoValueT htokeni {htrue codei}
\quarkVarIfNoValueF htokeni {hfalse codei}
\quarkVarIfNoValueTF htokeni {htrue codei} {hfalse codei}
108
Chapter 18
There are a small number of TEX or LATEX 2ε concepts which are not used in functional code but which
need to be manipulated when working as a LATEX 2ε package. To allow these to be integrated cleanly
into functional code, a set of legacy interfaces are provided here.
\legacyIf {hnamei}
\legacyIfT {hnamei} {htrue codei}
\legacyIfF {hnamei} {hfalse codei}
\legacyIfTF {hnamei} {htrue codei} {hfalse codei}
Tests if the LATEX 2ε /plain TEX conditional (generated by \newif) if true or false and branches
accordingly. The hnamei of the conditional should omit the leading if.
\newif \ifFooBar
\legacyIfTF {FooBar} {\prgReturn{True!}} {\prgReturn{False!}} False!
\legacyIfSetTrue {hnamei}
\legacyIfSetFalse {hnamei}
Sets the LATEX 2ε /plain TEX conditional \ifhnamei (generated by \newif) to be true or false.
\newif \ifFooBar
\legacyIfSetTrue {FooBar} True!
\legacyIfTF {FooBar} {\prgReturn{True!}} {\prgReturn{False!}}
Sets the LATEX 2ε /plain TEX conditional \ifhnamei (generated by \newif) to the result of evaluating the
hboolean expressioni.
\newif \ifFooBar
\legacyIfSet {FooBar} {\cFalseBool} False!
\legacyIfTF {FooBar} {\prgReturn{True!}} {\prgReturn{False!}}
109
Chapter 19
%% ----------------------------------------------------------------------------
%% Functional: Intuitive Functional Programming Interface for LaTeX2
%% Copyright : 2022-2023 (c) Jianrui Lyu <[email protected]>
%% Repository: https://github.com/lvjr/functional
%% Repository: https://bitbucket.org/lvjr/functional
%% License : The LaTeX Project Public License 1.3c
%% ----------------------------------------------------------------------------
\RequirePackage{expl3}
\ProvidesExplPackage{functional}{2024-12-18}{2024C}
{^^JIntuitive Functional Programming Interface for LaTeX2}
\cs_generate_variant:Nn \iow_log:n { V }
\cs_generate_variant:Nn \str_set:Nn { Ne }
\cs_generate_variant:Nn \tl_const:Nn { NV }
\cs_generate_variant:Nn \tl_log:n { e }
\cs_generate_variant:Nn \tl_set:Nn { Ne }
\prg_generate_conditional_variant:Nnn \str_if_eq:nn { Ve } { TF }
\cs_new_protected:Npn \__fun_ignore_spaces_on:
{
\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
\cs_new_protected:Npn \__fun_scoping_true:
{
110
CHAPTER 19. THE SOURCE CODE 111
\cs_new_protected:Npn \__fun_scoping_false:
{
\cs_set_eq:NN \__fun_group_begin: \scan_stop:
\cs_set_eq:NN \__fun_group_end: \scan_stop:
}
\cs_new_protected:Npn \__fun_scoping_set:
{
\bool_if:NTF \l__fun_scoping_bool
{ \__fun_scoping_true: } { \__fun_scoping_false: }
}
\bool_new:N \l__fun_tracing_bool
\tl_new:N \l__tracing_text_tl
\cs_new_protected:Npn \__fun_tracing_log_on:n #1
{
\tl_set:Ne \l__tracing_text_tl
{
\prg_replicate:nn
{ \int_eval:n { (\g__fun_nesting_level_int - 1) * 4 } } { ~ }
}
\tl_put_right:Nn \l__tracing_text_tl { #1 }
\iow_log:V \l__tracing_text_tl
}
\cs_generate_variant:Nn \__fun_tracing_log_on:n { e, V }
\cs_new_protected:Npn \__fun_tracing_log_off:n #1 { }
\cs_new_protected:Npn \__fun_tracing_log_off:e #1 { }
\cs_new_protected:Npn \__fun_tracing_log_off:V #1 { }
\cs_new_protected:Npn \__fun_tracing_true:
{
\cs_set_eq:NN \__fun_tracing_log:n \__fun_tracing_log_on:n
\cs_set_eq:NN \__fun_tracing_log:e \__fun_tracing_log_on:e
\cs_set_eq:NN \__fun_tracing_log:V \__fun_tracing_log_on:V
}
\cs_new_protected:Npn \__fun_tracing_false:
{
\cs_set_eq:NN \__fun_tracing_log:n \__fun_tracing_log_off:n
\cs_set_eq:NN \__fun_tracing_log:e \__fun_tracing_log_off:e
\cs_set_eq:NN \__fun_tracing_log:V \__fun_tracing_log_off:V
}
\cs_new_protected:Npn \__fun_tracing_set:
{
\bool_if:NTF \l__fun_tracing_bool
{ \__fun_tracing_true: } { \__fun_tracing_false: }
}
\keys_define:nn { functional }
CHAPTER 19. THE SOURCE CODE 112
{
scoping .bool_set:N = \l__fun_scoping_bool,
tracing .bool_set:N = \l__fun_tracing_bool,
}
\NewDocumentCommand \Functional { m }
{
\keys_set:nn { functional } { #1 }
\__fun_scoping_set:
\__fun_tracing_set:
}
\l__fun_parameters_defined_tl
{
\__fun_group_begin:
\tl_gclear:N \gResultTl
#3
\__fun_tracing_log:e { [O] ~ \exp_not:V \gResultTl }
\__fun_group_end:
}
\use:c { __fun_new_with_arg_ \int_to_roman:n { \l__fun_arg_count_int } :NnV }
#1 {#2} \l__fun_parameters_defined_tl
}
\cs_generate_variant:Nn \__fun_new_function:Nnn { cne }
\tl_new:N \g__fun_last_result_tl
\int_new:N \l__fun_cond_arg_count_int
\int_new:N \g__fun_nesting_level_int
\int_gdecr:N \g__fun_nesting_level_int
\__fun_return_result:
}
}
\cs_generate_variant:Nn \__fun_new_with_arg_:Nnn { NnV }
\cs_new_protected:Npn #1 #3
{
\int_gincr:N \g__fun_nesting_level_int
\__fun_new_arg_tl_vars:
\__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 }
\cs_new_protected:Npn #1 #3
{
\int_gincr:N \g__fun_nesting_level_int
\__fun_new_arg_tl_vars:
\__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 }
\__fun_return_result:
}
}
\cs_generate_variant:Nn \__fun_new_with_arg_ix:Nnn { NnV }
\tl_new:N \l__fun_argtype_tl
\tl_const:Nn \c__fun_argtype_e_tl { e }
\tl_const:Nn \c__fun_argtype_E_tl { E }
\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
\cs_new_protected:Npn \__fun_evaluate_all_and_put_argument:N #1
{
CHAPTER 19. THE SOURCE CODE 119
\__fun_eval_all:V #1
\__fun_arguments_gput:e { { \exp_not:V \gResultTl } }
}
\cs_new_protected:Npn \__fun_evaluate_and_put_argument:N #1
{
\cs_if_exist:cTF
{
__fun_defined_ \exp_last_unbraced:Ne \cs_to_str:N { \tl_head:N #1 } : w
}
{
#1
\__fun_arguments_gput:e { { \exp_not:V \gResultTl } }
}
{
\__fun_arguments_gput:e { { \exp_not:V #1 } }
}
}
\cs_new_protected:Npn \__fun_argtype_index_gzero:
{
\int_gzero_new:c
CHAPTER 19. THE SOURCE CODE 120
\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
#1 { g__fun_arguments_ \int_use:N \g__fun_nesting_level_int _tl }
}
\cs_generate_variant:Nn \__fun_arguments_called:N { c }
\cs_new_protected:Npn \__fun_arguments_gclear:
{
\tl_gclear:c { g__fun_arguments_ \int_use:N \g__fun_nesting_level_int _tl }
}
\cs_new_protected:Npn \__fun_arguments_log:N #1
{
\__fun_tracing_log:e
{
[I] ~ \token_to_str:N #1
\exp_not:v { g__fun_arguments_ \int_use:N \g__fun_nesting_level_int _tl }
}
}
\cs_new_protected:Npn \__fun_arguments_gput:n #1
{
\tl_gput_right:cn
{ g__fun_arguments_ \int_use:N \g__fun_nesting_level_int _tl } { #1 }
}
\cs_generate_variant:Nn \__fun_arguments_gput:n { e }
\prgNewFunction \prgReturn { m }
{
\__fun_put_result:n { #1 }
}
%% Obsolete function, will be removed in the future
%% We can not define it with \PrgSetEqFunction
\PrgNewFunction \Result { m }
{
\__fun_put_result:n { #1 }
}
\int_new:N \l__fun_return_level_int
\cs_new_protected:Npn \__fun_use_result_default:
{
\tl_use:N \gResultTl
}
\__fun_set_return_processor_default:
{
\group_begin:
\__fun_set_return_processor:n {#1}
#2
\group_end:
}
\tl_new:N \l__fun_eval_result_tl
%% Evaluate all functions in #1 and replace them with their return values
\cs_new_protected:Npn \__fun_eval_all:n #1
{
\fun_run_return_processor:nn
{ \exp_last_unbraced:NV \__fun_eval_all_aux:n \gResultTl }
{
\tl_clear:N \l__fun_eval_result_tl
\__fun_eval_all_aux:n #1 \q_stop
\tl_gset_eq:NN \gResultTl \l__fun_eval_result_tl
}
}
\cs_generate_variant:Nn \__fun_eval_all:n { V }
\cs_new_protected:Npn \__fun_eval_all_aux:n #1
{
\tl_if_single_token:nTF {#1}
{
\token_if_eq_meaning:NNF #1 \q_stop
{
\bool_lazy_and:nnTF
{ \token_if_cs_p:N #1 }
{ \cs_if_exist_p:c { __fun_defined_ \cs_to_str:N #1 : w } }
{ #1 }
{
\tl_put_right:Nn \l__fun_eval_result_tl {#1}
\__fun_eval_all_aux:n
}
}
}
{
%% The braces enclosing a single token (such as {x}) are removed
%% but I guess there is no harm inside \int_eval:n or \fp_eval:n
\tl_put_right:Nn \l__fun_eval_result_tl { {#1} }
\__fun_eval_all_aux:n
}
}
CHAPTER 19. THE SOURCE CODE 123
\tl_new:N \l__fun_eval_whole_tl
\bool_new:N \l__fun_eval_none_bool
%% Evaluate all functions in #1 and replace them with their return values
\cs_new_protected:Npn \__fun_eval_whole:n #1
{
\fun_run_return_processor:nn
{
\bool_if:NTF \l__fun_eval_none_bool
{
\tl_put_right:Nx \l__fun_eval_whole_tl
{ \exp_not:N \exp_not:n { \exp_not:V \gResultTl } }
\bool_set_false:N \l__fun_eval_none_bool
\__fun_eval_whole_aux:
}
{ \exp_last_unbraced:NV \__fun_eval_whole_aux: \gResultTl }
}
{
\tl_clear:N \l__fun_eval_whole_tl
\__fun_eval_whole_aux: #1 \q_stop
%\tl_log:N \l__fun_eval_whole_tl
\tl_gset:Nx \gResultTl { \l__fun_eval_whole_tl }
}
}
\cs_new_protected:Npn \__fun_eval_whole_aux:
{
%% ##1: <tokens> which both o-expand and x-expand to the current <token>;
%% ##2: <charcode>, a decimal number, −1 for a control sequence;
%% ##3: <catcode>, a capital hexadecimal digit, 0 for a control sequence.
\peek_analysis_map_inline:n
{
\int_compare:nNnTF {##2} = {-1} % control sequence
{
\exp_last_unbraced:No \token_if_eq_meaning:NNTF {##1} \q_stop
{ \peek_analysis_map_break: }
{
\cs_if_exist:cTF
{ __fun_defined_ \exp_last_unbraced:No \cs_to_str:N {##1} : w }
{
\exp_last_unbraced:No \cs_if_eq:NNT {##1} \evalNone
{ \bool_set_true:N \l__fun_eval_none_bool }
\peek_analysis_map_break:n
{
%% since ##1 is of the form "\exp_not:N \someFunc",
%% we need to remove \exp_not:N first before evaluating
\use:x {##1}
}
CHAPTER 19. THE SOURCE CODE 124
}
{ \tl_put_right:Nn \l__fun_eval_whole_tl {##1} }
}
}
{ \tl_put_right:Nn \l__fun_eval_whole_tl {##1} }
}
}
\prgNewFunction \prgRunOneArgCode { m n }
{
\cs_set:Npn \__fun_one_arg_cmd:n ##1 {#2}
\exp_args:NNo \tl_set:Nn \gResultTl { \__fun_one_arg_cmd:n {#1} }
}
\prgNewFunction \prgRunTwoArgCode { m m n }
{
\cs_set:Npn \__fun_two_arg_cmd:nn ##1 ##2 {#3}
\exp_args:NNo \tl_set:Nn \gResultTl { \__fun_two_arg_cmd:nn {#1} {#2} }
}
\prgNewFunction \prgRunThreeArgCode { m m m n }
{
\cs_set:Npn \__fun_three_arg_cmd:nnn ##1 ##2 ##3 {#4}
\exp_args:NNo \tl_set:Nn \gResultTl
{ \__fun_three_arg_cmd:nnn {#1} {#2} {#3} }
}
\prgNewFunction \prgRunFourArgCode { m m m m n }
{
\cs_set:Npn \__fun_four_arg_cmd:nnnn ##1 ##2 ##3 ##4 {#5}
\exp_args:NNo \tl_set:Nn \gResultTl
{ \__fun_four_arg_cmd:nnnn {#1} {#2} {#3} {#4} }
}
CHAPTER 19. THE SOURCE CODE 125
\prg_new_protected_conditional:Npnn \__fun_if_global_variable:N #1 { TF }
{
\str_set:Ne \l__fun_variable_name_str { \cs_to_str:N #1 }
\str_set:Ne \l__fun_variable_name_b_str
{ \str_item:Nn \l__fun_variable_name_str { 2 } }
\str_if_eq:VeTF
\l__fun_variable_name_b_str
{ \str_uppercase:f { \l__fun_variable_name_b_str } }
{
\str_set:Ne \l__fun_variable_name_a_str
{ \str_head:N \l__fun_variable_name_str }
\str_case:VnF \l__fun_variable_name_a_str
{
{ l } { \prg_return_false: }
{ g } { \prg_return_true: }
}
{ \__fun_if_set_local: }
}
{ \__fun_if_set_local: }
}
\bool_new:N \g__fun_variable_local_bool
\cs_new:Npn \__fun_if_set_local:
{
\bool_if:NTF \g__fun_variable_local_bool
{
\bool_gset_false:N \g__fun_variable_local_bool
\prg_return_false:
}
{ \prg_return_true: }
}
\prgNewFunction \prgLocal { }
{ \bool_gset_true:N \g__fun_variable_local_bool }
\prgNewFunction \expValue { M }
{
\__fun_put_result:V #1
}
\prgNewFunction \expWhole { m }
{
\__fun_put_result:e { #1 }
}
\prgNewFunction \expPartial { m }
{
\__fun_put_result:f { #1 }
}
\prgNewFunction \expOnce { m }
{
\__fun_put_result:o { #1 }
}
\prgNewFunction \gobbleOne { n } { }
\prgNewFunction \boolSet { M e } {
CHAPTER 19. THE SOURCE CODE 127
\__fun_do_assignment:Nnn #1
{ \bool_gset:Nn #1 {#2} } { \bool_set:Nn #1 {#2} }
}
\prgNewFunction \boolSetTrue { M }
{
\__fun_do_assignment:Nnn #1 { \bool_gset_true:N #1 } { \bool_set_true:N #1 }
}
\prgNewFunction \boolSetFalse { M }
{
\__fun_do_assignment:Nnn #1 { \bool_gset_false:N #1 } { \bool_set_false:N #1 }
}
\prgNewFunction \boolSetEq { M M }
{
\__fun_do_assignment:Nnn #1
{ \bool_gset_eq:NN #1 #2 } { \bool_set_eq:NN #1 #2 }
}
\prgNewConditional \boolIfExist { M }
{
\bool_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \boolVarNot { M }
{
\bool_if:NTF #1
{ \prgReturn { \cFalseBool } } { \prgReturn { \cTrueBool } }
}
\prgNewConditional \boolVarAnd { M M }
{
\bool_lazy_and:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \boolVarOr { M M }
{
\bool_lazy_or:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \boolVarXor { M M }
CHAPTER 19. THE SOURCE CODE 128
{
\bool_xor:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewFunction \boolVarDoUntil { N n }
{
\bool_do_until:Nn #1 {#2}
}
\prgNewFunction \boolVarDoWhile { N n }
{
\bool_do_while:Nn #1 {#2}
}
\prgNewFunction \boolVarUntilDo { N n }
{
\bool_until_do:Nn #1 {#2}
}
\prgNewFunction \boolVarWhileDo { N n }
{
\bool_while_do:Nn #1 {#2}
}
\prgNewFunction \tlToStr { m }
{ \expWhole { \tl_to_str:n { #1 } } }
CHAPTER 19. THE SOURCE CODE 129
\prgNewFunction \tlVarToStr { M }
{ \expWhole { \tl_to_str:N #1 } }
\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 \tlClearNew { M }
{
\__fun_do_assignment:Nnn #1 { \tl_gclear_new:N #1 } { \tl_clear_new:N #1 }
}
\prgNewFunction \tlPutLeft { M m }
{
\__fun_do_assignment:Nnn #1
{ \tl_gput_left:Nn #1 {#2} } { \tl_put_left:Nn #1 {#2} }
}
\prgNewFunction \tlPutRight { M m }
{
\__fun_do_assignment:Nnn #1
{ \tl_gput_right:Nn #1 {#2} } { \tl_put_right:Nn #1 {#2} }
}
\prgNewFunction \tlVarReplaceOnce { M m m }
{
\__fun_do_assignment:Nnn #1
{ \tl_greplace_once:Nnn #1 {#2} {#3} } { \tl_replace_once:Nnn #1 {#2} {#3} }
}
\prgNewFunction \tlVarReplaceAll { M m m }
{
\__fun_do_assignment:Nnn #1
{ \tl_greplace_all:Nnn #1 {#2} {#3} } { \tl_replace_all:Nnn #1 {#2} {#3} }
}
CHAPTER 19. THE SOURCE CODE 130
\prgNewFunction \tlVarRemoveOnce { M m }
{
\__fun_do_assignment:Nnn #1
{ \tl_gremove_once:Nn #1 {#2} } { \tl_remove_once:Nn #1 {#2} }
}
\prgNewFunction \tlVarRemoveAll { M m }
{
\__fun_do_assignment:Nnn #1
{ \tl_gremove_all:Nn #1 {#2} } { \tl_remove_all:Nn #1 {#2} }
}
\prgNewFunction \tlTrimSpaces { m }
{ \expWhole { \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 }
{ \expWhole { \tl_count:n { #1 } } }
\prgNewFunction \tlVarCount { M }
{ \expWhole { \tl_count:N #1 } }
\prgNewFunction \tlHead { m }
{ \expWhole { \tl_head:n { #1 } } }
\prgNewFunction \tlVarHead { M }
{ \expWhole { \tl_head:N #1 } }
\prgNewFunction \tlTail { m }
{ \expWhole { \tl_tail:n { #1 } } }
\prgNewFunction \tlVarTail { M }
{ \expWhole { \tl_tail:N #1 } }
\prgNewFunction \tlItem { m m }
{ \expWhole { \tl_item:nn {#1} {#2} } }
\prgNewFunction \tlVarItem { M m }
{ \expWhole { \tl_item:Nn #1 {#2} } }
\prgNewFunction \tlRandItem { m }
{ \expWhole { \tl_rand_item:n {#1} } }
\prgNewFunction \tlVarRandItem { M }
{ \expWhole { \tl_rand_item:N #1 } }
\prgNewFunction \tlVarCase { M m }
{ \tl_case:Nn {#1} {#2} }
\prgNewFunction \tlVarCaseT { M m n }
CHAPTER 19. THE SOURCE CODE 131
\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
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlIfEmpty { m }
{
\tl_if_empty:nTF {#1}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlVarIfEmpty { M }
{
\tl_if_empty:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlIfBlank { m }
{
\tl_if_blank:nTF {#1}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlIfEq { m m }
{
\tl_if_eq:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
CHAPTER 19. THE SOURCE CODE 132
\prgNewConditional \tlVarIfEq { M M }
{
\tl_if_eq:NNTF #1 #2
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlIfIn { m m }
{
\tl_if_in:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlVarIfIn { M m }
{
\tl_if_in:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlIfSingle { m }
{
\tl_if_single:nTF {#1}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \tlVarIfSingle { M }
{
\tl_if_single:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\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 \strVarReplaceOnce { M m m }
{
\__fun_do_assignment:Nnn #1
CHAPTER 19. THE SOURCE CODE 134
\prgNewFunction \strVarReplaceAll { M m m }
{
\__fun_do_assignment:Nnn #1
{ \str_greplace_all:Nnn #1 {#2} {#3} }
{ \str_replace_all:Nnn #1 {#2} {#3} }
}
\prgNewFunction \strVarRemoveOnce { M m }
{
\__fun_do_assignment:Nnn #1
{ \str_gremove_once:Nn #1 {#2} } { \str_remove_once:Nn #1 {#2} }
}
\prgNewFunction \strVarRemoveAll { M m }
{
\__fun_do_assignment:Nnn #1
{ \str_gremove_all:Nn #1 {#2} } { \str_remove_all:Nn #1 {#2} }
}
\prgNewConditional \strIfExist { M }
CHAPTER 19. THE SOURCE CODE 135
{
\str_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strVarIfEmpty { M }
{
\str_if_empty:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strIfEq { m m }
{
\str_if_eq:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strVarIfEq { M M }
{
\str_if_eq:NNTF #1 #2
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strIfIn { m m }
{
\str_if_in:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strVarIfIn { M m }
{
\str_if_in:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \strCompare { m N m }
{
\str_compare:nNnTF {#1} #2 {#3}
{ \prgReturn { \cTrueBool } }
{ \prgReturn { \cFalseBool } }
}
\prgNewFunction \intMathDivTruncate { e e }
{
\expWhole { \int_div_truncate:nn {#1} {#2} }
}
\prgNewFunction \intSet { M e }
{
\__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 }
CHAPTER 19. THE SOURCE CODE 137
\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 }
}
\prgNewFunction \intDecr { M }
{
\__fun_do_assignment:Nnn #1 { \int_gdecr:N #1 } { \int_decr:N #1 }
}
\prgNewFunction \intAdd { M e }
{
\__fun_do_assignment:Nnn #1 { \int_gadd:Nn #1 {#2} } { \int_add:Nn #1 {#2} }
}
\prgNewFunction \intSub { M e }
{
\__fun_do_assignment:Nnn #1 { \int_gsub:Nn #1 {#2} } { \int_sub:Nn #1 {#2} }
}
\prgNewFunction \intStepInline { e e e n }
{
\int_step_inline:nnnn {#1} {#2} {#3} {#4}
}
\prgNewFunction \intStepOneInline { e e n }
{
\int_step_inline:nnn {#1} {#2} {#3}
}
\prgNewFunction \intStepVariable { e e e M n }
{
\int_step_variable:nnnNn {#1} {#2} {#3} #4 {#5}
}
\prgNewFunction \intStepOneVariable { e e M n }
CHAPTER 19. THE SOURCE CODE 138
{
\int_step_variable:nnNn {#1} {#2} #3 {#4}
}
\prgNewConditional \intIfExist { M }
{
\int_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \intIfOdd { e }
{
\int_if_odd:nTF { #1 }
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \intIfEven { e }
{
\int_if_even:nTF { #1 }
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \intCompare { e N e }
{
\int_compare:nNnTF {#1} #2 {#3}
{ \prgReturn { \cTrueBool } }
{ \prgReturn { \cFalseBool } }
}
\prgNewFunction \fpSet { M e }
{
\__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 e }
{
CHAPTER 19. THE SOURCE CODE 140
\prgNewFunction \fpSub { M e }
{
\__fun_do_assignment:Nnn #1 { \fp_gsub:Nn #1 {#2} } { \fp_sub:Nn #1 {#2} }
}
\prgNewFunction \fpStepInline { e e e n }
{
\fp_step_inline:nnnn {#1} {#2} {#3} {#4}
}
\prgNewFunction \fpStepVariable { e e e M n }
{
\fp_step_variable:nnnNn {#1} {#2} {#3} #4 {#5}
}
\prgNewConditional \fpIfExist { M }
{
\fp_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \fpCompare { e N e }
{
\fp_compare:nNnTF {#1} #2 {#3}
{ \prgReturn { \cTrueBool } }
{ \prgReturn { \cFalseBool } }
}
\prgNewFunction \dimEval { m }
{
\prgReturn { \expWhole { \dim_eval:n { #1 } } }
}
\prgNewFunction \dimMathAdd { m m }
{
\dim_set:Nn \l@FunTmpxDim { \dim_eval:n { (#1) + (#2) } }
\prgReturn { \expValue \l@FunTmpxDim }
}
CHAPTER 19. THE SOURCE CODE 141
\prgNewFunction \dimMathSub { m m }
{
\dim_set:Nn \l@FunTmpxDim { \dim_eval:n { (#1) - (#2) } }
\prgReturn { \expValue \l@FunTmpxDim }
}
\prgNewFunction \dimMathSign { m }
{
\prgReturn { \expWhole { \dim_sign:n { #1 } } }
}
\prgNewFunction \dimMathAbs { m }
{
\prgReturn { \expWhole { \dim_abs:n { #1 } } }
}
\prgNewFunction \dimMathMax { m m }
{
\prgReturn { \expWhole { \dim_max:nn { #1 } { #2 } } }
}
\prgNewFunction \dimMathMin { m m }
{
\prgReturn { \expWhole { \dim_min:nn { #1 } { #2 } } }
}
\prgNewFunction \dimMathRatio { m m }
{
\prgReturn { \expWhole { \dim_ratio:nn { #1 } { #2 } } }
}
\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 }
CHAPTER 19. THE SOURCE CODE 142
\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
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \dimCompare { m N m }
{
\dim_compare:nNnTF {#1} #2 {#3}
{ \prgReturn { \cTrueBool } } { \prgReturn { \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} }
CHAPTER 19. THE SOURCE CODE 143
\clist_const:Nn \cEmptyClist {}
\prgNewFunction \clistVarJoin { M m }
{
\expWhole { \clist_use:Nn #1 { #2 } }
}
\prgNewFunction \clistVarJoinExtended { M m m m }
{
\expWhole { \clist_use:Nnnn #1 { #2 } { #3 } { #4 } }
}
\prgNewFunction \clistJoin { m m }
{
\expWhole { \clist_use:nn { #1 } { #2 } }
}
\prgNewFunction \clistJoinExtended { m m m m }
{
\expWhole { \clist_use:nnnn { #1 } { #2 } { #3 } { #4 } }
}
\prgNewFunction \clistConst { M m }
{ \clist_const:Nn #1 { #2 } }
\prgNewFunction \clistSet { M m }
CHAPTER 19. THE SOURCE CODE 144
{
\__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 \clistVarRemoveDuplicates { M }
{
\__fun_do_assignment:Nnn #1
{ \clist_gremove_duplicates:N #1 } { \clist_remove_duplicates:N #1 }
}
\prgNewFunction \clistVarRemoveAll { M m }
{
\__fun_do_assignment:Nnn #1
CHAPTER 19. THE SOURCE CODE 145
\prgNewFunction \clistVarReverse { M }
{
\__fun_do_assignment:Nnn #1 { \clist_greverse:N #1 } { \clist_reverse:N #1 }
}
\prgNewFunction \clistVarSort { M m }
{
\__fun_do_assignment:Nnn #1
{ \clist_gsort:Nn #1 {#2} } { \clist_sort:Nn #1 {#2} }
}
\prgNewFunction \clistCount { m }
{ \expWhole { \clist_count:n { #1 } } }
\prgNewFunction \clistVarCount { M }
{ \expWhole { \clist_count:N #1 } }
\prgNewFunction \clistGet { M M }
{
\clist_get:NN #1 #2
\__fun_quark_upgrade_no_value:N #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 }
\__fun_quark_upgrade_no_value:N #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} }
}
CHAPTER 19. THE SOURCE CODE 146
\prgNewFunction \clistItem { m m }
{ \expWhole { \clist_item:nn {#1} {#2} } }
\prgNewFunction \clistVarItem { M m }
{ \expWhole { \clist_item:Nn #1 {#2} } }
\prgNewFunction \clistRandItem { m }
{ \expWhole { \clist_rand_item:n {#1} } }
\prgNewFunction \clistVarRandItem { M }
{ \expWhole { \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}
}
\prgNewConditional \clistIfExist { M }
{
\clist_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \clistIfEmpty { m }
{
\clist_if_empty:nTF {#1}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \clistVarIfEmpty { M }
{
\clist_if_empty:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \clistIfIn { m m }
{
\clist_if_in:nnTF {#1} {#2}
CHAPTER 19. THE SOURCE CODE 147
\prgNewConditional \clistVarIfIn { M m }
{
\clist_if_in:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\seq_const_from_clist:Nn \cEmptySeq {}
\prgNewFunction \seqVarJoin { M m }
{
\expWhole { \seq_use:Nn #1 { #2 } }
}
\prgNewFunction \seqVarJoinExtended { M m m m }
{
\expWhole { \seq_use:Nnnn #1 { #2 } { #3 } { #4 } }
}
\prgNewFunction \seqJoin { m m }
{
\expWhole { \seq_use:nn { #1 } { #2 } }
}
\prgNewFunction \seqJoinExtended { m m m m }
{
\expWhole { \seq_use:nnnn { #1 } { #2 } { #3 } { #4 } }
}
\prgNewFunction \seqConstFromClist { M m }
{ \seq_const_from_clist:Nn #1 { #2 } }
\prgNewFunction \seqSetFromClist { M m }
{
\__fun_do_assignment:Nnn #1
CHAPTER 19. THE SOURCE CODE 148
\prgNewFunction \seqSetEq { M M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gset_eq:NN #1 #2 } { \seq_set_eq:NN #1 #2 }
}
\prgNewFunction \seqSetSplit { M m m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gset_split:Nnn #1 {#2} {#3} } { \seq_set_split:Nnn #1 {#2} {#3} }
}
\prgNewFunction \seqConcat { M M M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gconcat:NNN #1 #2 #3 } { \seq_concat:NNN #1 #2 #3 }
}
\prgNewFunction \seqClear { M }
{
\__fun_do_assignment:Nnn #1 { \seq_gclear:N #1 } { \seq_clear:N #1 }
}
\prgNewFunction \seqClearNew { M }
{
\__fun_do_assignment:Nnn #1 { \seq_gclear_new:N #1 } { \seq_clear_new:N #1 }
}
\prgNewFunction \seqPutLeft { M m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gput_left:Nn #1 {#2} } { \seq_put_left:Nn #1 {#2} }
}
\prgNewFunction \seqPutRight { M m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gput_right:Nn #1 {#2} } { \seq_put_right:Nn #1 {#2} }
}
\prgNewFunction \seqVarRemoveDuplicates { M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gremove_duplicates:N #1 } { \seq_remove_duplicates:N #1 }
}
\prgNewFunction \seqVarRemoveAll { M m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gremove_all:Nn #1 {#2} } { \seq_remove_all:Nn #1 {#2} }
}
CHAPTER 19. THE SOURCE CODE 149
\prgNewFunction \seqVarReverse { M }
{
\__fun_do_assignment:Nnn #1 { \seq_greverse:N #1 } { \seq_reverse:N #1 }
}
\prgNewFunction \seqVarSort { M m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gsort:Nn #1 {#2} } { \seq_sort:Nn #1 {#2} }
}
\prgNewFunction \seqVarCount { M }
{ \expWhole { \seq_count:N #1 } }
\prgNewFunction \seqGet { M M }
{
\seq_get:NN #1 #2
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqGetT { M M n }
{ \seq_get:NNT #1 #2 {#3} }
\prgNewFunction \seqGetF { M M n }
{ \seq_get:NNF #1 #2 {#3} }
\prgNewFunction \seqGetTF { M M n n }
{ \seq_get:NNTF #1 #2 {#3} {#4} }
\prgNewFunction \seqPop { M M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop:NN #1 #2 } { \seq_pop:NN #1 #2 }
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqPopT { M M n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop:NNT #1 #2 {#3} } { \seq_pop:NNT #1 #2 {#3} }
}
\prgNewFunction \seqPopF { M M n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop:NNF #1 #2 {#3} } { \seq_pop:NNF #1 #2 {#3} }
}
\prgNewFunction \seqPopTF { M M n n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop:NNTF #1 #2 {#3} {#4} } { \seq_pop:NNTF #1 #2 {#3} {#4} }
}
\prgNewFunction \seqPush { M m }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpush:Nn #1 {#2} } { \seq_push:Nn #1 {#2} }
}
\prgNewFunction \seqGetLeft { M M }
{
CHAPTER 19. THE SOURCE CODE 150
\seq_get_left:NN #1 #2
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqGetLeftT { M M n }
{ \seq_get_left:NNT #1 #2 {#3} }
\prgNewFunction \seqGetLeftF { M M n }
{ \seq_get_left:NNF #1 #2 {#3} }
\prgNewFunction \seqGetLeftTF { M M n n }
{ \seq_get_left:NNTF #1 #2 {#3} {#4} }
\prgNewFunction \seqGetRight { M M }
{
\seq_get_right:NN #1 #2
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqGetRightT { M M n }
{ \seq_get_right:NNT #1 #2 {#3} }
\prgNewFunction \seqGetRightF { M M n }
{ \seq_get_right:NNF #1 #2 {#3} }
\prgNewFunction \seqGetRightTF { M M n n }
{ \seq_get_right:NNTF #1 #2 {#3} {#4} }
\prgNewFunction \seqPopLeft { M M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_left:NN #1 #2 } { \seq_pop_left:NN #1 #2 }
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqPopLeftT { M M n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_left:NNT #1 #2 {#3} } { \seq_pop_left:NNT #1 #2 {#3} }
}
\prgNewFunction \seqPopLeftF { M M n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_left:NNF #1 #2 {#3} } { \seq_pop_left:NNF #1 #2 {#3} }
}
\prgNewFunction \seqPopLeftTF { M M n n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_left:NNTF #1 #2 {#3} {#4} }
{ \seq_pop_left:NNTF #1 #2 {#3} {#4} }
}
\prgNewFunction \seqPopRight { M M }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_right:NN #1 #2 } { \seq_pop_right:NN #1 #2 }
\__fun_quark_upgrade_no_value:N #2
}
\prgNewFunction \seqPopRightT { M M n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_right:NNT #1 #2 {#3} } { \seq_pop_right:NNT #1 #2 {#3} }
}
\prgNewFunction \seqPopRightF { M M n }
CHAPTER 19. THE SOURCE CODE 151
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_right:NNF #1 #2 {#3} } { \seq_pop_right:NNF #1 #2 {#3} }
}
\prgNewFunction \seqPopRightTF { M M n n }
{
\__fun_do_assignment:Nnn #1
{ \seq_gpop_right:NNTF #1 #2 {#3} {#4} }
{ \seq_pop_right:NNTF #1 #2 {#3} {#4} }
}
\prgNewFunction \seqVarItem { M m }
{ \expWhole { \seq_item:Nn #1 {#2} } }
\prgNewFunction \seqVarRandItem { M }
{ \expWhole { \seq_rand_item:N #1 } }
\prgNewFunction \seqVarMapInline { M n }
{
\seq_map_inline:Nn #1 {#2}
}
\prgNewFunction \seqVarMapVariable { M M n }
{
\seq_map_variable:NNn #1 #2 {#3}
}
\prgNewConditional \seqIfExist { M }
{
\seq_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \seqVarIfEmpty { M }
{
\seq_if_empty:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \seqVarIfIn { M m }
{
\seq_if_in:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prop_const_from_keyval:Nn \cEmptyProp {}
\prgNewFunction \propConstFromKeyval { M m }
{ \prop_const_from_keyval:Nn #1 { #2 } }
\prgNewFunction \propSetFromKeyval { M m }
{
\__fun_do_assignment:Nnn #1
{ \prop_gset_from_keyval:Nn #1 {#2} } { \prop_set_from_keyval:Nn #1 {#2} }
}
\prgNewFunction \propSetEq { M M }
{
\__fun_do_assignment:Nnn #1
{ \prop_gset_eq:NN #1 #2 } { \prop_set_eq:NN #1 #2 }
}
\prgNewFunction \propClear { M }
{
\__fun_do_assignment:Nnn #1 { \prop_gclear:N #1 } { \prop_clear:N #1 }
}
\prgNewFunction \propClearNew { M }
{
\__fun_do_assignment:Nnn #1 { \prop_gclear_new:N #1 } { \prop_clear_new:N #1 }
}
\prgNewFunction \propConcat { M M M }
{
\__fun_do_assignment:Nnn #1
{ \prop_gconcat:NNN #1 #2 #3 } { \prop_concat:NNN #1 #2 #3 }
}
\prgNewFunction \propPut { M m m }
{
\__fun_do_assignment:Nnn #1
{ \prop_gput:Nnn #1 {#2} {#3} } { \prop_put:Nnn #1 {#2} {#3} }
}
\prgNewFunction \propPutIfNew { M m m }
{
\__fun_do_assignment:Nnn #1
{ \prop_gput_if_new:Nnn #1 {#2} {#3} } { \prop_put_if_new:Nnn #1 {#2} {#3} }
}
\prgNewFunction \propPutFromKeyval { M m }
CHAPTER 19. THE SOURCE CODE 153
{
\__fun_do_assignment:Nnn #1
{ \prop_gput_from_keyval:Nn #1 {#2} } { \prop_put_from_keyval:Nn #1 {#2} }
}
\prgNewFunction \propVarRemove { M m }
{
\__fun_do_assignment:Nnn #1
{ \prop_gremove:Nn #1 {#2} } { \prop_remove:Nn #1 {#2} }
}
\prgNewFunction \propGet { M m M }
{
\prop_get:NnN #1 {#2} #3
\__fun_quark_upgrade_no_value:N #3
}
\prgNewFunction \propGetT { M m M n } { \prop_get:NnNT #1 {#2} #3 {#4} }
\prgNewFunction \propGetF { M m M n } { \prop_get:NnNF #1 {#2} #3 {#4} }
\prgNewFunction \propGetTF { M m M n n } { \prop_get:NnNTF #1 {#2} #3 {#4} {#5} }
\prgNewFunction \propPop { M m M }
{
\__fun_do_assignment:Nnn #1
{ \prop_gpop:NnN #1 {#2} #3 } { \prop_pop:NnN #1 {#2} #3 }
\__fun_quark_upgrade_no_value:N #3
}
\prgNewFunction \propPopT { M m M n }
{
\__fun_do_assignment:Nnn #1
{ \prop_gpop:NnNT #1 {#2} #3 {#4} } { \prop_pop:NnNT #1 {#2} #3 {#4} }
}
\prgNewFunction \propPopF { M m M n }
{
\__fun_do_assignment:Nnn #1
{ \prop_gpop:NnNF #1 {#2} #3 {#4} } { \prop_pop:NnNF #1 {#2} #3 {#4} }
}
\prgNewFunction \propPopTF { M m M n n }
{
\__fun_do_assignment:Nnn #1
{ \prop_gpop:NnNTF #1 {#2} #3 {#4} {#5} }
{ \prop_pop:NnNTF #1 {#2} #3 {#4} {#5} }
}
\prgNewConditional \propIfExist { M }
{
CHAPTER 19. THE SOURCE CODE 154
\prop_if_exist:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \propVarIfEmpty { M }
{
\prop_if_empty:NTF #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \propVarIfIn { M m }
{
\prop_if_in:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewFunction \regexSet { M m }
{
\__fun_do_assignment:Nnn #1
{ \regex_gset:Nn #1 {#2} } { \regex_set:Nn #1 {#2} }
}
\prgNewConditional \regexMatch { m m }
{
\regex_match:nnTF {#1} {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewConditional \regexVarMatch { M m }
CHAPTER 19. THE SOURCE CODE 155
{
\regex_match:NnTF #1 {#2}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewFunction \regexMatchCase { m m }
{
\regex_match_case:nn {#1} {#2}
}
\prgNewFunction \regexMatchCaseT { m m n }
{
\regex_match_case:nnT {#1} {#2} {#3}
}
\prgNewFunction \regexMatchCaseF { m m n }
{
\regex_match_case:nnF {#1} {#2} {#3}
}
\prgNewFunction \regexMatchCaseTF { m m n n }
{
\regex_match_case:nnTF {#1} {#2} {#3} {#4}
}
\prgNewFunction \regexExtractOnce { m m M }
{
\regex_extract_once:nnN {#1} {#2} #3
}
\prgNewFunction \regexExtractOnceT { m m M n }
{
\regex_extract_once:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \regexExtractOnceF { m m M n }
{
\regex_extract_once:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \regexExtractOnceTF { m m M n n }
{
\regex_extract_once:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewFunction \regexVarExtractOnce { M m M }
{
\regex_extract_once:NnN #1 {#2} #3
}
\prgNewFunction \regexVarExtractOnceT { M m M n }
{
\regex_extract_once:NnNT #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarExtractOnceF { M m M n }
{
\regex_extract_once:NnNF #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarExtractOnceTF { M m M n n }
{
CHAPTER 19. THE SOURCE CODE 156
\prgNewFunction \regexExtractAll { m m M }
{
\regex_extract_all:nnN {#1} {#2} #3
}
\prgNewFunction \regexExtractAllT { m m M n }
{
\regex_extract_all:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \regexExtractAllF { m m M n }
{
\regex_extract_all:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \regexExtractAllTF { m m M n n }
{
\regex_extract_all:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewFunction \regexVarExtractAll { M m M }
{
\regex_extract_all:NnN #1 {#2} #3
}
\prgNewFunction \regexVarExtractAllT { M m M n }
{
\regex_extract_all:NnNT #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarExtractAllF { M m M n }
{
\regex_extract_all:NnNF #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarExtractAllTF { M m M n n }
{
\regex_extract_all:NnNTF #1 {#2} #3 {#4} {#5}
}
\prgNewFunction \regexSplit { m m M }
{
\regex_split:nnN {#1} {#2} #3
}
\prgNewFunction \regexSplitT { m m M n }
{
\regex_split:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \regexSplitF { m m M n }
{
\regex_split:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \regexSplitTF { m m M n n }
{
\regex_split:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewFunction \regexVarSplit { M m M }
{
\regex_split:NnN #1 {#2} #3
CHAPTER 19. THE SOURCE CODE 157
}
\prgNewFunction \regexVarSplitT { M m M n }
{
\regex_split:NnNT #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarSplitF { M m M n }
{
\regex_split:NnNF #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarSplitTF { M m M n n }
{
\regex_split:NnNTF #1 {#2} #3 {#4} {#5}
}
\prgNewFunction \regexReplaceOnce { m m M }
{
\regex_replace_once:nnN {#1} {#2} #3
}
\prgNewFunction \regexReplaceOnceT { m m M n }
{
\regex_replace_once:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \regexReplaceOnceF { m m M n }
{
\regex_replace_once:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \regexReplaceOnceTF { m m M n n }
{
\regex_replace_once:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewFunction \regexVarReplaceOnce { M m M }
{
\regex_replace_once:NnN #1 {#2} #3
}
\prgNewFunction \regexVarReplaceOnceT { M m M n }
{
\regex_replace_once:NnNT #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarReplaceOnceF { M m M n }
{
\regex_replace_once:NnNF #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarReplaceOnceTF { M m M n n }
{
\regex_replace_once:NnNTF #1 {#2} #3 {#4} {#5}
}
\prgNewFunction \regexReplaceAll { m m M }
{
\regex_replace_all:nnN {#1} {#2} #3
}
\prgNewFunction \regexReplaceAllT { m m M n }
{
\regex_replace_all:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \regexReplaceAllF { m m M n }
CHAPTER 19. THE SOURCE CODE 158
{
\regex_replace_all:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \regexReplaceAllTF { m m M n n }
{
\regex_replace_all:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewFunction \regexVarReplaceAll { M m M }
{
\regex_replace_all:NnN #1 {#2} #3
}
\prgNewFunction \regexVarReplaceAllT { M m M n }
{
\regex_replace_all:NnNT #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarReplaceAllF { M m M n }
{
\regex_replace_all:NnNF #1 {#2} #3 {#4}
}
\prgNewFunction \regexVarReplaceAllTF { M m M n n }
{
\regex_replace_all:NnNTF #1 {#2} #3 {#4} {#5}
}
\prgNewFunction \regexReplaceCaseOnce { m M }
{
\regex_replace_case_once:nN {#1} #2
}
\prgNewFunction \regexReplaceCaseOnceT { m M n }
{
\regex_replace_case_once:nN {#1} #2 {#3}
}
\prgNewFunction \regexReplaceCaseOnceF { m M n }
{
\regex_replace_case_once:nN {#1} #2 {#3}
}
\prgNewFunction \regexReplaceCaseOnceTF { m M n n }
{
\regex_replace_case_once:nN {#1} #2 {#3} {#4}
}
\prgNewFunction \regexReplaceCaseAll { m M }
{
\regex_replace_case_all:nN {#1} #2
}
\prgNewFunction \regexReplaceCaseAllT { m M n }
{
\regex_replace_case_all:nN {#1} #2 {#3}
}
\prgNewFunction \regexReplaceCaseAllF { m M n }
{
\regex_replace_case_all:nN {#1} #2 {#3}
}
\prgNewFunction \regexReplaceCaseAllTF { m M n n }
{
\regex_replace_case_all:nN {#1} #2 {#3} {#4}
CHAPTER 19. THE SOURCE CODE 159
\prgNewFunction \textLowercase { m }
{
\expWhole { \text_lowercase:n {#1} }
}
\prgNewFunction \textUppercase { m }
{
\expWhole { \text_uppercase:n {#1} }
}
\prgNewFunction \textTitlecase { m }
{
\expWhole { \text_titlecase:n {#1} }
}
\prgNewFunction \textTitlecaseFirst { m }
CHAPTER 19. THE SOURCE CODE 160
{
\expWhole { \text_titlecase_first:n {#1} }
}
\prgNewFunction \textLangLowercase { m m }
{
\expWhole { \text_lowercase:nn {#1} {#2} }
}
\prgNewFunction \textLangUppercase { m m }
{
\expWhole { \text_uppercase:nn {#1} {#2} }
}
\prgNewFunction \textLangTitlecase { m m }
{
\expWhole { \text_titlecase:nn {#1} {#2} }
}
\prgNewFunction \textLangTitlecaseFirst { m m }
{
\expWhole { \text_titlecase_first:nn {#1} {#2} }
}
\prgNewFunction \fileInput { m }
{
\file_get:nnN {#1} {} \l@FunTmpxTl
\quark_if_no_value:NTF \l@FunTmpxTl
{ \msg_error:nnn { functional } { file-not-found } { #1 } }
{ \tlUse \l@FunTmpxTl }
}
\prgNewFunction \fileIfExistInput { m }
{
\file_get:nnN {#1} {} \l@FunTmpxTl
\quark_if_no_value:NF \l@FunTmpxTl { \tlUse \l@FunTmpxTl }
}
\prgNewFunction \fileIfExistInputF { m n }
{
\file_get:nnN {#1} {} \l@FunTmpxTl
\quark_if_no_value:NTF \l@FunTmpxTl { #2 } { \tlUse \l@FunTmpxTl }
}
\prgNewFunction \fileGet { m m M }
{
\file_get:nnN {#1} {#2} #3
\__fun_quark_upgrade_no_value:N #3
CHAPTER 19. THE SOURCE CODE 161
\prgNewFunction \fileGetT { m m M n }
{
\file_get:nnNT {#1} {#2} #3 {#4}
}
\prgNewFunction \fileGetF { m m M n }
{
\file_get:nnNF {#1} {#2} #3 {#4}
}
\prgNewFunction \fileGetTF { m m M n n }
{
\file_get:nnNTF {#1} {#2} #3 {#4} {#5}
}
\prgNewConditional \fileIfExist { m }
{
\file_if_exist:nTF {#1}
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\cs_new_protected:Npn \__fun_quark_upgrade_no_value:N #1
{
\quark_if_no_value:NT #1 { \tl_set_eq:NN #1 \qNoValue }
}
\prgNewConditional \quarkVarIfNoValue { M }
{
\tl_if_eq:NNTF \qNoValue #1
{ \prgReturn { \cTrueBool } } { \prgReturn { \cFalseBool } }
}
\prgNewFunction \legacyIfSetTrue { m }
{
\__fun_do_assignment:Nnn \c@name
{ \legacy_if_gset_true:n {#1} } { \legacy_if_set_true:n {#1} }
}
\prgNewFunction \legacyIfSetFalse { m }
CHAPTER 19. THE SOURCE CODE 162
{
\__fun_do_assignment:Nnn \c@name
{ \legacy_if_gset_false:n {#1} } { \legacy_if_set_false:n {#1} }
}
\prgNewFunction \legacyIfSet { m m }
{
\__fun_do_assignment:Nnn \c@name
{ \legacy_if_gset:nn {#1} {#2} } { \legacy_if_set:nn {#1} {#2} }
}