summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2011-08-13 22:19:08 +0000
committerKarl Berry <karl@freefriends.org>2011-08-13 22:19:08 +0000
commit5730d1c679f7a725b75ce80066cc6a6987aee888 (patch)
treedef203d2e98df13d7d8b1f97444810f2c685a974 /Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
parent8fdbc868b3cd64d89d3b4a172779aea071a118c5 (diff)
l3kernel 2564 (13aug11)
git-svn-id: svn://tug.org/texlive/trunk@23540 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3tl.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3tl.dtx1133
1 files changed, 776 insertions, 357 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx b/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
index 149f4a487ba..3db13f9b400 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
@@ -35,7 +35,7 @@
%
%<*driver|package>
\RequirePackage{l3names}
-\GetIdInfo$Id: l3tl.dtx 2534 2011-08-10 18:21:44Z joseph $
+\GetIdInfo$Id: l3tl.dtx 2563 2011-08-13 12:23:59Z joseph $
{L3 Experimental token lists}
%</driver|package>
%<*driver>
@@ -79,6 +79,15 @@
% braces. Many of the functions for token lists and token list variables
% are very similar, and so are grouped together here.
%
+% A token list can be seen either as a list of \enquote{items},
+% or a list of \enquote{tokens}. An item is whatever \cs{use_none:n}
+% grabs as its argument: either a single 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 \texttt{N} argument,
+% or | |, |{|, or |}| (assuming normal \TeX{} category codes).
+% Functions which act on items are often faster than their analog acting
+% directly on tokens.
+%
% \section{Creating and initialising token list variables}
%
% \begin{function}{\tl_new:N, \tl_new:c}
@@ -274,7 +283,10 @@
% Replaces all occurrences of \meta{old tokens} in the
% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens}
% cannot contain |{|, |}| or |#|
-% (assuming normal \TeX{} category codes). The assignment is
+% (assuming normal \TeX{} category codes). As this function
+% operates from left to right, the pattern \meta{old tokens}
+% may remain after the replacement (see \cs{tl_remove_all:Nn}
+% for an example). The assignment is
% restricted to the current \TeX{} group.
% \end{function}
%
@@ -286,8 +298,10 @@
% Replaces all occurrences of \meta{old tokens} in the
% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens}
% cannot contain |{|, |}| or |#|
-% (assuming normal \TeX{} category codes). The assignment is
-% applied globally.
+% (assuming normal \TeX{} category codes). As this function
+% operates from left to right, the pattern \meta{old tokens}
+% may remain after the replacement (see \cs{tl_remove_all:Nn}
+% for an example). The assignment is applied globally.
% \end{function}
%
% \begin{function}{\tl_remove_once:Nn, \tl_remove_once:cn}
@@ -316,8 +330,15 @@
% \end{syntax}
% Removes all occurrences of \meta{tokens} from the
% \meta{tl~var}. \meta{Tokens} cannot contain |{|, |}| or
-% |#| (assuming normal \TeX{} category codes). The assignment is
-% restricted to the current \TeX{} group.
+% |#| (assuming normal \TeX{} category codes). As this function
+% operates from left to right, the pattern \meta{tokens}
+% may remain after the removal, for instance,
+% \begin{quote}
+% \cs{tl_set:Nn} \cs{l_tmpa_tl} |{abbccd}|
+% \cs{tl_remove_all:Nn} \cs{l_tmpa_tl} |{bc}|
+% \end{quote}
+% will result in \cs{l_tmpa_tl} containing \texttt{abcd}.
+% The assignment is restricted to the current \TeX{} group.
% \end{function}
%
% \begin{function}{\tl_gremove_all:Nn, \tl_gremove_all:cn}
@@ -326,7 +347,10 @@
% \end{syntax}
% Removes all occurrences of \meta{tokens} from the
% \meta{tl~var}. \meta{Tokens} cannot contain |{|, |}| or
-% |#| (assuming normal \TeX{} category codes). The assignment is
+% |#| (assuming normal \TeX{} category codes). As this function
+% operates from left to right, the pattern \meta{tokens}
+% may remain after the removal (see \cs{tl_remove_all:Nn}
+% for an example). The assignment is
% applied globally.
% \end{function}
%
@@ -342,7 +366,7 @@
% ~~\Arg{tokens}
% \end{syntax}
% Sets \meta{tl~var} to contain \meta{tokens},
-% applying the category code regim{\'e} specified in the
+% applying the category code r\'egime specified in the
% \meta{setup} before carrying out the assignment. This allows the
% \meta{tl~var} to contain material with category codes
% other than those that apply when \meta{tokens} are absorbed. The
@@ -360,7 +384,7 @@
% ~~\Arg{tokens}
% \end{syntax}
% Sets \meta{tl~var} to contain \meta{tokens},
-% applying the category code regim{\'e} specified in the
+% applying the category code r\'egime specified in the
% \meta{setup} before carrying out the assignment. This allows the
% \meta{tl~var} to contain material with category codes
% other than those that apply when \meta{tokens} are absorbed. The
@@ -371,7 +395,7 @@
% \begin{syntax}
% \cs{tl_rescan:nn} \Arg{setup} \Arg{tokens}
% \end{syntax}
-% Rescans \meta{tokens} applying the category code regim{\'e} specified
+% Rescans \meta{tokens} applying the category code r\'egime specified
% in the \meta{setup}, and leaves the resulting tokens in the input
% stream. See also \cs{tl_set_rescan:Nnn}.
% \end{function}
@@ -404,7 +428,7 @@
% unchanged. This process does not alter the category code assigned
% to the \meta{tokens}.
% \begin{texnote}
-% This is the \TeX{} primitive \cs{uppercase} renamed..
+% This is the \TeX{} primitive \cs{uppercase} renamed.
% As a result, this function takes place on execution and
% not on expansion.
% \end{texnote}
@@ -417,11 +441,11 @@
% \cs{tl_if_blank_p:n} \Arg{token list}
% \cs{tl_if_blank:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
% \end{syntax}
-% Tests if the \meta{token list} consists only of blank spaces. The
-% test is \texttt{true} if \meta{token list} is empty or consists
-% entirely of explicit tokens of character code $32$
-% and category code $10$, and is \texttt{false}
-% otherwise. The branching versions then leave either \meta{true code}
+% Tests if the \meta{token list} consists only of blank spaces
+% (\emph{i.e.}~contains no item). The test is \texttt{true} if
+% \meta{token list} is zero or more explicit tokens of character code~$32$
+% and category code~$10$, and is \texttt{false} otherwise.
+% The branching versions then leave either \meta{true code}
% or \meta{false code} in the input stream, as appropriate to the
% truth of the test and the variant of the function chosen. The logical
% truth of the test is left in the input stream by the predicate
@@ -520,14 +544,13 @@
% \begin{function}[EXP,pTF]{\tl_if_single:N, \tl_if_single:c}
% \begin{syntax}
% \cs{tl_if_single_p:N} \Arg{tl~var}
-% \cs{tl_if_single:nTF} \Arg{tl~var} \Arg{true code} \Arg{false code}
-% \end{syntax}
-% Tests if the content of the \meta{tl~var} consists of a
-% single token or token group. The test is \texttt{true} if
-% \meta{token list} contains exactly one token, if it consists
-% entirely of explicit tokens of character code $32$ and category
-% code $10$, or if it contains one braced token group (optionally
-% preceeded by spaces), and it is \texttt{false} otherwise. The branching
+% \cs{tl_if_single:NTF} \Arg{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the content of the \meta{tl~var} consists of a single item,
+% \emph{i.e.}~is either a single normal token (excluding spaces,
+% and brace tokens) or a single brace group, surrounded by optional
+% spaces on both sides. In other words, such a token list has length
+% $1$ according to \cs{tl_length:N}. The branching
% versions then leave either \meta{true code} or \meta{false code}
% in the input stream, as appropriate to the truth of the test and the
% variant of the function chosen. The logical truth of the test is left
@@ -537,19 +560,32 @@
% \begin{function}[EXP,pTF]{\tl_if_single:n}
% \begin{syntax}
% \cs{tl_if_single_p:n} \Arg{token list}
-% \cs{tl_if_single:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
-% \end{syntax}
-% Tests if the \meta{token list} consists of a single token
-% or token group. The test is \texttt{true} if \meta{token list}
-% contains exactly one token, or if it consists entirely of
-% explicit tokens of character code $32$ and category code $10$,
-% or if it contains a braced token group (optionally preceeded by blank
-% spaces), and it is
-% \texttt{false} otherwise. The branching versions then leave
-% either \meta{true code} or \meta{false code} in the input stream, as
-% appropriate to the truth of the test and the variant of the function
-% chosen. The logical truth of the test is left in the input stream by
-% the predicate version.
+% \cs{tl_if_single:nTF} \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the token list has exactly one item, \emph{i.e.}~is either
+% a single normal token or a single brace group, surrounded by
+% optional spaces on both sides. In other words, such a token list
+% has length $1$ according to \cs{tl_length:n}.
+% The branching versions leave either \meta{true code} or \meta{false code}
+% in the input stream, as appropriate to the truth of the test and
+% the variant of the function chosen. The logical truth of the test
+% is left in the input stream by the predicate.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\tl_if_single_token:n}
+% \begin{syntax}
+% \cs{tl_if_single_token_p:n} \Arg{token list}
+% \cs{tl_if_single_token:nTF} \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the token list consists of exactly one token, \emph{i.e.}~is
+% either a single space character or a single \enquote{normal} token.
+% Token groups (|{|\ldots|}|) are not single tokens.
+% The branching versions leave either \meta{true code} or \meta{false code}
+% in the input stream, as appropriate to the truth of the test and
+% the variant of the function chosen. The logical truth of the test
+% is left in the input stream by the predicate.
% \end{function}
%
% \section{Mapping to token lists}
@@ -558,10 +594,9 @@
% \begin{syntax}
% \cs{tl_map_function:NN} \meta{tl~var} \meta{function}
% \end{syntax}
-% Applies \meta{function} to every \meta{token group} stored in the
-% \meta{tl~var}. The \meta{function}
-% will receive one argument for each iteration. This may be a number
-% of tokens if the \meta{token group} was an item stored within
+% Applies \meta{function} to every \meta{item} in the \meta{tl~var}.
+% The \meta{function} will receive one argument for each iteration.
+% This may be a number of tokens if the \meta{item} was stored within
% braces. Hence the \meta{function} should anticipate receiving
% \texttt{n}-type arguments. See also \cs{tl_map_function:nN}.
% \end{function}
@@ -570,10 +605,9 @@
% \begin{syntax}
% \cs{tl_map_function:nN} \meta{token list} \meta{function}
% \end{syntax}
-% Applies \meta{function} to every \meta{token group} in the
-% \meta{token list}. The \meta{function}
-% will receive one argument for each iteration. This may be a number
-% of tokens if the \meta{token group} was an item stored within
+% Applies \meta{function} to every \meta{item} in the \meta{token list},
+% The \meta{function} will receive one argument for each iteration.
+% This may be a number of tokens if the \meta{item} was stored within
% braces. Hence the \meta{function} should anticipate receiving
% \texttt{n}-type arguments. See also \cs{tl_map_function:NN}.
% \end{function}
@@ -582,31 +616,29 @@
% \begin{syntax}
% \cs{tl_map_inline:Nn} \meta{tl~var} \Arg{inline function}
% \end{syntax}
-% Applies the \meta{inline function} to every \meta{token group} stored
-% within the \meta{tl~var}. The \meta{inline function}
-% should consist of code which will receive the \meta{token group} as
-% |#1|. One in line mapping can be nested inside another. See also
-% \cs{tl_map_function:Nn}.
+% Applies the \meta{inline function} to every \meta{item} stored within the
+% \meta{tl~var}. The \meta{inline function} should consist of code which
+% will receive the \meta{item} as |#1|. One in line mapping can be nested
+% inside another. See also \cs{tl_map_function:Nn}.
% \end{function}
%
% \begin{function}{\tl_map_inline:nn}
% \begin{syntax}
% \cs{tl_map_inline:nn} \meta{token list} \Arg{inline function}
% \end{syntax}
-% Applies the \meta{inline function} to every \meta{token group} stored
-% within the \meta{token list}. The \meta{inline function} should
-% consist of code which will receive the \meta{token group} as
-% |#1|. One in line mapping can be nested inside another. See also
-% \cs{tl_map_function:nn}.
+% Applies the \meta{inline function} to every \meta{item} stored within the
+% \meta{token list}. The \meta{inline function} should consist of code which
+% will receive the \meta{item} as |#1|. One in line mapping can be nested
+% inside another. See also \cs{tl_map_function:nn}.
% \end{function}
%
% \begin{function}{\tl_map_variable:NNn, \tl_map_variable:cNn}
% \begin{syntax}
% \cs{tl_map_variable:NNn} \meta{tl~var} \meta{variable} \Arg{function}
% \end{syntax}
-% Applies the \meta{function} to every \meta{token group} stored
+% Applies the \meta{function} to every \meta{item} stored
% within the \meta{tl~var}. The \meta{function} should consist of code
-% which will receive the \meta{token group} stored in the \meta{variable}.
+% which will receive the \meta{item} stored in the \meta{variable}.
% One variable mapping can be nested inside another. See also
% \cs{tl_map_inline:Nn}.
% \end{function}
@@ -615,9 +647,9 @@
% \begin{syntax}
% \cs{tl_map_variable:nNn} \meta{token list} \meta{variable} \Arg{function}
% \end{syntax}
-% Applies the \meta{function} to every \meta{token group} stored
+% Applies the \meta{function} to every \meta{item} stored
% within the \meta{token list}. The \meta{function} should consist of code
-% which will receive the \meta{token group} stored in the \meta{variable}.
+% which will receive the \meta{item} stored in the \meta{variable}.
% One variable mapping can be nested inside another. See also
% \cs{tl_map_inline:nn}.
% \end{function}
@@ -681,11 +713,12 @@
%
% \section{Working with the content of token lists}
%
-% \begin{function}[EXP]{\tl_length:n, \tl_length:V, \tl_length:o}
+% \begin{function}[EXP]
+% {\tl_length:n, \tl_length:V, \tl_length:o}
% \begin{syntax}
% \cs{tl_length:n} \Arg{tokens}
% \end{syntax}
-% Counts the number of token groups in \meta{tokens} and leaves this
+% Counts the number of \meta{items} in \meta{tokens} and leaves this
% information in the input stream. Unbraced tokens count as one
% element as do each token group (|{|\ldots|}|). This process will
% ignore any unprotected spaces within \meta{tokens}. See also
@@ -707,14 +740,16 @@
%
% \begin{function}[EXP]{\tl_reverse:n, \tl_reverse:V, \tl_reverse:o}
% \begin{syntax}
-% \cs{tl_reverse:n} \Arg{tokens}
+% \cs{tl_reverse:n} \Arg{token list}
% \end{syntax}
-% Reverses the order of the \meta{tokens}, so that
-% \meta{token1}\meta{token2}\meta{token3} \ldots \meta{token$_n$}
-% becomes \meta{token$_n$}\ldots
-% \meta{token3}\meta{token2}\meta{token1}. This process will remove
-% any unprotected space within the \meta{tokens}. Tokens are not reversed
-% within braced token groups, which lose their outer set of braces.
+% Reverses the order of the \meta{items} in the \meta{token list},
+% so that \meta{item1}\meta{item2}\meta{item3} \ldots \meta{item$_n$}
+% becomes \meta{item$_n$}\ldots \meta{item3}\meta{item2}\meta{item1}.
+% This process will preserve unprotected space within the
+% \meta{token list}. Tokens are not reversed within braced token
+% groups, which keep their outer set of braces.
+% In situations where performance is important,
+% consider \cs{tl_reverse_items:n}.
% See also \cs{tl_reverse:N}.
% \end{function}
%
@@ -722,18 +757,31 @@
% \begin{syntax}
% \cs{tl_reverse:N} \Arg{tl~var}
% \end{syntax}
-% Reverses the order of the \meta{tokens} stored in
-% \meta{tl~var}, so that
-% \meta{token1}\meta{token2}\meta{token3} \ldots \meta{token$_n$}
-% becomes \meta{token$_n$}\ldots
-% \meta{token3}\meta{token2}\meta{token1}. This process will
-% remove any unprotected spaces within the
+% Reverses the order of the \meta{items} stored in \meta{tl~var}, so
+% that \meta{item1}\meta{item2}\meta{item3} \ldots \meta{item$_n$}
+% becomes \meta{item$_n$}\ldots \meta{item3}\meta{item2}\meta{item1}.
+% This process will preserve unprotected spaces within the
% \meta{token list variable}. Braced token groups are copied without
-% reversing the order of tokens, and the outer set of braces is removed.
+% reversing the order of tokens, but keep the outer set of braces.
% The reversal is local to the current
% \TeX{} group. See also \cs{tl_reverse:n}.
% \end{function}
%
+% \begin{function}[EXP]{\tl_reverse_items:n}
+% \begin{syntax}
+% \cs{tl_reverse_items:n} \Arg{token list}
+% \end{syntax}
+% Reverses the order of the \meta{items} stored in \meta{tl~var},
+% so that \Arg{item1}\Arg{item2}\Arg{item3} \ldots \Arg{item$_n$}
+% becomes \Arg{item$_n$} \ldots{} \Arg{item3}\Arg{item2}\Arg{item1}.
+% This process will remove any unprotected space within the
+% \meta{token list}. Braced token groups are copied without
+% reversing the order of tokens, and keep the outer set of braces.
+% Items which are initially not braced are copied with braces in
+% the result. In cases where preserving spaces is important,
+% consider \cs{tl_reverse:n} or \cs{tl_reverse_tokens:n}.
+% \end{function}
+%
% \begin{function}[EXP]{\tl_trim_spaces:n}
% \begin{syntax}
% \cs{tl_trim_spaces:n} \meta{token list}
@@ -761,7 +809,7 @@
%
% \section{The first token from a token list}
%
-% Functions which deal with either onlt the very first token of a
+% Functions which deal with either only the very first token of a
% token list or everything except the first token.
%
% \begin{function}[EXP]{\tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f}
@@ -798,17 +846,6 @@
% is not critical.
% \end{function}
%
-% \begin{function}[EXP]{\str_head:n}
-% \begin{syntax}
-% \cs{str_head:n} \Arg{tokens}
-% \end{syntax}
-% Converts the \meta{tokens} into a string, as described for
-% \cs{tl_to_str:n}, and then leaves the first character of this string
-% in the input stream; the first character may be a space. If the
-% \meta{tokens} argument is entirely empty, \cs{scan_stop:} will be left in
-% the input stream.
-% \end{function}
-%
% \begin{function}[EXP]{\tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f}
% \begin{syntax}
% \cs{tl_tail:n} \Arg{tokens}
@@ -845,6 +882,20 @@
% is not critical.
% \end{function}
%
+% \begin{function}[EXP]{\str_head:n,\str_tail:n}
+% \begin{syntax}
+% \cs{str_head:n} \Arg{tokens}
+% \cs{str_tail:n} \Arg{tokens}
+% \end{syntax}
+% Converts the \meta{tokens} into a string, as described for
+% \cs{tl_to_str:n}. The \cs{str_head:n} function then leaves
+% the first character of this string in the input stream.
+% The \cs{str_tail:n} function leaves all characters except
+% the first in the input stream. The first character may be
+% a space. If the \meta{tokens} argument is entirely empty,
+% nothing is left in the input stream.
+% \end{function}
+%
% \begin{function}[EXP,pTF]{\tl_if_head_eq_catcode:nN}
% \begin{syntax}
% \cs{tl_if_head_eq_catcode_p:nN} \Arg{token list} \meta{test token}
@@ -852,7 +903,10 @@
% ~~\Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the first \meta{token} in the \meta{token list} has the same
-% category code as the \meta{test token}. The branching versions then
+% category code as the \meta{test token}. In the case where
+% \meta{token list} is empty, its head is considered to be \cs{q_nil},
+% and the test will be true if \meta{test token} is a control sequence.
+% The branching versions then
% leave either \meta{true code} or \meta{false code} in the input
% stream, as appropriate to the truth of the test and the variant of
% the function chosen. The logical truth of the test is left in the
@@ -868,7 +922,10 @@
% ~~\Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the first \meta{token} in the \meta{token list} has the same
-% character code as the \meta{test token}. The branching versions then
+% character code as the \meta{test token}. In the case where
+% \meta{token list} is empty, its head is considered to be \cs{q_nil},
+% and the test will be true if \meta{test token} is a control sequence.
+% The branching versions then
% leave either \meta{true code} or \meta{false code} in the input
% stream, as appropriate to the truth of the test and the variant of
% the function chosen. The logical truth of the test is left in the
@@ -883,13 +940,86 @@
% ~~\Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the first \meta{token} in the \meta{token list} has the same
-% meaning as the \meta{test token}. The branching versions then
+% meaning as the \meta{test token}. In the case where \meta{token list}
+% is empty, its head is considered to be \cs{q_nil}, and the test will
+% be true if \meta{test token} has the same meaning as \cs{q_nil}.
+% The branching versions then
% leave either \meta{true code} or \meta{false code} in the input
% stream, as appropriate to the truth of the test and the variant of
% the function chosen. The logical truth of the test is left in the
% input stream by the predicate
% version.
% \end{function}
+%
+% \begin{function}[EXP,pTF,int]{\tl_if_head_group:n}
+% \begin{syntax}
+% \cs{tl_if_head_group_p:n} \Arg{token list}
+% \cs{tl_if_head_group:nTF} \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list}
+% is an explicit begin-group character (with category code~1
+% and any character code), in other words, if the \meta{token list}
+% starts with a brace group. In particular, the test is false
+% if the \meta{token list} starts with an implicit token such as
+% \cs{c_group_begin_token}, or if it empty.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% The branching versions leave either \meta{true code} or \meta{false code}
+% in the input stream, as appropriate to the truth of the test and
+% the variant of the function chosen. The logical truth of the test
+% is left in the input stream by the predicate.
+% \end{function}
+%
+% \begin{function}[EXP,pTF,int]{\tl_if_head_N_type:n}
+% \begin{syntax}
+% \cs{tl_if_head_N_type_p:n} \Arg{token list}
+% \cs{tl_if_head_N_type:nTF} \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list}
+% is a normal \texttt{N}-type argument. In other words,
+% it is neither an explicit space character (with category code~10
+% and character code~32) nor an explicit begin-group character
+% (with category code~1 and any character code). An empty
+% argument yields false, as it does not have a \enquote{normal}
+% first token.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% The branching versions leave either \meta{true code} or \meta{false code}
+% in the input stream, as appropriate to the truth of the test and
+% the variant of the function chosen. The logical truth of the test
+% is left in the input stream by the predicate.
+% \end{function}
+%
+% \begin{function}[EXP,pTF,int]{\tl_if_head_space:n}
+% \begin{syntax}
+% \cs{tl_if_head_space_p:n} \Arg{token list}
+% \cs{tl_if_head_space:nTF} \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+%^^A We need to add a discussion of
+%^^A explicit vs implicit tokens somewhere in the doc.
+% Tests if the first \meta{token} in the \meta{token list}
+% is an explicit space character (with category code~$10$
+% and character code~$32$). If \meta{token list} starts with
+% an implicit token such as \cs{c_space_token}, the test
+% will yield false, as well as if the argument is empty.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% The branching versions leave either \meta{true code} or
+% \meta{false code} in the input stream, as appropriate to the truth
+% of the test and the variant of the function chosen. The logical
+% truth of the test is left in the input stream by the predicate.
+% \begin{texnote}
+% When \TeX{} reads a character of category code $10$ for the
+% first time, it is converted to an explicit space token, with
+% character code $32$, regardless of the initial character code.
+% \enquote{Funny} spaces with a different category code, can be produced
+% using \cs{tex_lowercase:D}. Explicit spaces are also produced
+% as a result of \cs{token_to_str:N}, \cs{tl_to_str:n}, etc.
+% \end{texnote}
+% \end{function}
%
% \section{Viewing token lists}
%
@@ -950,82 +1080,56 @@
%
% \section{Experimental token list functions}
%
-% \begin{function}[EXP,pTF]{\tl_if_single_item:n}
+% \begin{function}[EXP]{\tl_reverse_tokens:n}
% \begin{syntax}
-% \cs{tl_if_single_item_p:n} \Arg{token list}
-% \cs{tl_if_single_item:nTF} \Arg{token list}
-% ~~\Arg{true code} \Arg{false code}
+% \cs{tl_reverse_tokens:n} \Arg{tokens}
% \end{syntax}
-% Tests if the token list has exactly one item, i.e., is either
-% a single normal token or a single brace group, surrounded by
-% optional spaces on both sides. In other words, such a token list
-% has length $1$ according to \cs{tl_length:n}. The branching
-% versions then leave either \meta{true code} or \meta{false code}
-% in the input stream, as appropriate to the truth of the test and
-% the variant of the function chosen. The logical truth of the test
-% is left in the input stream by the predicate.
+% This function, which works directly on \TeX{} tokens, reverses
+% the order of the \meta{tokens}: the first will be the last and
+% the last will become first. Spaces are preserved. The reversal
+% also operates within brace groups, but the braces themselves
+% are not exchanged, as this would lead to an unbalanced token
+% list. For instance, \cs{tl_reverse_tokens:n} |{a~{b()}}|
+% leaves |{)(b}~a| in the input stream. This function requires
+% two steps of expansion.
% \end{function}
%
-% \begin{function}[EXP,pTF]{\tl_if_head_begin_group:n}
+% \begin{function}[EXP]{\tl_length_tokens:n}
% \begin{syntax}
-% \cs{tl_if_head_begin_group_p:n} \Arg{token list}
-% \cs{tl_if_head_begin_group:nTF} \Arg{token list}
-% ~~\Arg{true code} \Arg{false code}
+% \cs{tl_length_tokens:n} \Arg{tokens}
% \end{syntax}
-% Tests if the first \meta{token} in the \meta{token list}
-% has catcode~1, i.e., is a begin-group character. This conditional
-% is mainly intended to be used in combination with
-% \cs{tl_if_head_eq_space:n} to check if grabbing an undelimited
-% argument from the token list is safe. The branching
-% versions leave either \meta{true code} or \meta{false code}
-% in the input stream, as appropriate to the truth of the test and
-% the variant of the function chosen. The logical truth of the test
-% is left in the input stream by the predicate.
+% Counts the number of \TeX{} tokens in the \meta{tokens} and leaves
+% this information in the input stream. Every token, including spaces and
+% braces, contributes one to the total; thus for instance, the length of
+% |a~{bc}| is $6$.
+% This function requires three expansions,
+% giving an \meta{integer denotation}.
% \end{function}
%
-% \begin{function}[EXP,pTF]{\tl_if_head_eq_space:n}
+% \begin{function}[EXP]{\tl_expandable_uppercase:n,\tl_expandable_lowercase:n}
% \begin{syntax}
-% \cs{tl_if_head_eq_space_p:n} \Arg{token list}
-% \cs{tl_if_head_eq_space:nTF} \Arg{token list}
-% ~~\Arg{true code} \Arg{false code}
+% \cs{tl_expandable_uppercase:n} \Arg{tokens}
+% \cs{tl_expandable_lowercase:n} \Arg{tokens}
% \end{syntax}
-%^^A Needs rewriting. We need to add a discussion of
-%^^A explicit vs implicit tokens somewhere in the doc.
-% Tests if the first token of the token list is an explicit space,
-% i.e., a token which matches \verb*+ + both in character code and
-% in category code. In the case of an implicit space token, such as
-% \cs{c_space_token}, the test will return \meta{false}.
-% This conditional is mainly intended to be used in combination with
-% \cs{tl_if_head_begin_group:n} to test if grabbing an undelimited
-% argument is safe (see \cs{tl_if_single_token:n} for an example.
-% The branching versions leave either \meta{true code} or
-% \meta{false code} in the input stream, as appropriate to the truth
-% of the test and the variant of the function chosen. The logical
-% truth of the test is left in the input stream by the predicate.
+% The \cs{tl_expandable_uppercase:n} function works through all of
+% the \meta{tokens}, replacing characters in the range |a|--|z|
+% (with arbitrary category code) by the corresponding letter
+% in the range |A|--|Z|, with category code $11$ (letter). Similarly,
+% \cs{tl_expandable_lowercase:n} replaces characters in the range
+% |A|--|Z| by letters in the range |a|--|z|, and leaves other tokens
+% unchanged. This function requires two steps of expansion.
% \begin{texnote}
-% When \TeX{}'s reads a character of category code $10$ for the
-% first time, it is converted to an explicit space token, with
-% character code $32$, regardless of the initial character code.
-% ``Funny'' spaces with a different category code, can be produced
-% using \cs{tex_lowercase:D}. Explicit spaces are also produced
-% as a result of \cs{token_to_str:N}, \cs{tl_to_str:n}, etc.
+% Begin-group and end-group characters are normalized and become
+% |{| and |}|, respectively.
% \end{texnote}
% \end{function}
-%
-% \begin{function}[EXP,pTF]{\tl_if_single_token:n}
-% \begin{syntax}
-% \cs{tl_if_single_token_p:n} \Arg{token list}
-% \cs{tl_if_single_token:nTF} \Arg{token list}
-% ~~\Arg{true code} \Arg{false code}
-% \end{syntax}
-% Tests if the token list consists of exactly one token, i.e.,
-% is either a single space character or a single \enquote{normal} token.
-% Token groups are not single tokens. The branching
-% versions leaves either \meta{true code} or \meta{false code}
-% in the input stream, as appropriate to the truth of the test and
-% the variant of the function chosen. The logical truth of the test
-% is left in the input stream by the predicate.
-% \end{function}
+%
+% \section{Internal functions}
+%
+% \begin{variable}{\q_tl_act_mark,\q_tl_act_stop}
+% Quarks which are only used for the particular purposes of
+% \cs{tl_act_...} functions.
+% \end{variable}
%
% \end{documentation}
%
@@ -1302,8 +1406,10 @@
% within the token list which is scanned: two |@| symbols with different
% category codes. The rescanned token list cannot contain the end marker,
% because all |@| present in the token list are read with the same category
-% code. The setting of \cs{tex_endlinechar:D}
-% is needed to avoid introducing an extraneous space in the result.
+% code. As every character with charcode \cs{tex_newlinechar:D} is replaced
+% by the \cs{tex_endlinechar:D}, and an extra \cs{tex_endlinechar:D} is
+% added at the end, we need to set both of those to $-1$,
+% \enquote{unprintable}.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \tl_set_rescan:Nnn
{ \tl_set_rescan_aux:NNnn \tl_set:Nn }
@@ -1314,6 +1420,7 @@
\group_begin:
\exp_args:No \etex_everyeof:D { \c_tl_rescan_marker_tl }
\tex_endlinechar:D \c_minus_one
+ \tex_newlinechar:D \c_minus_one
#3
\tl_clear:N \l_tl_rescan_tl
\exp_after:wN \tl_rescan_aux:w \etex_scantokens:D {#4}
@@ -1352,6 +1459,7 @@
\group_begin:
\etex_everyeof:D { \exp_not:N }
\tex_endlinechar:D \c_minus_one
+ \tex_newlinechar:D \c_minus_one
#3
\tl_set:Nx \l_tl_rescan_tl { \etex_scantokens:D {#4} }
\exp_args:NNNo \group_end:
@@ -1372,6 +1480,7 @@
\group_begin:
\exp_args:No \etex_everyeof:D { \c_tl_rescan_marker_tl }
\tex_endlinechar:D \c_minus_one
+ \tex_newlinechar:D \c_minus_one
#1
\exp_after:wN \tl_rescan_aux:w \etex_scantokens:D {#2}
\exp_args:No \group_end:
@@ -1401,86 +1510,106 @@
% \end{macrocode}
% \end{variable}
%
+%
+% \begin{macro}{\tl_replace_all:Nnn, \tl_replace_all:cnn}
+% \begin{macro}{\tl_greplace_all:Nnn, \tl_greplace_all:cnn}
% \begin{macro}{\tl_replace_once:Nnn, \tl_replace_once:cnn}
% \begin{macro}{\tl_greplace_once:Nnn, \tl_greplace_once:cnn}
-% \begin{macro}[aux]{\tl_replace_once_aux:NNnn}
-% The concept here is that only the first occurrence should be
-% replaced. The first step is to define an auxiliary which will
-% match the appropriate item, with a trailing marker. If the last token
-% is the marker there is nothing to do, otherwise replace the token
-% and clean up (hence the second use of \cs{tl_tmp:w}). To prevent
-% loosing braces or spaces there are a couple of empty groups and
-% the strange-looking \cs{use:n}. There is a \cs{q_nil} between the
-% original and the search input, to prevent cases where
-% the end of the original and the start of the search run together
-% to give an erroneous test result.
+% \begin{macro}[aux]{\tl_replace_aux:NNNnn, \tl_replace_aux_ii:w}
+% \begin{macro}[aux]{\tl_replace_all_aux:, \tl_replace_once_aux:}
+% \begin{macro}[aux]{\tl_replace_once_aux_end:w}
+% All of the replace functions are based on \cs{tl_replace_aux:NNNnn},
+% whose arguments are: \meta{function}, \cs{tl_(g)set:Nx}, \meta{tl~var},
+% \meta{search tokens}, \meta{replacement tokens}.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \tl_replace_once:Nnn
- { \tl_replace_once_aux:NNnn \tl_set_eq:NN }
+ { \tl_replace_aux:NNNnn \tl_replace_once_aux: \tl_set:Nx }
\cs_new_protected_nopar:Npn \tl_greplace_once:Nnn
- { \tl_replace_once_aux:NNnn \tl_gset_eq:NN }
-\cs_new_protected:Npn \tl_replace_once_aux:NNnn #1#2#3#4
+ { \tl_replace_aux:NNNnn \tl_replace_once_aux: \tl_gset:Nx }
+\cs_new_protected_nopar:Npn \tl_replace_all:Nnn
+ { \tl_replace_aux:NNNnn \tl_replace_all_aux: \tl_set:Nx }
+\cs_new_protected_nopar:Npn \tl_greplace_all:Nnn
+ { \tl_replace_aux:NNNnn \tl_replace_all_aux: \tl_gset:Nx }
+\cs_generate_variant:Nn \tl_replace_once:Nnn { c }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn { c }
+\cs_generate_variant:Nn \tl_replace_all:Nnn { c }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn { c }
+% \end{macrocode}
+% The idea is easier to understand by considering the case of
+% \cs{tl_replace_all:Nnn}. The replacement happens within an
+% \texttt{x}-type expansion. We use an auxiliary function \cs{tl_tmp:w},
+% which essentially replaces the next \meta{search tokens} by
+% \meta{replacement tokens}. To avoid runaway arguments,
+% we expand something like \cs{tl_tmp:w} \meta{token list} \cs{q_mark}
+% \meta{search tokens} \cs{q_stop}, repeating until the end. How do we
+% detect that we have reached the last occurrence of \meta{search tokens}?
+% The last replacement is characterized by the fact that the argument of
+% \cs{tl_tmp:w} contains \cs{q_mark}. In the code below,
+% \cs{tl_replace_aux_ii:w} takes an argument delimited by \cs{q_mark},
+% and removes the following token. Before we reach the end, this gobbles
+% \cs{q_mark} \cs{use_none_delimit_by_q_stop:w} which appear in the
+% definition of \cs{tl_tmp:w}, and leaves the \meta{replacement tokens},
+% passed to \cs{exp_not:n}, to be included in the \texttt{x}-expanding
+% definition. At the end, the first \cs{q_mark} is within the argument
+% of \cs{tl_tmp:w}, and \cs{tl_replace_aux_ii:w} gobbles the second
+% \cs{q_mark} as well, leaving \cs{use_none_delimit_by_q_stop:w},
+% which ends the recursion cleanly.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_replace_aux:NNNnn #1#2#3#4#5
{
- \cs_set_protected:Npx \tl_tmp:w ##1 #3 ##2 \q_stop
+ \tl_if_empty:nTF {#4}
{
- \exp_not:N \quark_if_no_value:nF {##2}
+ \msg_kernel_error:nnx { tl } { empty-search-pattern }
+ { \tl_to_str:n {#5} }
+ }
+ {
+ \cs_set:Npx \tl_tmp:w ##1##2 #4
+ {
+ ##2
+ \exp_not:N \q_mark
+ \exp_not:N \use_none_delimit_by_q_stop:w
+ \exp_not:n { \exp_not:n {#5} }
+ ##1
+ }
+ #2 #3
{
- \tl_set:No \exp_not:N \l_tl_replace_tl { ##1 \exp_not:n{#4} }
- \exp_not:n
- {
- \cs_set_protected:Npn \tl_tmp:w ##1 \q_nil #3 \q_no_value
- { \tl_put_right:No \l_tl_replace_tl {##1} }
- }
- \exp_not:n { \tl_tmp:w \prg_do_nothing: } ##2
- \exp_not:n { #1 #2 \l_tl_replace_tl }
+ \exp_after:wN #1
+ #3 \q_mark #4 \q_stop
}
}
- \exp_after:wN \tl_tmp:w \exp_after:wN \prg_do_nothing:
- #2 \q_nil #3 \q_no_value \q_stop
}
-\cs_generate_variant:Nn \tl_replace_once:Nnn { c }
-\cs_generate_variant:Nn \tl_greplace_once:Nnn { c }
+\cs_new:Npn \tl_replace_aux_ii:w #1 \q_mark #2 { \exp_not:o {#1} }
+% \end{macrocode}
+% The first argument of \cs{tl_tmp:w} is responsible for repeating
+% the replacement in the case of \texttt{replace_all}, and stopping
+% it early for \texttt{replace_once}. Note also that we build
+% \cs{tl_tmp:w} within an \texttt{x}-expansion so that the
+% \meta{replacement tokens} can contain |#|. The second
+% \cs{exp_not:n} ensures that the \meta{replacement tokens}
+% are not expanded by \cs{tl_(g)set:Nx}.
+%
+% Now on to the difference between \enquote{once} and \enquote{all}.
+% The \cs{prg_do_nothing:} and accompanying \texttt{o}-expansion
+% ensure that we don't lose braces in case the tokens between two
+% occurrences of the \meta{search tokens} form a brace group.
+% \begin{macrocode}
+\cs_new:Npn \tl_replace_all_aux:
+ {
+ \exp_after:wN \tl_replace_aux_ii:w
+ \tl_tmp:w \tl_replace_all_aux: \prg_do_nothing:
+ }
+\cs_new_nopar:Npn \tl_replace_once_aux:
+ {
+ \exp_after:wN \tl_replace_aux_ii:w
+ \tl_tmp:w { \tl_replace_once_aux_end:w \prg_do_nothing: } \prg_do_nothing:
+ }
+\cs_new:Npn \tl_replace_once_aux_end:w #1 \q_mark #2 \q_stop
+ { \exp_not:o {#1} }
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
-%
-% \begin{macro}{\tl_replace_all:Nnn, \tl_replace_all:cnn}
-% \begin{macro}{\tl_greplace_all:Nnn, \tl_greplace_all:cnn}
-% \begin{macro}[aux]{\tl_replace_all_aux:NNnn}
-% A similar approach here but with a loop built in.
-% \begin{macrocode}
-\cs_new_protected_nopar:Npn \tl_replace_all:Nnn
- { \tl_replace_all_aux:NNnn \tl_set_eq:NN }
-\cs_new_protected_nopar:Npn \tl_greplace_all:Nnn
- { \tl_replace_all_aux:NNnn \tl_gset_eq:NN }
-\cs_new_protected:Npn \tl_replace_all_aux:NNnn #1#2#3#4
- {
- \tl_clear:N \l_tl_replace_tl
- \cs_set_protected:Npx \tl_tmp:w ##1 #3 ##2 \q_stop
- {
- \exp_not:N \quark_if_no_value:nTF {##2}
- {
- \exp_not:n
- {
- \cs_set_protected:Npn \tl_tmp:w ##1 \q_nil ##2 \q_stop
- { \tl_put_right:No \l_tl_replace_tl {##1} }
- }
- \exp_not:N \tl_tmp:w ##1 \exp_not:N \q_stop
- }
- {
- \exp_not:n { \tl_put_right:No \l_tl_replace_tl }
- { ##1 \exp_not:n{#4} }
- \exp_not:n { \tl_tmp:w \prg_do_nothing: } ##2 \exp_not:N \q_stop
- }
- }
- \exp_after:wN \tl_tmp:w \exp_after:wN \prg_do_nothing:
- #2 \q_nil #3 \q_no_value \q_stop
- #1 #2 \l_tl_replace_tl
- }
-\cs_generate_variant:Nn \tl_replace_all:Nnn { c }
-\cs_generate_variant:Nn \tl_greplace_all:Nnn { c }
-% \end{macrocode}
+% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
@@ -1575,13 +1704,14 @@
% that. This way the test will even accept |\q_nil| as the first
% token.
% \begin{macrocode}
-\prg_new_conditional:Npnn \tl_if_empty:n #1 { p,TF,T,F} {
- \exp_after:wN \if_meaning:w \exp_after:wN \q_nil \tl_to_str:n {#1} \q_nil
- \prg_return_true:
- \else:
- \prg_return_false:
- \fi:
-}
+\prg_new_conditional:Npnn \tl_if_empty:n #1 { p , TF , T , F }
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil \tl_to_str:n {#1} \q_nil
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
\cs_generate_variant:Nn \tl_if_empty_p:n { V }
\cs_generate_variant:Nn \tl_if_empty:nTF { V }
\cs_generate_variant:Nn \tl_if_empty:nT { V }
@@ -1595,27 +1725,28 @@
% in conditionals on token lists, which mostly reduce to testing
% if a given token list is empty after applying a simple function
% to it.
-% The test for emptyness is based on \cs{tl_if_empty:n(TF)}, but
+% The test for emptiness is based on \cs{tl_if_empty:n(TF)}, but
% the expansion is hard-coded for efficiency, as this auxiliary
% function is used in many places.
% Note that this works because \cs{tl_to_str:n} expands tokens
% that follow until reading a catcode $1$ (begin-group) token.
% \begin{macrocode}
-\cs_new:Npn \tl_if_empty_return:o #1 {
- \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
- \tl_to_str:n \exp_after:wN {#1} \q_nil
- \prg_return_true:
- \else:
- \prg_return_false:
- \fi:
-}
-\prg_new_conditional:Npnn \tl_if_empty:o #1 { p,TF,T,F}
+\cs_new:Npn \tl_if_empty_return:o #1
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
+ \tl_to_str:n \exp_after:wN {#1} \q_nil
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F }
{ \tl_if_empty_return:o {#1} }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \begin{macro}[pTF]{\tl_if_eq:NN,\tl_if_eq:Nc,\tl_if_eq:cN,\tl_if_eq:cc}
+% \begin{macro}[pTF]{\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc}
% Returns \cs{c_true_bool} if and only if the two token list variables are
% equal.
% \begin{macrocode}
@@ -1703,30 +1834,6 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[pTF]{\tl_if_single:n}
-% \begin{macro}[aux]{\tl_if_single_aux:n}
-% If the argument is a single token, or a single brace group
-% preceeded by optional (explicit) spaces, or a non-zero number
-% of spaces.
-% \begin{macrocode}
-\prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF }
- {
- \tl_if_blank:nTF {#1}
- { \tl_if_empty:nTF {#1} { \prg_return_false: } { \prg_return_true: } }
- { \tl_if_single_aux:n {#1} }
- }
-\prg_new_conditional:Npnn \tl_if_single:N #1 { p , T , F , TF }
- {
- \tl_if_blank:oTF #1
- { \tl_if_empty:NTF #1 { \prg_return_false: } { \prg_return_true: } }
- { \exp_args:No \tl_if_single_aux:n {#1} }
- }
-\cs_new:Npn \tl_if_single_aux:n #1
- { \tl_if_empty_return:o { \use_none:n #1 } }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
% \subsection{Mapping to token lists}
%
% \begin{macro}{\tl_map_function:nN}
@@ -1851,14 +1958,9 @@
\if_cs_exist:w #1 \cs_end:
\cs:w #1 \exp_after:wN \cs_end:
\else:
- \exp_after:wN \tl_error_message:
+ \msg_expandable_error:n { Undefined~variable~name~'#1'! }
\fi:
}
-\group_begin:
-\tex_catcode:D `\! = 11 \scan_stop:
-\tex_catcode:D `\ = 11\scan_stop:%
-\cs_gset_nopar:Npn\tl_error_message:{\undefined variable name!}%
-\group_end:%
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -1892,33 +1994,23 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\tl_reverse:n, \tl_reverse:V, \tl_reverse:o}
-% \begin{macro}[aux]{\tl_reverse_aux:nN}
-% Reversal of a token list is done by taking one token at a time
-% and putting it in front of the ones before it.
+% \begin{macro}{\tl_reverse_items:n}
+% \begin{macro}[aux]{\tl_reverse_items_aux:nN}
+% Reversal of a token list is done by taking one item at a time
+% and putting it after \cs{q_recursion_stop}.
% \begin{macrocode}
-\cs_new:Npn \tl_reverse:n #1
- { \tl_reverse_aux:nN { } #1 \q_recursion_tail \q_recursion_stop }
-\cs_new:Npn \tl_reverse_aux:nN #1#2
+\cs_new:Npn \tl_reverse_items:n #1
+ { \tl_reverse_items_aux:nw #1 \q_recursion_tail \q_recursion_stop }
+\cs_new:Npn \tl_reverse_items_aux:nw #1 #2 \q_recursion_stop
{
- \quark_if_recursion_tail_stop_do:nn {#2} {#1}
- \tl_reverse_aux:nN { #2 #1 }
+ \quark_if_recursion_tail_stop_do:nn {#1} { \use_none:n }
+ \tl_reverse_items_aux:nw #2 \q_recursion_stop
+ {#1}
}
-\cs_generate_variant:Nn \tl_reverse:n {V,o}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\tl_reverse:N, \tl_reverse:c}
-% This reverses the list, leaving |\exp_stop_f:| in front, which in turn
-% is removed by the |f| expansion which comes to a halt.
-% \begin{macrocode}
-\cs_new_protected_nopar:Npn \tl_reverse:N #1
- { \tl_set:Nf #1 { \tl_reverse:o { #1 \exp_stop_f: } } }
-\cs_generate_variant:Nn \tl_reverse:N { c }
-% \end{macrocode}
-% \end{macro}
-%
% \begin{macro}{\tl_trim_spaces:n}
% \begin{macro}
% {
@@ -1966,15 +2058,16 @@
% \begin{macro}{\tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f}
% \begin{macro}{\tl_tail:w}
% These functions pick up either the head or the tail of a list. The
-% \texttt{n}-type implementation needs a test in order to avoid run-away
-% arguments occurring with blank input.
+% empty brace groups in \cs{tl_head:n} and \cs{tl_tail:n} ensure that
+% a blank argument gives an empty result.
% \begin{macrocode}
\cs_new:Npn \tl_head:w #1#2 \q_stop {#1}
\cs_new:Npn \tl_tail:w #1#2 \q_stop {#2}
\cs_new:Npn \tl_head:n #1
- { \tl_if_blank:nF {#1} { \tl_head:w #1 \q_stop } }
+ { \tl_head:w #1 { } \q_stop }
\cs_new:Npn \tl_tail:n #1
- { \tl_if_blank:nF {#1} { \tl_tail:w #1 \q_stop } }
+ { \tl_tail_aux:w #1 \q_mark { } \q_mark \q_stop }
+\cs_new:Npn \tl_tail_aux:w #1 #2 \q_mark #3 \q_stop { #2 }
\cs_generate_variant:Nn \tl_head:n { V , v , f }
\cs_generate_variant:Nn \tl_tail:n { V , v , f }
% \end{macrocode}
@@ -1983,7 +2076,7 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\str_head:n}
+% \begin{macro}{\str_head:n, \str_tail:n}
% \begin{macro}[aux]{\str_head_aux:w}
% After \cs{tl_to_str:n}, we have a list of character tokens,
% all with category code 12, except the space, which has category
@@ -1993,17 +2086,34 @@
% space, then the argument of \cs{str_head_aux:w} is empty, and
% the function correctly returns a space character. Otherwise,
% it returns the first token of |#1|, which is the first token
-% of the string. If the string is empty, we return
-% \cs{scan_stop:}.
+% of the string. If the string is empty, we return an empty result.
+%
+% To remove the first character of \cs{tl_to_str:n} |{#1}|,
+% we test it using \cs{if_charcode:w} \cs{scan_stop:},
+% always false for characters. If the argument was non-empty,
+% then \cs{str_tail_aux:w} returns everything until the first
+% \texttt{X} (with category code letter, no risk of confusing
+% with the user input). If the argument was empty, the first
+% \texttt{X} is taken by \cs{if_charcode:w}, and nothing
+% is returned. We use \texttt{X} as a \meta{marker}, rather than
+% a quark because the test \cs{if_charcode:w} \cs{scan_stop:}
+% \meta{marker} has to be false.
% \begin{macrocode}
\cs_new:Npn \str_head:n #1
{
\exp_after:wN \str_head_aux:w
\tl_to_str:n {#1}
- { \scan_stop: } ~ \q_stop
+ { { } } ~ \q_stop
}
\cs_new_nopar:Npn \str_head_aux:w #1 ~ %
{ \tl_head:w #1 { ~ } }
+\cs_new:Npn \str_tail:n #1
+ {
+ \exp_after:wN \str_tail_aux:w
+ \reverse_if:N \if_charcode:w
+ \scan_stop: \tl_to_str:n {#1} X X \q_stop
+ }
+\cs_new_nopar:Npn \str_tail_aux:w #1 X #2 \q_stop { \fi: #1 }
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -2013,8 +2123,8 @@
% \begin{macro}[pTF]{\tl_if_head_eq_charcode:fN}
% \begin{macro}[pTF]{\tl_if_head_eq_catcode:nN}
% Accessing the first token of a token list is tricky in two cases:
-% when it has category code 1 (begin-group token), or when it is
-% an explicit space, with category code 10 and character code 32.
+% when it has category code $1$ (begin-group token), or when it is
+% an explicit space, with category code $10$ and character code $32$.
%
% Forgetting temporarily about this issue we would use the
% following test in \cs{tl_if_head_eq_charcode:nN}. Here,
@@ -2025,8 +2135,8 @@
% \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \q_stop
% \exp_not:N #2
% \end{verbatim}
-% The special cases are detected by comparing |#1| with
-% \cs{use:n}~|#1| (the extra |?| takes care of empty arguments).
+% The special cases are detected using \cs{tl_if_head_N_type:n}
+% (the extra |?| takes care of empty arguments).
% In those cases, the first token is a character, and
% since we only care about its character code, we can
% use \cs{str_head:n} to access it (this works even if
@@ -2036,7 +2146,7 @@
{
\if_charcode:w
\exp_not:N #2
- \str_if_eq:onTF { \use:n #1 ? } { #1 ? }
+ \tl_if_head_N_type:nTF { #1 ? }
{ \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \q_stop }
{ \str_head:n {#1} }
\prg_return_true:
@@ -2050,7 +2160,7 @@
\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNF { f }
% \end{macrocode}
% For \cs{tl_if_head_eq_catcode:nN}, again we detect special
-% cases with a \cs{str_if_eq:onTF} test. Then we need to test
+% cases with a \cs{tl_if_head_N_type}. Then we need to test
% if the first token is a begin-group token or an explicit
% space token, and produce the relevant token, either
% \cs{c_group_begin_token} or \cs{c_space_token}.
@@ -2059,10 +2169,10 @@
{
\if_catcode:w
\exp_not:N #2
- \str_if_eq:onTF { \use:n #1 ? } { #1 ? }
+ \tl_if_head_N_type:nTF { #1 ? }
{ \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \q_stop }
{
- \tl_if_head_begin_group:nTF {#1}
+ \tl_if_head_group:nTF {#1}
{ \c_group_begin_token }
{ \c_space_token }
}
@@ -2081,7 +2191,7 @@
% \begin{macrocode}
\prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 { p , T , F , TF }
{
- \str_if_eq:onTF { \use:n #1 ? } { #1 ? }
+ \tl_if_head_N_type:nTF { #1 ? }
{ \tl_if_head_eq_meaning_aux_normal:nN }
{ \tl_if_head_eq_meaning_aux_special:nN }
{#1} #2
@@ -2104,7 +2214,7 @@
\fi:
{
\if_catcode:w \exp_not:N #2
- \tl_if_head_begin_group:nTF {#1}
+ \tl_if_head_group:nTF {#1}
{ \c_group_begin_token }
{ \c_space_token }
\prg_return_true:
@@ -2119,6 +2229,78 @@
% \end{macro}
% \end{macro}
%
+% \begin{macro}[pTF]{\tl_if_head_N_type:n}
+% The first token of a token list can be either an N-type argument,
+% a begin-group token (catcode 1), or an explicit space token
+% (catcode 10 and charcode 32). These two cases are characterized
+% by the fact that \cs{use:n} removes some tokens from |#1|, hence
+% changing its string representation (no token can have an empty
+% string representation). The extra brace group covers the case of
+% an empty argument, whose head is not \enquote{normal}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_N_type:n #1 { p , T , F , TF }
+ { \str_if_eq_return:on { \use:n #1 { } } { #1 { } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_head_group:n}
+% Pass the first token of |#1| through \cs{token_to_str:N},
+% then check for the brace balance. The extra \texttt{?}
+% caters for an empty argument.\footnote{Bruno: this could
+% be made faster, but we don't: if we hope to ever have
+% an e-type argument, we need all brace \enquote{tricks}
+% to happen in one step of expansion, keeping the token
+% list brace balanced at all times.}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_group:n #1 { p , T , F , TF }
+ {
+ \if_predicate:w
+ \exp_after:wN \use_none:n
+ \exp_after:wN {
+ \exp_after:wN {
+ \token_to_str:N #1 ?
+ }
+ \c_false_bool
+ }
+ \c_true_bool
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_head_space:n}
+% \begin{macro}[EXP,aux]{\tl_if_head_space_aux:w}
+% If the first token of the token list is an explicit space, i.e.,
+% a character token with character code $32$ and category code $10$,
+% then this test will be \meta{true}. It is \meta{false} if the token
+% list is empty, if the first token is an implicit space token,
+% such as \cs{c_space_token}, or any token other than an explicit space.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_space:n #1 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \pdftex_strcmp:D
+ { }
+ { \tl_if_head_space_aux:w \prg_do_nothing: #1 ? ~ }
+ = \c_zero
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \tl_if_head_space_aux:w #1 ~ %
+ {
+ \exp_not:o {#1}
+ \if_false: { \fi: }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Viewing token lists}
%
% \begin{macro}{\tl_show:N, \tl_show:c}
@@ -2142,7 +2324,9 @@
% \begin{variable}{\c_job_name_tl}
% Inherited from the \LaTeX3 name for the primitive: this needs to
% actually contain the text of the job name rather than the name of
-% the primitive, of course.
+% the primitive, of course. \LuaTeX{} does not quote file names containing
+% spaces, whereas \pdfTeX{} and \XeTeX{} do. So there may be a correction to
+% make in the \LuaTeX{} case.
% \begin{macrocode}
%<*package>
\tl_const:Nx \c_job_name_tl { \tex_jobname:D }
@@ -2202,7 +2386,36 @@
%
% \subsection{Experimental functions}
%
-% \begin{macro}[EXP,pTF]{\tl_if_single_item:n}
+% \begin{macro}[EXP,pTF]{\str_if_eq_return:on}
+% It turns out that we often need to compare a token list
+% with the result of applying some function to it, and
+% return with \cs{prg_return_true/false:}. This test is
+% similar to \cs{str_if_eq:nnTF}, but hard-coded for speed.
+% \begin{macrocode}
+\cs_new:Npn \str_if_eq_return:on #1 #2
+ {
+ \if_int_compare:w
+ \pdftex_strcmp:D { \exp_not:o {#1} } { \exp_not:n {#2} }
+ = \c_zero
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:N}
+% Expand the token list and feed it to \cs{tl_if_single:n}.
+% \begin{macrocode}
+\cs_new:Npn \tl_if_single_p:N { \exp_args:No \tl_if_single_p:n }
+\cs_new:Npn \tl_if_single:NT { \exp_args:No \tl_if_single:nT }
+\cs_new:Npn \tl_if_single:NF { \exp_args:No \tl_if_single:nF }
+\cs_new:Npn \tl_if_single:NTF { \exp_args:No \tl_if_single:nTF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:n}
% A token list has exactly one item if it is either a single
% token surrounded by optional explicit spaces, or a single brace
% group surrounded by optional explicit spaces. The naive
@@ -2211,82 +2424,288 @@
% the token list is empty. Furthermore, it does not allow optional
% trailing spaces.
% \begin{macrocode}
-\cs_new:Npn \tl_if_single_item:nTF #1
- { \str_if_eq:onTF { \use_ii:nn #1 {?} ? } {??} }
-\cs_new:Npn \tl_if_single_item:nT #1
- { \str_if_eq:onT { \use_ii:nn #1 {?} ? } {??} }
-\cs_new:Npn \tl_if_single_item:nF #1
- { \str_if_eq:onF { \use_ii:nn #1 {?} ? } {??} }
-\cs_new:Npn \tl_if_single_item_p:n #1
- { \str_if_eq_p:on { \use_ii:nn #1 {?} ? } {??} }
+\prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF }
+ { \str_if_eq_return:on { \use_none:nn #1 ?? } {?} }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[EXP,pTF]{\tl_if_head_begin_group:n}
+% \begin{macro}[EXP,pTF]{\tl_if_single_token:n}
+% There are four cases: empty token list, token list starting with
+% a normal token, with a brace group, or with a space token.
+% If the token list starts with a normal token, remove it
+% and check for emptyness. Otherwise, compare with a single
+% space, only case where we have a single token.
% \begin{macrocode}
-\prg_new_conditional:Npnn \tl_if_head_begin_group:n #1 { p , T , F , TF }
+\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF }
+ {
+ \tl_if_head_N_type:nTF {#1}
+ { \str_if_eq_return:on { \use_none:n #1 } { } }
+ { \str_if_eq_return:on { ~ } { #1 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_tl_act_mark,\q_tl_act_stop}
+% The \cs{tl_act} functions may be applied to any token list.
+% Hence, we use two private quarks, to allow any token, even quarks,
+% in the token list.^^A in particular critical for future \::e.
+% Only \cs{q_tl_act_mark} and \cs{q_tl_act_stop} may not appear
+% in the token lists manipulated by \cs{tl_act} functions. The quarks
+% are effectively defined in \pkg{l3quark}.
+% \end{variable}
+%
+% \begin{macro}[EXP]{\tl_act:NNNnn,\tl_act_aux:NNNnn}
+% \begin{macro}[EXP]{\tl_act_output:n,\tl_act_reverse_output:n,
+% \tl_act_group_recurse:Nnn}
+% \begin{macro}[EXP,aux]{\tl_act_loop:w,\tl_act_normal:NwnNNN,
+% \tl_act_group:nwnNNN,\tl_act_space:wwnNNN,\tl_act_end:w}
+% To help control the expansion, \cs{tl_act:NNNnn} starts with
+% \cs{tex_romannumeral:D} and ends by producing \cs{c_zero}
+% once the result has been obtained. Then loop over tokens,
+% groups, and spaces in |#5|. The marker \cs{q_tl_act_mark}
+% is used both to avoid losing outer braces and to detect the
+% end of the token list more easily. The result is stored
+% as an argument for the dummy function \cs{tl_act_result:n}.
+% \begin{macrocode}
+\cs_new:Npn \tl_act:NNNnn { \tex_romannumeral:D \tl_act_aux:NNNnn }
+\cs_new:Npn \tl_act_aux:NNNnn #1 #2 #3 #4 #5
{
- \exp_after:wN \use_none:n
- \exp_after:wN {
- \exp_after:wN {
- \token_to_str:N #1 .
- }
- \prg_return_true: \exp_after:wN \use_none:nn \token_to_str:N
+ \tl_act_loop:w #5 \q_tl_act_mark \q_tl_act_stop
+ {#4} #1 #2 #3
+ \tl_act_result:n { }
+ }
+% \end{macrocode}
+% In the loop, we check how the token list begins and act
+% accordingly. In the \enquote{normal} case, we may have
+% reached \cs{q_tl_act_mark}, the end of the list. Then
+% leave \cs{c_zero} and the result in the input stream,
+% to terminate the expansion of \cs{tex_romannumeral:D}.
+% Otherwise, apply the relevant function to the
+% \enquote{arguments}, |#3|
+% and to the head of the token list. Then repeat the loop.
+% The scheme is the same if the token list starts with a
+% group or with a space. Some extra work is needed to
+% make \cs{tl_act_space:wwnNNN} gobble the space.
+% \begin{macrocode}
+\cs_new:Npn \tl_act_loop:w #1 \q_tl_act_stop
+ {
+ \tl_if_head_N_type:nTF {#1}
+ { \tl_act_normal:NwnNNN }
+ {
+ \tl_if_head_group:nTF {#1}
+ { \tl_act_group:nwnNNN }
+ { \tl_act_space:wwnNNN }
}
- \prg_return_false:
+ #1 \q_tl_act_stop
}
+\cs_new:Npn \tl_act_normal:NwnNNN #1 #2 \q_tl_act_stop #3#4
+ {
+ \if_meaning:w \q_tl_act_mark #1
+ \exp_after:wN \tl_act_end:wn
+ \fi:
+ #4 {#3} #1
+ \tl_act_loop:w #2 \q_tl_act_stop
+ {#3} #4
+ }
+\cs_new:Npn \tl_act_end:wn #1 \tl_act_result:n #2 { \c_zero #2 }
+\cs_new:Npn \tl_act_group:nwnNNN #1 #2 \q_tl_act_stop #3#4#5
+ {
+ #5 {#3} {#1}
+ \tl_act_loop:w #2 \q_tl_act_stop
+ {#3} #4 #5
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \tl_act_space:wwnNNN \c_space_tl #1 \q_tl_act_stop #2#3#4#5
+ {
+ #5 {#2}
+ \tl_act_loop:w #1 \q_tl_act_stop
+ {#2} #3 #4 #5
+ }
+% \end{macrocode}
+% Typically, the output is done to the right of what was already output,
+% using \cs{tl_act_output:n}, but for the \cs{tl_act_reverse} functions,
+% it should be done to the left.
+% \begin{macrocode}
+\cs_new:Npn \tl_act_output:n #1 #2 \tl_act_result:n #3
+ { #2 \tl_act_result:n { #3 #1 } }
+\cs_new:Npn \tl_act_reverse_output:n #1 #2 \tl_act_result:n #3
+ { #2 \tl_act_result:n { #1 #3 } }
% \end{macrocode}
+% In many applications of \cs{tl_act:NNNnn}, we need to recursively
+% apply some transformation within brace groups, then output. In this
+% code, |#1| is the output function, |#2| is the transformation,
+% which should expand in two steps, and |#3| is the group.
+% \begin{macrocode}
+\cs_new:Npn \tl_act_group_recurse:Nnn #1#2#3
+ {
+ \exp_args:Nf #1
+ { \exp_after:wN \exp_after:wN \exp_after:wN { #2 {#3} } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
% \end{macro}
%
-% \begin{macro}[EXP,pTF]{\tl_if_head_eq_space:n}
-% \begin{macro}[EXP,aux]{\tl_if_head_eq_space_aux:w}
-% If the first token of the token list is an explicit space, i.e.,
-% a character token with character code $32$ and category code $10$,
-% then this test will be \meta{true}. It is \meta{false} if the token
-% list is empty, if the first token is an implicit space token,
-% such as \cs{c_space_token}, or any token other than an explicit space.
+% \begin{macro}[EXP]{\tl_reverse_tokens:n}
+% \begin{macro}[EXP,aux]{\tl_act_reverse_normal:nN,
+% \tl_act_reverse_group:nn, \tl_act_reverse_space:n}
+% The goal is to reverse a token list. This is done by feeding
+% \cs{tl_act_aux:NNNnn} three functions, an empty fourth argument
+% (we don't use it for \cs{tl_act_reverse_tokens:n}), and as
+% a fifth argument the token list to be reversed.
+% Spaces and normal tokens are output to the left of the current
+% output. For groups, we must recursively apply
+% \cs{tl_act_reverse_tokens:n} to the group, and output, still
+% on the left. Note that in all three cases, we throw one argument
+% away: this \meta{parameter} is where for instance the
+% upper/lowercasing action stores the information of whether it
+% is uppercasing or lowercasing.
% \begin{macrocode}
-\prg_new_conditional:Npnn \tl_if_head_eq_space:n #1 { p , T , F , TF }
+\cs_new:Npn \tl_reverse_tokens:n
{
- \if_false: { \fi:
- \tl_if_head_eq_space_aux:w \prg_do_nothing: #1 ? ~ }
+ \tex_romannumeral:D
+ \tl_act_aux:NNNnn
+ \tl_act_reverse_normal:nN
+ \tl_act_reverse_group:nn
+ \tl_act_reverse_space:n
+ { }
}
-\cs_new:Npn \tl_if_head_eq_space_aux:w #1 ~ %
+\cs_new:Npn \tl_act_reverse_space:n #1
+ { \tl_act_reverse_output:n {~} }
+\cs_new:Npn \tl_act_reverse_normal:nN #1 #2
+ { \tl_act_reverse_output:n {#2} }
+\cs_new:Npn \tl_act_reverse_group:nn #1
{
- \tl_if_empty_return:o { #1 }
- \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ \tl_act_group_recurse:Nnn
+ \tl_act_reverse_output:n
+ { \tl_reverse_tokens:n }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
-%
-% \begin{macro}[EXP,pTF]{\tl_if_single_token:n}
-% If the token list starts with a space, then it is
-% a single token if and only if its string representation
-% is exactly one space character. Otherwise, we check if removing
-% the first token group makes the token list empty. If it doesn't,
-% the token list is not a single token. If it does, then the token
-% list is either a single token or a single brace group. This is
-% tested for using \cs{tl_if_head_begin_group:n}.
+%
+% \begin{macro}[EXP]{\tl_reverse:n,\tl_reverse:o,\tl_reverse:V}
+% \begin{macro}[EXP,aux]{\tl_reverse_group_preserve:nn}
+% The goal here is to reverse without losing spaces nor braces.
+% The only difference with \cs{tl_reverse_tokens:n} is that
+% we now simply output groups without entering them.
% \begin{macrocode}
-\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF }
+\cs_new:Npn \tl_reverse:n
+ {
+ \tex_romannumeral:D
+ \tl_act_aux:NNNnn
+ \tl_act_reverse_normal:nN
+ \tl_act_reverse_group_preserve:nn
+ \tl_act_reverse_space:n
+ { }
+ }
+\cs_new:Npn \tl_act_reverse_group_preserve:nn #1 #2
+ { \tl_act_reverse_output:n { {#2} } }
+\cs_generate_variant:Nn \tl_reverse:n { o , V }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_reverse:N, \tl_reverse:c}
+% This reverses the list, leaving |{}| in front, which in turn
+% is removed by the \cs{etex_unexpanded:D} primitive.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \tl_reverse:N #1
+ { \tl_set:No #1 { \etex_unexpanded:D \tl_reverse:o { #1 { } } } }
+\cs_generate_variant:Nn \tl_reverse:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_length_tokens:n}
+% \begin{macro}[EXP,aux]{\tl_act_length_normal:nN,
+% \tl_act_length_group:nn,\tl_act_length_space:n}
+% The length is computed through an \cs{int_eval:n} construction.
+% Each \texttt{1+} is output to the \emph{left}, into the integer
+% expression, and the sum is ended by the \cs{c_zero} inserted by
+% \cs{tl_act_end:wn}. Somewhat a hack.
+% \begin{macrocode}
+\cs_new:Npn \tl_length_tokens:n #1
{
- \tl_if_head_eq_space:nTF {#1}
+ \int_eval:n
{
- \str_if_eq:nnTF {#1} {~}
- \prg_return_true: \prg_return_false:
+ \tl_act_aux:NNNnn
+ \tl_act_length_normal:nN
+ \tl_act_length_group:nn
+ \tl_act_length_space:n
+ { }
+ {#1}
}
+ }
+\cs_new:Npn \tl_act_length_normal:nN #1 #2 { 1 + }
+\cs_new:Npn \tl_act_length_space:n #1 { 1 + }
+\cs_new:Npn \tl_act_length_group:nn #1 #2
+ { 2 + \tl_length_tokens:n {#2} + }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_tl_act_uppercase_tl, \c_tl_act_lowercase_tl}
+% These constants contain the correspondance between lowercase
+% and uppercase letters, in the form |aAbBcC...| and |AaBbCc...|
+% respectively.
+% \begin{macrocode}
+\tl_const:Nn \c_tl_act_uppercase_tl
+ {
+ aA bB cC dD eE fF gG hH iI jJ kK lL mM
+ nN oO pP qQ rR sS tT uU vV wW xX yY zZ
+ }
+\tl_const:Nn \c_tl_act_lowercase_tl
+ {
+ Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm
+ Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\tl_expandable_uppercase:n,\tl_expandable_lowercase:n}
+% \begin{macro}[EXP,aux]{\tl_act_case_normal:nN,
+% \tl_act_case_group:nn,\tl_act_case_space:n}
+% The only difference between uppercasing and lowercasing is
+% the table of correspondance that is used. As for other
+% token list actions, we feed \cs{tl_act_aux:NNNnn} three
+% functions, and this time, we use the \meta{parameters}
+% argument to carry which case-changing we are applying.
+% A space is simply output. A normal token is compared
+% to each letter in the alphabet using \cs{str_if_eq:nn}
+% tests, and converted if necessary to upper/lowercase,
+% before being output. For a group, we must perform the
+% conversion within the group (the \cs{exp_after:wN} trigger
+% \cs{tex_romannumeral:D}, which expands fully to give the
+% converted group), then output.
+% \begin{macrocode}
+\cs_new:Npn \tl_expandable_uppercase:n
+ { \tex_romannumeral:D \tl_act_case_aux:nn { \c_tl_act_uppercase_tl } }
+\cs_new:Npn \tl_expandable_lowercase:n
+ { \tex_romannumeral:D \tl_act_case_aux:nn { \c_tl_act_lowercase_tl } }
+\cs_new:Npn \tl_act_case_aux:nn
+ {
+ \tl_act_aux:NNNnn
+ \tl_act_case_normal:nN
+ \tl_act_case_group:nn
+ \tl_act_case_space:n
+ }
+\cs_new:Npn \tl_act_case_space:n #1 { \tl_act_output:n {~} }
+\cs_new:Npn \tl_act_case_normal:nN #1 #2
+ {
+ \exp_args:Nf \tl_act_output:n
{
- \str_if_eq:onTF { \use_none:n #1 ? } {?}
- {
- \tl_if_head_begin_group:nTF {#1}
- \prg_return_false: \prg_return_true:
- }
- \prg_return_false:
+ \exp_args:NNo \prg_case_str:nnn #2 {#1}
+ { \exp_stop_f: #2 }
}
}
+\cs_new:Npn \tl_act_case_group:nn #1 #2
+ {
+ \exp_after:wN \tl_act_output:n \exp_after:wN
+ { \exp_after:wN { \tex_romannumeral:D \tl_act_case_aux:nn {#1} {#2} } }
+ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \subsection{Deprecated functions}
%