From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/l3kernel/l3tl.dtx | 3394 ++++++++++++++++++++++++++++++++ 1 file changed, 3394 insertions(+) create mode 100644 macros/latex/contrib/l3kernel/l3tl.dtx (limited to 'macros/latex/contrib/l3kernel/l3tl.dtx') diff --git a/macros/latex/contrib/l3kernel/l3tl.dtx b/macros/latex/contrib/l3kernel/l3tl.dtx new file mode 100644 index 0000000000..77444e6d13 --- /dev/null +++ b/macros/latex/contrib/l3kernel/l3tl.dtx @@ -0,0 +1,3394 @@ +% \iffalse meta-comment +% +%% File: l3tl.dtx +% +% Copyright (C) 1990-2019 The LaTeX3 Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3kernel bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \pkg{l3tl} package\\ Token lists^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2019-08-25} +% +% \maketitle +% +% \begin{documentation} +% +% \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: +% \begin{verbatim} +% \foo:n { a collection of \tokens } +% \end{verbatim} +% or may be stored in a so-called \enquote{token list variable}, which +% have the suffix \texttt{tl}: a token list variable can also be used as +% the argument to a function, for example +% \begin{verbatim} +% \foo:N \l_some_tl +% \end{verbatim} +% In both cases, functions are available to test and manipulate the lists +% of tokens, and these have the module prefix \texttt{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 \enquote{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 \enquote{items}, +% or a list of \enquote{tokens}. An item is whatever \cs{use:n} 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 \texttt{N} argument, +% or \verb*| |, |{|, or |}| (assuming normal \TeX{} category codes). +% Thus for example +% \begin{verbatim} +% { Hello } ~ world +% \end{verbatim} +% contains six items (\texttt{Hello}, \texttt{w}, \texttt{o}, \texttt{r}, +% \texttt{l} and \texttt{d}), but thirteen tokens (|{|, \texttt{H}, \texttt{e}, +% \texttt{l}, \texttt{l}, \texttt{o}, |}|, \verb*| |, \texttt{w}, \texttt{o}, +% \texttt{r}, \texttt{l} and \texttt{d}). +% Functions which act on items are often faster than their analogue acting +% directly on tokens. +% +% \section{Creating and initialising token list variables} +% +% \begin{function}{\tl_new:N, \tl_new:c} +% \begin{syntax} +% \cs{tl_new:N} \meta{tl~var} +% \end{syntax} +% Creates a new \meta{tl~var} or raises an error if the +% name is already taken. The declaration is global. The +% \meta{tl~var} is initially empty. +% \end{function} +% +% \begin{function}{\tl_const:Nn, \tl_const:Nx, \tl_const:cn, \tl_const:cx} +% \begin{syntax} +% \cs{tl_const:Nn} \meta{tl~var} \Arg{token list} +% \end{syntax} +% Creates a new constant \meta{tl~var} or raises an error +% if the name is already taken. The value of the +% \meta{tl~var} is set globally to the \meta{token list}. +% \end{function} +% +% \begin{function}{\tl_clear:N, \tl_clear:c, \tl_gclear:N, \tl_gclear:c} +% \begin{syntax} +% \cs{tl_clear:N} \meta{tl~var} +% \end{syntax} +% Clears all entries from the \meta{tl~var}. +% \end{function} +% +% \begin{function} +% {\tl_clear_new:N, \tl_clear_new:c, \tl_gclear_new:N, \tl_gclear_new:c} +% \begin{syntax} +% \cs{tl_clear_new:N} \meta{tl~var} +% \end{syntax} +% Ensures that the \meta{tl~var} exists globally by applying +% \cs{tl_new:N} if necessary, then applies \cs[index=tl_clear:N]{tl_(g)clear:N} to leave +% the \meta{tl~var} empty. +% \end{function} +% +% \begin{function} +% { +% \tl_set_eq:NN, \tl_set_eq:cN, \tl_set_eq:Nc, \tl_set_eq:cc, +% \tl_gset_eq:NN, \tl_gset_eq:cN, \tl_gset_eq:Nc, \tl_gset_eq:cc +% } +% \begin{syntax} +% \cs{tl_set_eq:NN} \meta{tl~var_1} \meta{tl~var_2} +% \end{syntax} +% Sets the content of \meta{tl~var_1} equal to that of +% \meta{tl~var_2}. +% \end{function} +% +% \begin{function}[added = 2012-05-18] +% { +% \tl_concat:NNN, \tl_concat:ccc, +% \tl_gconcat:NNN, \tl_gconcat:ccc +% } +% \begin{syntax} +% \cs{tl_concat:NNN} \meta{tl~var_1} \meta{tl~var_2} \meta{tl~var_3} +% \end{syntax} +% Concatenates the content of \meta{tl~var_2} and \meta{tl~var_3} +% together and saves the result in \meta{tl~var_1}. The \meta{tl~var_2} +% is placed at the left side of the new token list. +% \end{function} +% +% \begin{function}[EXP, pTF, added=2012-03-03]{\tl_if_exist:N, \tl_if_exist:c} +% \begin{syntax} +% \cs{tl_if_exist_p:N} \meta{tl~var} +% \cs{tl_if_exist:NTF} \meta{tl~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{tl~var} is currently defined. This does not +% check that the \meta{tl~var} really is a token list variable. +% \end{function} +% +% \section{Adding data to token list variables} +% +% \begin{function} +% { +% \tl_set:Nn, \tl_set:NV, \tl_set:Nv, \tl_set:No, \tl_set:Nf, \tl_set:Nx, +% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:cf, \tl_set:cx, +% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv, +% \tl_gset:No, \tl_gset:Nf, \tl_gset:Nx, +% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv, +% \tl_gset:co, \tl_gset:cf, \tl_gset:cx +% } +% \begin{syntax} +% \cs{tl_set:Nn} \meta{tl~var} \Arg{tokens} +% \end{syntax} +% Sets \meta{tl~var} to contain \meta{tokens}, +% removing any previous content from the variable. +% \end{function} +% +% \begin{function} +% { +% \tl_put_left:Nn, \tl_put_left:NV, \tl_put_left:No, \tl_put_left:Nx, +% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:co, \tl_put_left:cx, +% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:No, \tl_gput_left:Nx, +% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:co, \tl_gput_left:cx +% } +% \begin{syntax} +% \cs{tl_put_left:Nn} \meta{tl~var} \Arg{tokens} +% \end{syntax} +% Appends \meta{tokens} to the left side of the current content of +% \meta{tl~var}. +% \end{function} +% +% \begin{function} +% { +% \tl_put_right:Nn, \tl_put_right:NV, \tl_put_right:No, \tl_put_right:Nx, +% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:co, \tl_put_right:cx, +% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:No, +% \tl_gput_right:Nx, +% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:co, +% \tl_gput_right:cx +% } +% \begin{syntax} +% \cs{tl_put_right:Nn} \meta{tl~var} \Arg{tokens} +% \end{syntax} +% Appends \meta{tokens} to the right side of the current content of +% \meta{tl~var}. +% \end{function} +% +% \section{Modifying token list variables} +% +% \begin{function}[updated = 2011-08-11] +% { +% \tl_replace_once:Nnn, \tl_replace_once:cnn, +% \tl_greplace_once:Nnn, \tl_greplace_once:cnn +% } +% \begin{syntax} +% \cs{tl_replace_once:Nnn} \meta{tl~var} \Arg{old tokens} \Arg{new tokens} +% \end{syntax} +% Replaces the first (leftmost) occurrence of \meta{old tokens} in the +% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens} +% cannot contain |{|, |}| or |#| +% (more precisely, explicit character tokens with category code $1$ +% (begin-group) or $2$ (end-group), and tokens with category code $6$). +% \end{function} +% +% \begin{function}[updated = 2011-08-11] +% { +% \tl_replace_all:Nnn, \tl_replace_all:cnn, +% \tl_greplace_all:Nnn, \tl_greplace_all:cnn +% } +% \begin{syntax} +% \cs{tl_replace_all:Nnn} \meta{tl~var} \Arg{old tokens} \Arg{new tokens} +% \end{syntax} +% Replaces all occurrences of \meta{old tokens} in the +% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens} +% cannot contain |{|, |}| 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 \meta{old tokens} +% may remain after the replacement (see \cs{tl_remove_all:Nn} +% for an example). +% \end{function} +% +% \begin{function}[updated = 2011-08-11] +% { +% \tl_remove_once:Nn, \tl_remove_once:cn, +% \tl_gremove_once:Nn, \tl_gremove_once:cn +% } +% \begin{syntax} +% \cs{tl_remove_once:Nn} \meta{tl~var} \Arg{tokens} +% \end{syntax} +% Removes the first (leftmost) occurrence of \meta{tokens} from the +% \meta{tl~var}. \meta{Tokens} cannot contain |{|, |}| or |#| +% (more precisely, explicit character tokens with category code $1$ +% (begin-group) or $2$ (end-group), and tokens with category code $6$). +% \end{function} +% +% \begin{function}[updated = 2011-08-11] +% { +% \tl_remove_all:Nn, \tl_remove_all:cn, +% \tl_gremove_all:Nn, \tl_gremove_all:cn +% } +% \begin{syntax} +% \cs{tl_remove_all:Nn} \meta{tl~var} \Arg{tokens} +% \end{syntax} +% Removes all occurrences of \meta{tokens} from the +% \meta{tl~var}. \meta{Tokens} 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 \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} +% results in \cs{l_tmpa_tl} containing \texttt{abcd}. +% \end{function} +% +% \section{Reassigning token list category codes} +% +% These functions allow the rescanning of tokens: re-apply \TeX{}'s +% tokenization process to apply category codes different from those +% in force when the tokens were absorbed. Whilst this functionality is +% supported, it is often preferable to find alternative approaches +% to achieving outcomes rather than rescanning tokens (for example +% construction of token lists token-by-token with intervening category +% code changes or using \cs{char_generate:nn}). +% +% \begin{function}[updated = 2015-08-11] +% { +% \tl_set_rescan:Nnn, \tl_set_rescan:Nno, \tl_set_rescan:Nnx, +% \tl_set_rescan:cnn, \tl_set_rescan:cno, \tl_set_rescan:cnx, +% \tl_gset_rescan:Nnn, \tl_gset_rescan:Nno, \tl_gset_rescan:Nnx, +% \tl_gset_rescan:cnn, \tl_gset_rescan:cno, \tl_gset_rescan:cnx +% } +% \begin{syntax} +% \cs{tl_set_rescan:Nnn} \meta{tl~var} \Arg{setup} \Arg{tokens} +% \end{syntax} +% Sets \meta{tl~var} to contain \meta{tokens}, applying the category +% code r\'{e}gime specified in the \meta{setup} before carrying out +% the assignment. (Category codes applied to tokens not explicitly covered +% by the \meta{setup} are those in force at the point of use of +% \cs{tl_set_rescan:Nnn}.) +% This allows the \meta{tl~var} to contain material +% with category codes other than those that apply when \meta{tokens} +% are absorbed. The \meta{setup} is run within a group and may +% contain any valid input, although only changes in category codes +% are relevant. See also \cs{tl_rescan:nn}. +% \begin{texnote} +% The \meta{tokens} are first turned into a string (using +% \cs{tl_to_str:n}). If the string contains one or more characters +% with character code \tn{newlinechar} (set equal to +% \tn{endlinechar} unless that is equal to $32$, before the user +% \meta{setup}), then it is split into lines at these characters, +% then read as if reading multiple lines from a file, ignoring +% spaces (catcode $10$) at the beginning and spaces and tabs +% (character code $32$ or $9$) at the end of every line. +% Otherwise, spaces (and tabs) are retained at both ends of the +% single-line string, as if it appeared in the middle of a line +% read from a file. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2015-08-11]{\tl_rescan:nn} +% \begin{syntax} +% \cs{tl_rescan:nn} \Arg{setup} \Arg{tokens} +% \end{syntax} +% Rescans \meta{tokens} applying the category code r\'{e}gime +% specified in the \meta{setup}, and leaves the resulting tokens in +% the input stream. (Category codes applied to tokens not explicitly covered +% by the \meta{setup} are those in force at the point of use of +% \cs{tl_rescan:nn}.) +% The \meta{setup} is run within a group and may +% contain any valid input, although only changes in category codes +% are relevant. See also \cs{tl_set_rescan:Nnn}, which is more +% robust than using \cs{tl_set:Nn} in the \meta{tokens} argument of +% \cs{tl_rescan:nn}. +% \begin{texnote} +% The \meta{tokens} are first turned into a string (using +% \cs{tl_to_str:n}). If the string contains one or more characters +% with character code \tn{newlinechar} (set equal to +% \tn{endlinechar} unless that is equal to $32$, before the user +% \meta{setup}), then it is split into lines at these characters, +% then read as if reading multiple lines from a file, ignoring +% spaces (catcode $10$) at the beginning and spaces and tabs +% (character code $32$ or $9$) at the end of every line. +% Otherwise, spaces (and tabs) are retained at both ends of the +% single-line string, as if it appeared in the middle of a line +% read from a file. +% \end{texnote} +% \end{function} +% +% \section{Token list conditionals} +% +% \begin{function}[EXP,pTF]{\tl_if_blank:n, \tl_if_blank:V, \tl_if_blank:o} +% \begin{syntax} +% \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 +% (\emph{i.e.}~contains no item). The test is \texttt{true} if +% \meta{token list} is zero or more explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$), +% and is \texttt{false} otherwise. +% \end{function} +% +% \begin{function}[EXP,pTF]{\tl_if_empty:N, \tl_if_empty:c} +% \begin{syntax} +% \cs{tl_if_empty_p:N} \meta{tl~var} +% \cs{tl_if_empty:NTF} \meta{tl~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{token list variable} is entirely empty +% (\emph{i.e.}~contains no tokens at all). +% \end{function} +% +% \begin{function}[added = 2012-05-24, updated = 2012-06-05, EXP,pTF] +% {\tl_if_empty:n, \tl_if_empty:V, \tl_if_empty:o} +% \begin{syntax} +% \cs{tl_if_empty_p:n} \Arg{token list} +% \cs{tl_if_empty:nTF} \Arg{token list} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{token list} is entirely empty +% (\emph{i.e.}~contains no tokens at all). +% \end{function} +% +% \begin{function}[EXP,pTF] +% {\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc} +% \begin{syntax} +% \cs{tl_if_eq_p:NN} \meta{tl~var_1} \meta{tl~var_2} +% \cs{tl_if_eq:NNTF} \meta{tl~var_1} \meta{tl~var_2} \Arg{true code} \Arg{false code} +% \end{syntax} +% Compares the content of two \meta{token list variables} and +% is logically \texttt{true} if the two contain the same list of +% tokens (\emph{i.e.}~identical in both the list of characters they +% contain and the category codes of those characters). Thus for example +% \begin{verbatim} +% \tl_set:Nn \l_tmpa_tl { abc } +% \tl_set:Nx \l_tmpb_tl { \tl_to_str:n { abc } } +% \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { true } { false } +% \end{verbatim} +% yields \texttt{false}. +% \end{function} +% +% \begin{function}[TF]{\tl_if_eq:nn} +% \begin{syntax} +% \cs{tl_if_eq:nnTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if \meta{token list_1} and \meta{token list_2} contain the +% same list of tokens, both in respect of character codes and category +% codes. +% \end{function} +% +% \begin{function}[TF]{\tl_if_in:Nn, \tl_if_in:cn} +% \begin{syntax} +% \cs{tl_if_in:NnTF} \meta{tl~var} \Arg{token list} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{token list} is found in the content of the +% \meta{tl~var}. The \meta{token list} cannot contain +% the tokens |{|, |}| or |#| +% (more precisely, explicit character tokens with category code $1$ +% (begin-group) or $2$ (end-group), and tokens with category code $6$). +% \end{function} +% +% \begin{function}[TF] +% {\tl_if_in:nn, \tl_if_in:Vn, \tl_if_in:on, \tl_if_in:no} +% \begin{syntax} +% \cs{tl_if_in:nnTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if \meta{token list_2} is found inside \meta{token list_1}. +% The \meta{token list_2} cannot contain the tokens |{|, |}| or |#| +% (more precisely, explicit character tokens with category code $1$ +% (begin-group) or $2$ (end-group), and tokens with category code $6$). +% \end{function} +% +% \begin{function}[added = 2017-11-14, EXP,pTF]{\tl_if_novalue:n} +% \begin{syntax} +% \cs{tl_if_novalue_p:n} \Arg{token list} +% \cs{tl_if_novalue:nTF} \Arg{token list} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{token list} is exactly equal to the special +% \cs{c_novalue_tl} marker. This function is intended to allow construction +% of flexible document interface structures in which missing optional +% arguments are detected. +% \end{function} +% +% \begin{function}[updated = 2011-08-13, EXP,pTF] +% {\tl_if_single:N, \tl_if_single:c} +% \begin{syntax} +% \cs{tl_if_single_p:N} \meta{tl~var} +% \cs{tl_if_single:NTF} \meta{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 a single normal token (neither an explicit space +% character nor a begin-group character) or a single brace group, +% surrounded by optional spaces on both sides. In other words, such a +% token list has token count $1$ according to \cs{tl_count:N}. +% \end{function} +% +% \begin{function}[updated = 2011-08-13, 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} has exactly one item, \emph{i.e.}~is +% a single normal token (neither an explicit space character nor a +% begin-group character) or a single brace group, surrounded by +% optional spaces on both sides. In other words, such a token list has +% token count $1$ according to \cs{tl_count:n}. +% \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. +% \end{function} +% +% \begin{function}[added = 2013-07-24, EXP, noTF]{\tl_case:Nn, \tl_case:cn} +% \begin{syntax} +% \cs{tl_case:NnTF} \meta{test token list variable} \\ +% ~~"{" \\ +% ~~~~\meta{token list variable case_1} \Arg{code case_1} \\ +% ~~~~\meta{token list variable case_2} \Arg{code case_2} \\ +% ~~~~\ldots \\ +% ~~~~\meta{token list variable case_n} \Arg{code case_n} \\ +% ~~"}" \\ +% ~~\Arg{true code} +% ~~\Arg{false code} +% \end{syntax} +% This function compares the \meta{test token list variable} in turn +% with each of the \meta{token list variable cases}. If the two +% are equal (as described for \cs{tl_if_eq:NNTF}) +% then the associated \meta{code} is left in the input +% stream and other cases are discarded. If any of the +% cases are matched, the \meta{true code} is also inserted into the +% input stream (after the code for the appropriate case), while if none +% match then the \meta{false code} is inserted. The function +% \cs{tl_case:Nn}, which does nothing if there is no match, is also +% available. +% \end{function} +% +% \section{Mapping to token lists} +% +% All mappings are done at the current group level, \emph{i.e.}~any +% local assignments made by the \meta{function} or \meta{code} discussed +% below remain in effect after the loop. +% +% \begin{function}[updated = 2012-06-29, rEXP] +% {\tl_map_function:NN, \tl_map_function:cN} +% \begin{syntax} +% \cs{tl_map_function:NN} \meta{tl~var} \meta{function} +% \end{syntax} +% Applies \meta{function} to every \meta{item} in the \meta{tl~var}. +% The \meta{function} receives one argument for each iteration. +% This may be a number of tokens if the \meta{item} was stored within +% braces. Hence the \meta{function} should anticipate receiving +% \texttt{n}-type arguments. See also \cs{tl_map_function:nN}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_function:nN} +% \begin{syntax} +% \cs{tl_map_function:nN} \Arg{token list} \meta{function} +% \end{syntax} +% Applies \meta{function} to every \meta{item} in the \meta{token list}, +% The \meta{function} receives one argument for each iteration. +% This may be a number of tokens if the \meta{item} was stored within +% braces. Hence the \meta{function} should anticipate receiving +% \texttt{n}-type arguments. See also \cs{tl_map_function:NN}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29] +% {\tl_map_inline:Nn, \tl_map_inline:cn} +% \begin{syntax} +% \cs{tl_map_inline:Nn} \meta{tl~var} \Arg{inline function} +% \end{syntax} +% Applies the \meta{inline function} to every \meta{item} stored within the +% \meta{tl~var}. The \meta{inline function} should consist of code which +% receives the \meta{item} as |#1|. See also \cs{tl_map_function:NN}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29]{\tl_map_inline:nn} +% \begin{syntax} +% \cs{tl_map_inline:nn} \Arg{token list} \Arg{inline function} +% \end{syntax} +% Applies the \meta{inline function} to every \meta{item} stored within the +% \meta{token list}. The \meta{inline function} should consist of code which +% receives the \meta{item} as |#1|. See also \cs{tl_map_function:nN}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29] +% {\tl_map_variable:NNn, \tl_map_variable:cNn} +% \begin{syntax} +% \cs{tl_map_variable:NNn} \meta{tl~var} \meta{variable} \Arg{code} +% \end{syntax} +% Stores each \meta{item} of the \meta{tl~var} in turn in the (token +% list) \meta{variable} and applies the \meta{code}. The \meta{code} +% will usually make use of the \meta{variable}, but this is not +% enforced. The assignments to the \meta{variable} are local. Its +% value after the loop is the last \meta{item} in the \meta{tl~var}, +% or its original value if the \meta{tl~var} is blank. See also +% \cs{tl_map_inline:Nn}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29]{\tl_map_variable:nNn} +% \begin{syntax} +% \cs{tl_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{code} +% \end{syntax} +% Stores each \meta{item} of the \meta{token list} in turn in the +% (token list) \meta{variable} and applies the \meta{code}. The +% \meta{code} will usually make use of the \meta{variable}, but this +% is not enforced. The assignments to the \meta{variable} are local. +% Its value after the loop is the last \meta{item} in the +% \meta{tl~var}, or its original value if the \meta{tl~var} is blank. +% See also \cs{tl_map_inline:nn}. +% \end{function} +% +% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_break:} +% \begin{syntax} +% \cs{tl_map_break:} +% \end{syntax} +% Used to terminate a \cs[no-index]{tl_map_\ldots} function before all +% entries in the \meta{token list variable} have been processed. This +% normally takes place within a conditional statement, for example +% \begin{verbatim} +% \tl_map_inline:Nn \l_my_tl +% { +% \str_if_eq:nnT { #1 } { bingo } { \tl_map_break: } +% % Do something useful +% } +% \end{verbatim} +% See also \cs{tl_map_break:n}. +% Use outside of a \cs[no-index]{tl_map_\ldots} scenario leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before the \meta{tokens} are +% inserted into the input stream. +% This depends on the design of the mapping function. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_break:n} +% \begin{syntax} +% \cs{tl_map_break:n} \Arg{code} +% \end{syntax} +% Used to terminate a \cs[no-index]{tl_map_\ldots} function before all +% entries in the \meta{token list variable} have been processed, inserting +% the \meta{code} after the mapping has ended. This +% normally takes place within a conditional statement, for example +% \begin{verbatim} +% \tl_map_inline:Nn \l_my_tl +% { +% \str_if_eq:nnT { #1 } { bingo } +% { \tl_map_break:n { } } +% % Do something useful +% } +% \end{verbatim} +% Use outside of a \cs[no-index]{tl_map_\ldots} scenario leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before the \meta{code} is +% inserted into the input stream. +% This depends on the design of the mapping function. +% \end{texnote} +% \end{function} +% +% \section{Using token lists} +% +% \begin{function}[EXP]{\tl_to_str:n, \tl_to_str:V} +% \begin{syntax} +% \cs{tl_to_str:n} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, leaving the +% resulting character tokens in the input stream. A \meta{string} +% is a series of tokens with category code $12$ (other) with the exception +% of spaces, which retain category code $10$ (space). +% This function requires only a single expansion. +% Its argument \emph{must} be braced. +% \begin{texnote} +% This is the \eTeX{} primitive \tn{detokenize}. +% Converting a \meta{token list} to a \meta{string} yields a +% concatenation of the string representations of every token in the +% \meta{token list}. +% The string representation of a control sequence is +% \begin{itemize} +% \item an escape character, whose character code is given by the +% internal parameter \tn{escapechar}, absent if the +% \tn{escapechar} is negative or greater than the largest +% character code; +% \item the control sequence name, as defined by \cs{cs_to_str:N}; +% \item a space, unless the control sequence name is a single +% character whose category at the time of expansion of +% \cs{tl_to_str:n} is not \enquote{letter}. +% \end{itemize} +% The string representation of an explicit character token is that +% character, doubled in the case of (explicit) macro parameter +% characters (normally |#|). +% In particular, the string representation of a token list may +% depend on the category codes in effect when it is evaluated, and +% the value of the \tn{escapechar}: for instance |\tl_to_str:n {\a}| +% normally produces the three character \enquote{backslash}, +% \enquote{lower-case a}, \enquote{space}, but it may also produce a +% single \enquote{lower-case a} if the escape character is negative +% and \texttt{a} is currently not a letter. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\tl_to_str:N, \tl_to_str:c} +% \begin{syntax} +% \cs{tl_to_str:N} \meta{tl~var} +% \end{syntax} +% Converts the content of the \meta{tl~var} into a series of characters +% with category code $12$ (other) with the exception of spaces, which +% retain category code $10$ (space). This \meta{string} is then left +% in the input stream. For low-level details, see the notes given for +% \cs{tl_to_str:n}. +% \end{function} +% +% \begin{function}[EXP]{\tl_use:N, \tl_use:c} +% \begin{syntax} +% \cs{tl_use:N} \meta{tl~var} +% \end{syntax} +% Recovers the content of a \meta{tl~var} and places it +% directly in the input stream. An error is raised if the variable +% does not exist or if it is invalid. Note that it is possible to use +% a \meta{tl~var} directly without an accessor function. +% \end{function} +% +% \section{Working with the content of token lists} +% +% \begin{function}[added = 2012-05-13, EXP] +% {\tl_count:n, \tl_count:V, \tl_count:o} +% \begin{syntax} +% \cs{tl_count:n} \Arg{tokens} +% \end{syntax} +% 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 +% ignores any unprotected spaces within \meta{tokens}. See also +% \cs{tl_count:N}. This function requires three expansions, +% giving an \meta{integer denotation}. +% \end{function} +% +% \begin{function}[added = 2012-05-13, EXP]{\tl_count:N, \tl_count:c} +% \begin{syntax} +% \cs{tl_count:N} \meta{tl~var} +% \end{syntax} +% Counts the number of token groups in the \meta{tl~var} +% and leaves this information in the input stream. Unbraced tokens +% count as one element as do each token group (|{|\ldots|}|). This +% process ignores any unprotected spaces within the \meta{tl~var}. +% See also \cs{tl_count:n}. This function requires three expansions, +% giving an \meta{integer denotation}. +% \end{function} +% +% \begin{function}[EXP, added = 2019-02-25]{\tl_count_tokens:n} +% \begin{syntax} +% \cs{tl_count_tokens:n} \Arg{tokens} +% \end{syntax} +% 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 token count of +% |a~{bc}| is $6$. +% \end{function} +% +% \begin{function}[updated = 2012-01-08, EXP] +% {\tl_reverse:n, \tl_reverse:V, \tl_reverse:o} +% \begin{syntax} +% \cs{tl_reverse:n} \Arg{token list} +% \end{syntax} +% Reverses the order of the \meta{items} in the \meta{token list}, +% so that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n} +% becomes \meta{item_n}\ldots \meta{item_3}\meta{item_2}\meta{item_1}. +% This process preserves 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}. +% \begin{texnote} +% The result is returned within \tn{unexpanded}, which means that the token +% list does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2012-01-08] +% {\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c} +% \begin{syntax} +% \cs{tl_reverse:N} \meta{tl~var} +% \end{syntax} +% Reverses the order of the \meta{items} stored in \meta{tl~var}, so +% that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n} +% becomes \meta{item_n}\ldots \meta{item_3}\meta{item_2}\meta{item_1}. +% This process preserves unprotected spaces within the +% \meta{token list variable}. Braced token groups are copied without +% reversing the order of tokens, but keep the outer set of braces. +% See also \cs{tl_reverse:n}, and, for improved performance, +% \cs{tl_reverse_items:n}. +% \end{function} +% +% \begin{function}[added = 2012-01-08, 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{item_1}\Arg{item_2}\Arg{item_3} \ldots \Arg{item_n} +% becomes \Arg{item_n} \ldots{} \Arg{item_3}\Arg{item_2}\Arg{item_1}. +% This process removes 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 the slower function \cs{tl_reverse:n}. +% \begin{texnote} +% The result is returned within \tn{unexpanded}, which means that the token +% list does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2011-07-09, updated = 2012-06-25, EXP] +% {\tl_trim_spaces:n, \tl_trim_spaces:o} +% \begin{syntax} +% \cs{tl_trim_spaces:n} \Arg{token list} +% \end{syntax} +% Removes any leading and trailing explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$) +% from the \meta{token list} and leaves the result in the input +% stream. +% \begin{texnote} +% The result is returned within \tn{unexpanded}, which means that the token +% list does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2018-04-12, EXP] +% {\tl_trim_spaces_apply:nN, \tl_trim_spaces_apply:oN} +% \begin{syntax} +% \cs{tl_trim_spaces_apply:nN} \Arg{token list} \meta{function} +% \end{syntax} +% Removes any leading and trailing explicit space characters (explicit +% tokens with character code~$32$ and category code~$10$) from the +% \meta{token list} and passes the result to the \meta{function} as an +% \texttt{n}-type argument. +% \end{function} +% +% \begin{function}[added = 2011-07-09] +% { +% \tl_trim_spaces:N, \tl_trim_spaces:c, +% \tl_gtrim_spaces:N, \tl_gtrim_spaces:c +% } +% \begin{syntax} +% \cs{tl_trim_spaces:N} \meta{tl~var} +% \end{syntax} +% Removes any leading and trailing explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$) +% from the content of the \meta{tl~var}. Note that this therefore +% \emph{resets} the content of the variable. +% \end{function} +% +% \begin{function}[added = 2017-02-06] +% {\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn} +% \begin{syntax} +% \cs{tl_sort:Nn} \meta{tl var} \Arg{comparison code} +% \end{syntax} +% Sorts the items in the \meta{tl var} according to the +% \meta{comparison code}, and assigns the result to +% \meta{tl var}. The details of sorting comparison are +% described in Section~\ref{sec:l3sort:mech}. +% \end{function} +% +% \begin{function}[added = 2017-02-06, EXP]{\tl_sort:nN} +% \begin{syntax} +% \cs{tl_sort:nN} \Arg{token list} \meta{conditional} +% \end{syntax} +% Sorts the items in the \meta{token list}, using the +% \meta{conditional} to compare items, and leaves the result in the +% input stream. The \meta{conditional} should have signature |:nnTF|, +% and return \texttt{true} if the two items being compared should be +% left in the same order, and \texttt{false} if the items should be +% swapped. The details of sorting comparison are +% described in Section~\ref{sec:l3sort:mech}. +% \begin{texnote} +% The result is returned within \cs{exp_not:n}, which means that the +% token list does not expand further when appearing in an +% \texttt{x}-type or \texttt{e}-type argument expansion. +% \end{texnote} +% \end{function} +% +% \section{The first token from a token list} +% +% Functions which deal with either only the very first item (balanced +% text or single normal token) in a token list, or the remaining tokens. +% +% \begin{function}[updated = 2012-09-09, EXP] +% {\tl_head:N, \tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f} +% \begin{syntax} +% \cs{tl_head:n} \Arg{token list} +% \end{syntax} +% Leaves in the input stream the first \meta{item} in the +% \meta{token list}, discarding the rest of the \meta{token list}. +% All leading explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$) +% are discarded; for example +% \begin{verbatim} +% \tl_head:n { abc } +% \end{verbatim} +% and +% \begin{verbatim} +% \tl_head:n { ~ abc } +% \end{verbatim} +% both leave |a| in the input stream. If the \enquote{head} is a +% brace group, rather than a single token, the braces are removed, and +% so +% \begin{verbatim} +% \tl_head:n { ~ { ~ ab } c } +% \end{verbatim} +% yields \verb*| ab|. +% A blank \meta{token list} (see \cs{tl_if_blank:nTF}) results in +% \cs{tl_head:n} leaving nothing in the input stream. +% \begin{texnote} +% The result is returned within \cs{exp_not:n}, which means that the token +% list does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\tl_head:w} +% \begin{syntax} +% \cs{tl_head:w} \meta{token list} | { } | \cs{q_stop} +% \end{syntax} +% Leaves in the input stream the first \meta{item} in the +% \meta{token list}, discarding the rest of the \meta{token list}. +% All leading explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$) +% are discarded. +% A blank \meta{token list} (which consists only of space characters) +% results in a low-level \TeX{} error, which may be avoided by the +% inclusion of an empty group in the input (as shown), without the need +% for an explicit test. Alternatively, \cs{tl_if_blank:nF} may be used to +% avoid using the function with a \enquote{blank} argument. +% This function requires only a single expansion, and thus is suitable for +% use within an \texttt{o}-type expansion. In general, \cs{tl_head:n} should +% be preferred if the number of expansions is not critical. +% \end{function} +% +% \begin{function}[updated = 2012-09-01, EXP] +% {\tl_tail:N, \tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f} +% \begin{syntax} +% \cs{tl_tail:n} \Arg{token list} +% \end{syntax} +% Discards all leading explicit space characters +% (explicit tokens with character code~$32$ and category code~$10$) +% and the first \meta{item} in the \meta{token list}, and leaves the +% remaining tokens in the input stream. Thus for example +% \begin{verbatim} +% \tl_tail:n { a ~ {bc} d } +% \end{verbatim} +% and +% \begin{verbatim} +% \tl_tail:n { ~ a ~ {bc} d } +% \end{verbatim} +% both leave \verb*| {bc}d| in the input stream. A blank +% \meta{token list} (see \cs{tl_if_blank:nTF}) results +% in \cs{tl_tail:n} leaving nothing in the input stream. +% \begin{texnote} +% The result is returned within \cs{exp_not:n}, which means that the +% token list does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2012-07-09, EXP, pTF]{\tl_if_head_eq_catcode:nN} +% \begin{syntax} +% \cs{tl_if_head_eq_catcode_p:nN} \Arg{token list} \meta{test token} +% \cs{tl_if_head_eq_catcode:nNTF} \Arg{token list} \meta{test token} +% ~~\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}. In the case where the +% \meta{token list} is empty, the test is always \texttt{false}. +% \end{function} +% +% \begin{function}[updated = 2012-07-09, EXP, pTF] +% {\tl_if_head_eq_charcode:nN, \tl_if_head_eq_charcode:fN} +% \begin{syntax} +% \cs{tl_if_head_eq_charcode_p:nN} \Arg{token list} \meta{test token} +% \cs{tl_if_head_eq_charcode:nNTF} \Arg{token list} \meta{test token} +% ~~\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}. In the case where the +% \meta{token list} is empty, the test is always \texttt{false}. +% \end{function} +% +% \begin{function}[updated = 2012-07-09, EXP, pTF]{\tl_if_head_eq_meaning:nN} +% \begin{syntax} +% \cs{tl_if_head_eq_meaning_p:nN} \Arg{token list} \meta{test token} +% \cs{tl_if_head_eq_meaning:nNTF} \Arg{token list} \meta{test token} +% ~~\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}. In the case where +% \meta{token list} is empty, the test is always \texttt{false}. +% \end{function} +% +% \begin{function}[added = 2012-07-08, EXP, pTF]{\tl_if_head_is_group:n} +% \begin{syntax} +% \cs{tl_if_head_is_group_p:n} \Arg{token list} +% \cs{tl_if_head_is_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 \texttt{false} +% if the \meta{token list} starts with an implicit token such as +% \cs{c_group_begin_token}, or if it is empty. +% This function is useful to implement actions on token lists on +% a token by token basis. +% \end{function} +% +% \begin{function}[added = 2012-07-08, EXP, pTF]{\tl_if_head_is_N_type:n} +% \begin{syntax} +% \cs{tl_if_head_is_N_type_p:n} \Arg{token list} +% \cs{tl_if_head_is_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 +% (explicit token with character code~$32$ and category code~$10$) +% nor an explicit begin-group character +% (with category code~1 and any character code). An empty +% argument yields \texttt{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. +% \end{function} +% +% \begin{function}[updated = 2012-07-08, EXP, pTF]{\tl_if_head_is_space:n} +% \begin{syntax} +% \cs{tl_if_head_is_space_p:n} \Arg{token list} +% \cs{tl_if_head_is_space: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 space character +% (explicit token with character code~$12$ and category code~$10$). +% In particular, the test is \texttt{false} if the \meta{token list} +% starts with an implicit token such as \cs{c_space_token}, or if it +% is empty. +% This function is useful to implement actions on token lists on +% a token by token basis. +% \end{function} +% +% \section{Using a single item} +% +% \begin{function}[added = 2014-07-17, EXP] +% {\tl_item:nn, \tl_item:Nn, \tl_item:cn} +% \begin{syntax} +% \cs{tl_item:nn} \Arg{token list} \Arg{integer expression} +% \end{syntax} +% Indexing items in the \meta{token list} from~$1$ on the left, this +% function evaluates the \meta{integer expression} and leaves the +% appropriate item from the \meta{token list} in the input stream. +% If the \meta{integer expression} is negative, indexing occurs from +% the right of the token list, starting at $-1$ for the right-most item. +% If the index is out of bounds, then the function expands to nothing. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2016-12-06] +% {\tl_rand_item:N, \tl_rand_item:c, \tl_rand_item:n} +% \begin{syntax} +% \cs{tl_rand_item:N} \meta{tl~var} +% \cs{tl_rand_item:n} \Arg{token list} +% \end{syntax} +% Selects a pseudo-random item of the \meta{token list}. If the +% \meta{token list} is blank, the result is empty. +% This is not available in older versions of \XeTeX{}. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2017-02-17, updated = 2017-07-15] +% {\tl_range:Nnn, \tl_range:nnn} +% \begin{syntax} +% \cs{tl_range:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index} +% \cs{tl_range:nnn} \Arg{token list} \Arg{start index} \Arg{end index} +% \end{syntax} +% Leaves in the input stream the items from the \meta{start index} to the +% \meta{end index} inclusive. Spaces and braces are preserved between +% the items returned (but never at either end of the list). +% +% Here \meta{start index} and \meta{end index} should be integer denotations. +% For describing in detail the functions' behavior, let $m$ and $n$ be the start +% and end index respectively. If either is $0$, the result is empty. A positive +% index means `start counting from the left end', a negative index means +% `start counting from the right end'. Let $l$ be the count of the token list. +% +% The \emph{actual start point} is determined as $M=m$ if~$m>0$ and as $M=l+m+1$ +% if~$m<0$. Similarly the \emph{actual end point} is $N=n$ if~$n>0$ and $N=l+n+1$ +% if~$n<0$. If $M>N$, the result is empty. Otherwise it consists of all items from +% position $M$ to position $N$ inclusive; for the purpose of this rule, we can +% imagine that the token list extends at infinity on either side, with void items +% at positions $s$ for $s\le0$ or $s>l$. +% +% Spaces in between items in the actual range are preserved. Spaces at either end +% of the token list will be removed anyway (think to the token list being passed to +% |\tl_trim_spaces:n| to begin with. +% +% Thus, with $l=7$ as in the examples below, all of the following are equivalent +% and result in the whole token list +% \begin{verbatim} +% \tl_range:nnn { abcd~{e{}}fg } { 1 } { 7 } +% \tl_range:nnn { abcd~{e{}}fg } { 1 } { 12 } +% \tl_range:nnn { abcd~{e{}}fg } { -7 } { 7 } +% \tl_range:nnn { abcd~{e{}}fg } { -12 } { 7 } +% \end{verbatim} +% Here are some more interesting examples. The calls +% \begin{verbatim} +% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { 2 } { 5 } } +% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { 2 } { -3 } } +% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { -6 } { 5 } } +% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { -6 } { -3 } } +% \end{verbatim} +% are all equivalent and will print |bcd{e{}}| on the terminal; similarly +% \begin{verbatim} +% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { 2 } { 5 } } +% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { 2 } { -3 } } +% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { -6 } { 5 } } +% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { -6 } { -3 } } +% \end{verbatim} +% are all equivalent and will print |bcd {e{}}| on the +% terminal (note the space in the middle). To the contrary, +% \begin{verbatim} +% \tl_range:nnn { abcd~{e{}}f } { 2 } { 4 } +% \end{verbatim} +% will discard the space after `d`. +% +% If we want to get the items from the third to the last, the call +% is |\tl_range:nnn { } { 3 } { -1 }|. Similarly, for discarding +% the last item, we can do |\tl_range:nnn { } { 1 } { -2 }|. +% +% The behavior of \cs{tl_range:Nnn} is exactly the same, acting on the +% contents of the tl variable. +% +% For improved performance, see \cs{tl_range_braced:nnn} and +% \cs{tl_range_unbraced:nnn}. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \section{Viewing token lists} +% +% \begin{function}[updated = 2015-08-01]{\tl_show:N, \tl_show:c} +% \begin{syntax} +% \cs{tl_show:N} \meta{tl~var} +% \end{syntax} +% Displays the content of the \meta{tl~var} on the terminal. +% \begin{texnote} +% This is similar to the \TeX{} primitive \tn{show}, wrapped to a +% fixed number of characters per line. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2015-08-07]{\tl_show:n} +% \begin{syntax} +% \cs{tl_show:n} \Arg{token list} +% \end{syntax} +% Displays the \meta{token list} on the terminal. +% \begin{texnote} +% This is similar to the \eTeX{} primitive \tn{showtokens}, wrapped +% to a fixed number of characters per line. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2014-08-22, updated = 2015-08-01]{\tl_log:N, \tl_log:c} +% \begin{syntax} +% \cs{tl_log:N} \meta{tl~var} +% \end{syntax} +% Writes the content of the \meta{tl~var} in the log file. See also +% \cs{tl_show:N} which displays the result in the terminal. +% \end{function} +% +% \begin{function}[added = 2014-08-22, updated = 2015-08-07]{\tl_log:n} +% \begin{syntax} +% \cs{tl_log:n} \Arg{token list} +% \end{syntax} +% Writes the \meta{token list} in the log file. See also +% \cs{tl_show:n} which displays the result in the terminal. +% \end{function} +% +% \section{Constant token lists} +% +% \begin{variable}{\c_empty_tl} +% Constant that is always empty. +% \end{variable} +% +% \begin{variable}[added = 2017-11-14]{\c_novalue_tl} +% A marker for the absence of an argument. This constant |tl| can safely +% be typeset (\emph{cf.}~\cs{q_nil}), with the result being |-NoValue-|. +% It is important to note that \cs{c_novalue_tl} is constructed such that it +% will \emph{not} match the simple text input |-NoValue-|, \emph{i.e.} +% that +% \begin{verbatim} +% \tl_if_eq:VnTF \c_novalue_tl { -NoValue- } +% \end{verbatim} +% is logically \texttt{false}. The \cs{c_novalue_tl} marker is intended for +% use in creating document-level interfaces, where it serves as an indicator +% that an (optional) argument was omitted. In particular, it is distinct +% from a simple empty |tl|. +% \end{variable} +% +% \begin{variable}{\c_space_tl} +% An explicit space character contained in a token list (compare this with +% \cs{c_space_token}). For use where an explicit space is required. +% \end{variable} +% +% \section{Scratch token lists} +% +% \begin{variable}{\l_tmpa_tl, \l_tmpb_tl} +% Scratch token lists for local assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \begin{variable}{\g_tmpa_tl, \g_tmpb_tl} +% Scratch token lists for global assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3tl} implementation} +% +% \begin{macrocode} +%<*initex|package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=tl> +% \end{macrocode} +% +% A token list variable is a \TeX{} macro that holds tokens. By using the +% \eTeX{} primitive \tn{unexpanded} inside a \TeX{} \tn{edef} it is +% possible to store any tokens, including |#|, in this way. +% +% \subsection{Functions} +% +% \begin{macro}{\tl_new:N, \tl_new:c} +% Creating new token list variables is a case of checking for an +% existing definition and doing the definition. +% \begin{macrocode} +\cs_new_protected:Npn \tl_new:N #1 + { + \__kernel_chk_if_free_cs:N #1 + \cs_gset_eq:NN #1 \c_empty_tl + } +\cs_generate_variant:Nn \tl_new:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_const:Nn, \tl_const:Nx, \tl_const:cn, \tl_const:cx} +% Constants are also easy to generate. +% \begin{macrocode} +\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { } +\cs_new_protected:Npn \tl_const:Nn #1#2 + { + \__kernel_chk_if_free_cs:N #1 + \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } + } +\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { } +\cs_new_protected:Npn \tl_const:Nx #1#2 + { + \__kernel_chk_if_free_cs:N #1 + \cs_gset_nopar:Npx #1 {#2} + } +\cs_generate_variant:Nn \tl_const:Nn { c } +\cs_generate_variant:Nn \tl_const:Nx { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_clear:N, \tl_clear:c} +% \begin{macro}{\tl_gclear:N, \tl_gclear:c} +% Clearing a token list variable means setting it to an empty value. +% Error checking is sorted out by the parent function. +% \begin{macrocode} +\cs_new_protected:Npn \tl_clear:N #1 + { \tl_set_eq:NN #1 \c_empty_tl } +\cs_new_protected:Npn \tl_gclear:N #1 + { \tl_gset_eq:NN #1 \c_empty_tl } +\cs_generate_variant:Nn \tl_clear:N { c } +\cs_generate_variant:Nn \tl_gclear:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_clear_new:N, \tl_clear_new:c} +% \begin{macro}{\tl_gclear_new:N, \tl_gclear_new:c} +% Clearing a token list variable means setting it to an empty value. +% Error checking is sorted out by the parent function. +% \begin{macrocode} +\cs_new_protected:Npn \tl_clear_new:N #1 + { \tl_if_exist:NTF #1 { \tl_clear:N #1 } { \tl_new:N #1 } } +\cs_new_protected:Npn \tl_gclear_new:N #1 + { \tl_if_exist:NTF #1 { \tl_gclear:N #1 } { \tl_new:N #1 } } +\cs_generate_variant:Nn \tl_clear_new:N { c } +\cs_generate_variant:Nn \tl_gclear_new:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_set_eq:NN, \tl_set_eq:Nc, \tl_set_eq:cN, \tl_set_eq:cc} +% \begin{macro}{\tl_gset_eq:NN, \tl_gset_eq:Nc, \tl_gset_eq:cN, \tl_gset_eq:cc} +% For setting token list variables equal to each other. When checking +% is turned on, make sure both variables exist. +% \begin{macrocode} +\__kernel_if_debug:TF + { + \cs_new_protected:Npn \tl_set_eq:NN #1#2 + { + \__kernel_chk_var_local:N #1 + \__kernel_chk_var_exist:N #2 + \cs_set_eq:NN #1 #2 + } + \cs_new_protected:Npn \tl_gset_eq:NN #1#2 + { + \__kernel_chk_var_global:N #1 + \__kernel_chk_var_exist:N #2 + \cs_gset_eq:NN #1 #2 + } + } + { + \cs_new_eq:NN \tl_set_eq:NN \cs_set_eq:NN + \cs_new_eq:NN \tl_gset_eq:NN \cs_gset_eq:NN + } +\cs_generate_variant:Nn \tl_set_eq:NN { cN, Nc, cc } +\cs_generate_variant:Nn \tl_gset_eq:NN { cN, Nc, cc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_concat:NNN, \tl_concat:ccc} +% \begin{macro}{\tl_gconcat:NNN, \tl_gconcat:ccc} +% Concatenating token lists is easy. When checking is turned on, all +% three arguments must be checked: a token list |#2| or |#3| equal to +% \cs{scan_stop:} would lead to problems later on. +% \begin{macrocode} +\__kernel_patch:nnNNpn + { + \__kernel_chk_var_exist:N #2 + \__kernel_chk_var_exist:N #3 + } + { } +\cs_new_protected:Npn \tl_concat:NNN #1#2#3 + { \tl_set:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } +\__kernel_patch:nnNNpn + { + \__kernel_chk_var_exist:N #2 + \__kernel_chk_var_exist:N #3 + } + { } +\cs_new_protected:Npn \tl_gconcat:NNN #1#2#3 + { \tl_gset:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } +\cs_generate_variant:Nn \tl_concat:NNN { ccc } +\cs_generate_variant:Nn \tl_gconcat:NNN { ccc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\tl_if_exist:N, \tl_if_exist:c} +% Copies of the \texttt{cs} functions defined in \pkg{l3basics}. +% \begin{macrocode} +\prg_new_eq_conditional:NNn \tl_if_exist:N \cs_if_exist:N { TF , T , F , p } +\prg_new_eq_conditional:NNn \tl_if_exist:c \cs_if_exist:c { TF , T , F , p } +% \end{macrocode} +% \end{macro} +% +% \subsection{Constant token lists} +% +% \begin{variable}{\c_empty_tl} +% Never full. We need to define that constant before using \cs{tl_new:N}. +% \begin{macrocode} +\tl_const:Nn \c_empty_tl { } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\c_novalue_tl} +% A special marker: as we don't have |\char_generate:nn| yet, has to be +% created the old-fashioned way. +% \begin{macrocode} +\group_begin: +\tex_lccode:D `A = `- +\tex_lccode:D `N = `N +\tex_lccode:D `V = `V +\tex_lowercase:D + { + \group_end: + \tl_const:Nn \c_novalue_tl { ANoValue- } + } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\c_space_tl} +% A space as a token list (as opposed to as a character). +% \begin{macrocode} +\tl_const:Nn \c_space_tl { ~ } +% \end{macrocode} +% \end{variable} +% +% \subsection{Adding to token list variables} +% +% \begin{macro} +% { +% \tl_set:Nn, \tl_set:NV, \tl_set:Nv, \tl_set:No, \tl_set:Nf, \tl_set:Nx, +% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:cf, \tl_set:cx +% } +% \begin{macro} +% { +% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv, +% \tl_gset:No, \tl_gset:Nf, \tl_gset:Nx, +% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv, +% \tl_gset:co, \tl_gset:cf, \tl_gset:cx +% } +% By using \cs{exp_not:n} token list variables can contain |#| tokens, +% which makes the token list registers provided by \TeX{} +% more or less redundant. The \cs{tl_set:No} version is done +% \enquote{by hand} as it is used quite a lot. Each definition is +% prefixed by a call to \cs{__kernel_patch:nnNNpn} which adds an +% existence check to the definition. +% \begin{macrocode} +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_set:Nn #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:n {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_set:No #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_set:Nx #1#2 + { \cs_set_nopar:Npx #1 {#2} } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gset:Nn #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gset:No #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gset:Nx #1#2 + { \cs_gset_nopar:Npx #1 {#2} } +\cs_generate_variant:Nn \tl_set:Nn { NV , Nv , Nf } +\cs_generate_variant:Nn \tl_set:Nx { c } +\cs_generate_variant:Nn \tl_set:Nn { c, co , cV , cv , cf } +\cs_generate_variant:Nn \tl_gset:Nn { NV , Nv , Nf } +\cs_generate_variant:Nn \tl_gset:Nx { c } +\cs_generate_variant:Nn \tl_gset:Nn { c, co , cV , cv , cf } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \tl_put_left:Nn, \tl_put_left:NV, \tl_put_left:No, \tl_put_left:Nx, +% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:co, \tl_put_left:cx +% } +% \begin{macro} +% { +% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:No, \tl_gput_left:Nx, +% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:co, \tl_gput_left:cx +% } +% Adding to the left is done directly to gain a little performance. +% \begin{macrocode} +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_left:Nn #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_left:NV #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_left:No #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_left:Nx #1#2 + { \cs_set_nopar:Npx #1 { #2 \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_left:Nn #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_left:NV #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_left:No #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_left:Nx #1#2 + { \cs_gset_nopar:Npx #1 { #2 \exp_not:o {#1} } } +\cs_generate_variant:Nn \tl_put_left:Nn { c } +\cs_generate_variant:Nn \tl_put_left:NV { c } +\cs_generate_variant:Nn \tl_put_left:No { c } +\cs_generate_variant:Nn \tl_put_left:Nx { c } +\cs_generate_variant:Nn \tl_gput_left:Nn { c } +\cs_generate_variant:Nn \tl_gput_left:NV { c } +\cs_generate_variant:Nn \tl_gput_left:No { c } +\cs_generate_variant:Nn \tl_gput_left:Nx { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \tl_put_right:Nn, \tl_put_right:NV, \tl_put_right:No, \tl_put_right:Nx, +% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:co, \tl_put_right:cx +% } +% \begin{macro} +% { +% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:No, +% \tl_gput_right:Nx, +% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:co, +% \tl_gput_right:cx +% } +% The same on the right. +% \begin{macrocode} +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_right:Nn #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_right:NV #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_right:No #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { } +\cs_new_protected:Npn \tl_put_right:Nx #1#2 + { \cs_set_nopar:Npx #1 { \exp_not:o #1 #2 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_right:Nn #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_right:NV #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_right:No #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } +\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { } +\cs_new_protected:Npn \tl_gput_right:Nx #1#2 + { \cs_gset_nopar:Npx #1 { \exp_not:o {#1} #2 } } +\cs_generate_variant:Nn \tl_put_right:Nn { c } +\cs_generate_variant:Nn \tl_put_right:NV { c } +\cs_generate_variant:Nn \tl_put_right:No { c } +\cs_generate_variant:Nn \tl_put_right:Nx { c } +\cs_generate_variant:Nn \tl_gput_right:Nn { c } +\cs_generate_variant:Nn \tl_gput_right:NV { c } +\cs_generate_variant:Nn \tl_gput_right:No { c } +\cs_generate_variant:Nn \tl_gput_right:Nx { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Reassigning token list category codes} +% +% \begin{variable}{\c_@@_rescan_marker_tl} +% The rescanning code needs a special token list containing the same +% character (chosen here to be a colon) with two different category +% codes: it cannot appear in the tokens being rescanned since all +% colons have the same category code. +% \begin{macrocode} +\tl_const:Nx \c_@@_rescan_marker_tl { : \token_to_str:N : } +% \end{macrocode} +% \end{variable} +% +% \begin{macro} +% { +% \tl_set_rescan:Nnn, \tl_set_rescan:Nno, \tl_set_rescan:Nnx, +% \tl_set_rescan:cnn, \tl_set_rescan:cno, \tl_set_rescan:cnx +% } +% \begin{macro} +% { +% \tl_gset_rescan:Nnn, \tl_gset_rescan:Nno, \tl_gset_rescan:Nnx, +% \tl_gset_rescan:cnn, \tl_gset_rescan:cno, \tl_gset_rescan:cnx +% } +% \begin{macro}{\tl_rescan:nn} +% \begin{macro}{\@@_set_rescan:NNnn, \@@_set_rescan_multi:nNN} +% \begin{macro}[EXP]{\@@_rescan:NNw} +% In a group, after some initial setup explained below and the user +% setup~|#3| (followed by \cs{scan_stop:} to be safe), there is a call +% to \cs{@@_set_rescan:nNN}. This shared auxiliary defined later +% distinguishes single-line and multi-line ``files''. In the simplest +% case of multi-line files, it calls (with the same arguments) +% \cs{@@_set_rescan_multi:nNN}, whose code is included here to help +% understand the approach. This function rescans its argument |#1|, +% closes the group, and performs the assignment. +% +% One difficulty when rescanning is that \tn{scantokens} treats the +% argument as a file, and without the correct settings a \TeX{} error +% occurs: +% \begin{verbatim} +% ! File ended while scanning definition of ... +% \end{verbatim} +% A related minor issue is a warning due to opening a group before the +% \tn{scantokens} and closing it inside that temporary file; we avoid +% that by setting \tn{tracingnesting}. The standard solution to the +% ``File ended'' error is to grab the rescanned tokens as a delimited +% argument of an auxiliary, here \cs{@@_rescan:NNw}, that performs the +% assignment, then let \TeX{} ``execute'' the end of file marker. As +% usual in delimited arguments we use \cs{prg_do_nothing:} to avoid +% stripping an outer set braces: this is removed by using +% \texttt{o}-expanding assignments. The delimiter cannot appear +% within the rescanned token list because it contains twice the same +% character, with different catcodes. +% +% For \cs{tl_rescan:nn} we cannot simply call \cs{@@_set_rescan:NNnn} +% \cs{prg_do_nothing:} \cs{use:n} because that would leave the +% end-of-file marker \emph{after} the result of rescanning. If that +% rescanned result is code that looks further in the input stream for +% arguments, it would break. +% +% For multi-line files the only subtlety is that \tn{newlinechar} +% should be equal to \tn{endlinechar} because \tn{newlinechar} +% characters become new lines and then become \tn{endlinechar} +% characters when writing to an abstract file and reading back. This +% equality is ensured by setting \tn{newlinechar} equal to +% \tn{endlinechar}. Prior to this, \tn{endlinechar} is set to $-1$ if +% it was $32$ (in particular true after \cs{ExplSyntaxOn}) to avoid +% unreasonable line-breaks at every space for instance in error +% messages triggered by the user setup. Another side effect of +% reading back from the file is that spaces (catcode $10$) are ignored +% at the beginning of lines, and spaces and tabs (character code $32$ +% and $9$) are ignored at the end of lines. +% +% The two \cs{if_false:} \ldots{} \cs{fi:} are there to prevent +% alignment tabs to cause a change of tabular cell while rescanning. +% We put the \enquote{opening} one after \cs{group_begin:} so that if +% one accidentally \texttt{f}-expands \cs{tl_set_rescan:Nnn} braces +% remain balanced. This is essential in \texttt{e}-type arguments +% when \tn{expanded} is not available. +% \begin{macrocode} +\cs_new_protected:Npn \tl_rescan:nn #1#2 + { + \tl_set_rescan:Nnn \l_@@_internal_a_tl {#1} {#2} + \exp_after:wN \tl_clear:N \exp_after:wN \l_@@_internal_a_tl + \l_@@_internal_a_tl + } +\cs_new_protected:Npn \tl_set_rescan:Nnn + { \@@_set_rescan:NNnn \tl_set:No } +\cs_new_protected:Npn \tl_gset_rescan:Nnn + { \@@_set_rescan:NNnn \tl_gset:No } +\cs_new_protected:Npn \@@_set_rescan:NNnn #1#2#3#4 + { + \group_begin: + \if_false: { \fi: + \int_set_eq:NN \tex_tracingnesting:D \c_zero_int + \int_compare:nNnT \tex_endlinechar:D = { 32 } + { \int_set:Nn \tex_endlinechar:D { -1 } } + \int_set_eq:NN \tex_newlinechar:D \tex_endlinechar:D + #3 \scan_stop: + \exp_args:No \@@_set_rescan:nNN { \tl_to_str:n {#4} } #1 #2 + \if_false: } \fi: + } +\cs_new_protected:Npn \@@_set_rescan_multi:nNN #1#2#3 + { + \exp_args:No \tex_everyeof:D { \c_@@_rescan_marker_tl } + \exp_after:wN \@@_rescan:NNw + \exp_after:wN #2 + \exp_after:wN #3 + \exp_after:wN \prg_do_nothing: + \tex_scantokens:D {#1} + } +\exp_args:Nno \use:nn + { \cs_new:Npn \@@_rescan:NNw #1#2#3 } \c_@@_rescan_marker_tl + { + \group_end: + #1 #2 {#3} + } +\cs_generate_variant:Nn \tl_set_rescan:Nnn { Nno , Nnx } +\cs_generate_variant:Nn \tl_set_rescan:Nnn { c , cno , cnx } +\cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nno , Nnx } +\cs_generate_variant:Nn \tl_gset_rescan:Nnn { c , cno } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_set_rescan:nNN} +% \begin{macro}{\@@_set_rescan_single:nnNN, \@@_set_rescan_single_aux:nnnNN} +% \begin{macro}[rEXP]{\@@_set_rescan_single_aux:w} +% The function \cs{@@_set_rescan:nNN} calls \cs{@@_set_rescan_multi:nNN} or +% \cs{@@_set_rescan_single:nnNN} |{ ' }| depending on whether its argument +% is a single-line fragment of code/data or is made of multiple lines +% by testing for the presence of a \tn{newlinechar} character. If +% \tn{newlinechar} is out of range, the argument is assumed to be a +% single line. +% +% For a single line, no \tn{endlinechar} should be added, so it is +% set to $-1$, and spaces should not be removed. +% Trailing spaces and tabs are a difficult matter, as \TeX{} removes +% these at a very low level. The only way to preserve them is to +% rescan not the argument but the argument followed by a character +% with a reasonable category code. Here, $11$ (letter) and $12$ (other) +% are accepted, as these are convenient, suitable for +% delimiting an argument, and it is very unlikely that none of the +% ASCII characters are in one of these categories. To avoid +% selecting one particular character to put at the end, whose +% category code may have been modified, there is a loop through +% characters from |'| (ASCII $39$) to |~| (ASCII $127$). The choice +% of starting point was made because this is the start of a very long +% range of characters whose standard category is letter or other, +% thus minimizing the number of steps needed by the loop (most often +% just a single one). If no valid character is found (very rare), +% fall-back on \cs{@@_set_rescan_multi:nNN}. +% +% Otherwise, once a valid character is found (let us use |'| in this +% explanation) run some code very similar to \cs{@@_set_rescan_multi:nNN} +% but with |'| added at both ends of the input. Of course, we need to +% define the auxiliary \cs{@@_set_rescan_single:NNww} on the fly to remove +% the additional~|'| that is just before |::| (by which we mean +% \cs{c_@@_rescan_marker_tl}). Note that the argument must be +% delimited by |'| with the current catcode; this is done thanks to +% \cs{char_generate:nn}. Yet another issue is that the rescanned +% token list may contain a comment character, in which case the |'| we +% expected is not there. We fix this as follows: rather than just +% |::| we set \tn{everyeof} to |::|\Arg{code1} |'::|\Arg{code2} +% \cs{q_stop}. The auxiliary \cs{@@_set_rescan_single:NNww} runs the +% \texttt{o}-expanding assignment, expanding either \meta{code1} or +% \meta{code2} before its the main argument~|#3|. In the typical case +% without comment character, \meta{code1} is expanded, removing the +% leading~|'|. In the rarer case with comment character, \meta{code2} +% is expanded, calling \cs{@@_set_rescan_single_aux:w}, which removes the +% trailing |::|\Arg{code1} and the leading~|'|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_rescan:nNN #1 + { + \int_compare:nNnTF \tex_newlinechar:D < 0 + { \use_ii:nn } + { + \exp_args:Nnf \tl_if_in:nnTF {#1} + { \char_generate:nn { \tex_newlinechar:D } { 12 } } + } + { \@@_set_rescan_multi:nNN } + { + \int_set:Nn \tex_endlinechar:D { -1 } + \@@_set_rescan_single:nnNN { `' } + } + {#1} + } +\cs_new_protected:Npn \@@_set_rescan_single:nnNN #1 + { + \int_compare:nNnTF + { \char_value_catcode:n {#1} / 2 } = 6 + { + \exp_args:Nof \@@_set_rescan_single_aux:nnnNN + \c_@@_rescan_marker_tl + { \char_generate:nn {#1} { \char_value_catcode:n {#1} } } + } + { + \int_compare:nNnTF {#1} < { `\~ } + { + \exp_args:Nf \@@_set_rescan_single:nnNN + { \int_eval:n { #1 + 1 } } + } + { \@@_set_rescan_multi:nNN } + } + } +\cs_new_protected:Npn \@@_set_rescan_single_aux:nnnNN #1#2#3#4#5 + { + \tex_everyeof:D + { + #1 \use_none:n + #2 #1 { \exp:w \@@_set_rescan_single_aux:w } + \q_stop + } + \cs_set:Npn \@@_rescan:NNw ##1##2##3 #2 #1 ##4 ##5 \q_stop + { + \group_end: + ##1 ##2 { ##4 ##3 } + } + \exp_after:wN \@@_rescan:NNw + \exp_after:wN #4 + \exp_after:wN #5 + \tex_scantokens:D { #2 #3 #2 } + } +\exp_args:Nno \use:nn + { \cs_new:Npn \@@_set_rescan_single_aux:w #1 } + \c_@@_rescan_marker_tl #2 + { \use_i:nn \exp_end: #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Modifying token list variables} +% +% \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} +% All of the \texttt{replace} functions call \cs{@@_replace:NnNNNnn} +% with appropriate arguments. The first two arguments are explained +% later. The next controls whether the replacement function calls +% itself (\cs{@@_replace_next:w}) or stops (\cs{@@_replace_wrap:w}) +% after the first replacement. Next comes an \texttt{x}-type +% assignment function \cs{tl_set:Nx} or \cs{tl_gset:Nx} for local or +% global replacements. Finally, the three arguments \meta{tl~var} +% \Arg{pattern} \Arg{replacement} provided by the user. When +% describing the auxiliary functions below, we denote the contents of +% the \meta{tl~var} by \meta{token list}. +% \begin{macrocode} +\cs_new_protected:Npn \tl_replace_once:Nnn + { \@@_replace:NnNNNnn \q_mark ? \@@_replace_wrap:w \tl_set:Nx } +\cs_new_protected:Npn \tl_greplace_once:Nnn + { \@@_replace:NnNNNnn \q_mark ? \@@_replace_wrap:w \tl_gset:Nx } +\cs_new_protected:Npn \tl_replace_all:Nnn + { \@@_replace:NnNNNnn \q_mark ? \@@_replace_next:w \tl_set:Nx } +\cs_new_protected:Npn \tl_greplace_all:Nnn + { \@@_replace:NnNNNnn \q_mark ? \@@_replace_next:w \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} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \@@_replace:NnNNNnn, +% \@@_replace_auxi:NnnNNNnn, +% \@@_replace_auxii:nNNNnn, +% \@@_replace_next:w, +% \@@_replace_wrap:w, +% } +% To implement the actual replacement auxiliary +% \cs{@@_replace_auxii:nNNNnn} we need a \meta{delimiter} with +% the following properties: +% \begin{itemize} +% \item all occurrences of the \meta{pattern}~|#6| in +% \enquote{\meta{token list} \meta{delimiter}} belong to the +% \meta{token list} and have no overlap with the \meta{delimiter}, +% \item the first occurrence of the \meta{delimiter} in +% \enquote{\meta{token list} \meta{delimiter}} is the trailing +% \meta{delimiter}. +% \end{itemize} +% We first find the building blocks for the \meta{delimiter}, namely +% two tokens \meta{A} and~\meta{B} such that \meta{A} does not appear +% in~|#6| and |#6| is not~\meta{B} (this condition is trivial if |#6| +% has more than one token). Then we consider the delimiters +% \enquote{\meta{A}} and \enquote{\meta{A} \meta{A}$^n$ \meta{B} +% \meta{A}$^n$ \meta{B}}, for $n\geq 1$, where $\meta{A}^n$ denotes +% $n$~copies of \meta{A}, and we choose as our \meta{delimiter} the +% first one which is not in the \meta{token list}. +% +% Every delimiter in the set obeys the first condition: |#6|~does not +% contain~\meta{A} hence cannot be overlapping with the \meta{token +% list} and the \meta{delimiter}, and it cannot be within the +% \meta{delimiter} since it would have to be in one of the two +% \meta{B} hence be equal to this single token (or empty, but this is +% an error case filtered separately). Given the particular form of +% these delimiters, for which no prefix is also a suffix, the second +% condition is actually a consequence of the weaker condition that the +% \meta{delimiter} we choose does not appear in the \meta{token list}. +% Additionally, the set of delimiters is such that a \meta{token list} +% of $n$~tokens can contain at most $O(n^{1/2})$ of them, hence we +% find a \meta{delimiter} with at most $O(n^{1/2})$ tokens in a time +% at most $O(n^{3/2})$. Bear in mind that these upper bounds are +% reached only in very contrived scenarios: we include the case +% \enquote{\meta{A}} in the list of delimiters to try, so that the +% \meta{delimiter} is simply \cs{q_mark} in the most common +% situation where neither the \meta{token list} nor the \meta{pattern} +% contains \cs{q_mark}. +% +% Let us now ahead, optimizing for this most common case. First, two +% special cases: an empty \meta{pattern}~|#6| is an error, and if +% |#1|~is absent from both the \meta{token list}~|#5| and the +% \meta{pattern}~|#6| then we can use it as the \meta{delimiter} +% through \cs{@@_replace_auxii:nNNNnn} |{#1}|. Otherwise, we end up +% calling \cs{@@_replace:NnNNNnn} repeatedly with the first two +% arguments \cs{q_mark} |{?}|, |\?| |{??}|, |\??| |{???}|, and so on, +% until |#6|~does not contain the control sequence~|#1|, which we take +% as our~\meta{A}. The argument~|#2| only serves to collect~|?| +% characters for~|#1|. Note that the order of the tests means that +% the first two are done every time, which is wasteful (for instance, +% we repeatedly test for the emptyness of~|#6|). However, this is +% rare enough not to matter. Finally, choose~\meta{B} to be +% \cs{q_nil} or~\cs{q_stop} such that it is not equal to~|#6|. +% +% The \cs{@@_replace_auxi:NnnNNNnn} auxiliary receives \Arg{A} and +% |{|\meta{A}$^n$\meta{B}|}| as its arguments, initially with $n=1$. +% If \enquote{\meta{A} \meta{A}$^n$\meta{B} \meta{A}$^n$\meta{B}} is +% in the \meta{token list} then increase~$n$ and try again. Once it +% is not anymore in the \meta{token list} we take it as our +% \meta{delimiter} and pass this to the \texttt{auxii} auxiliary. +% \begin{macrocode} +\cs_new_protected:Npn \@@_replace:NnNNNnn #1#2#3#4#5#6#7 + { + \tl_if_empty:nTF {#6} + { + \__kernel_msg_error:nnx { kernel } { empty-search-pattern } + { \tl_to_str:n {#7} } + } + { + \tl_if_in:onTF { #5 #6 } {#1} + { + \tl_if_in:nnTF {#6} {#1} + { \exp_args:Nc \@@_replace:NnNNNnn {#2} {#2?} } + { + \quark_if_nil:nTF {#6} + { \@@_replace_auxi:NnnNNNnn #5 {#1} { #1 \q_stop } } + { \@@_replace_auxi:NnnNNNnn #5 {#1} { #1 \q_nil } } + } + } + { \@@_replace_auxii:nNNNnn {#1} } + #3#4#5 {#6} {#7} + } + } +\cs_new_protected:Npn \@@_replace_auxi:NnnNNNnn #1#2#3 + { + \tl_if_in:NnTF #1 { #2 #3 #3 } + { \@@_replace_auxi:NnnNNNnn #1 { #2 #3 } {#2} } + { \@@_replace_auxii:nNNNnn { #2 #3 #3 } } + } +% \end{macrocode} +% The auxiliary \cs{@@_replace_auxii:nNNNnn} receives the following +% arguments: +% \begin{quote} +% \Arg{delimiter} \meta{function} \meta{assignment} \\ +% \meta{tl~var} \Arg{pattern} \Arg{replacement} +% \end{quote} +% All of its work is done between +% \cs{group_align_safe_begin:} and \cs{group_align_safe_end:} to avoid +% issues in alignments. It does the actual replacement within +% |#3|~|#4|~|{...}|, an \texttt{x}-expanding \meta{assignment}~|#3| to +% the \meta{tl~var}~|#4|. The auxiliary \cs{@@_replace_next:w} is +% called, followed by the \meta{token list}, some tokens including the +% \meta{delimiter}~|#1|, followed by the \meta{pattern}~|#5|. +% This auxiliary finds an argument delimited by~|#5| (the presence of +% a trailing~|#5| avoids runaway arguments) and calls +% \cs{@@_replace_wrap:w} to test whether this |#5| is found within the +% \meta{token list} or is the trailing one. +% +% If on the one hand it is found within the \meta{token list}, then +% |##1| cannot contain the \meta{delimiter}~|#1| that we worked so +% hard to obtain, thus \cs{@@_replace_wrap:w} gets~|##1| as its own +% argument~|##1|, and protects it against +% the \texttt{x}-expanding assignment. It also finds \cs{exp_not:n} +% as~|##2| and does nothing to it, thus letting through \cs{exp_not:n} +% \Arg{replacement} into the assignment. Note that +% \cs{@@_replace_next:w} and \cs{@@_replace_wrap:w} are always called +% followed by two empty brace groups. These are safe because no +% delimiter can match them. They prevent losing braces when grabbing +% delimited arguments, but require the use of \cs{exp_not:o} and +% \cs{use_none:nn}, rather than simply \cs{exp_not:n}. +% Afterwards, \cs{@@_replace_next:w} is called +% to repeat the replacement, or \cs{@@_replace_wrap:w} if we only want +% a single replacement. In this second case, |##1| is the +% \meta{remaining tokens} in the \meta{token list} and |##2| is some +% \meta{ending code} which ends the assignment and removes the +% trailing tokens |#5| using some \cs{if_false:} |{| \cs{fi:} |}| +% trickery because~|#5| may contain any delimiter. +% +% If on the other hand the argument~|##1| of \cs{@@_replace_next:w} is +% delimited by the trailing \meta{pattern}~|#5|, then |##1| is +% \enquote{\{ \} \{ \} \meta{token list} \meta{delimiter} +% \Arg{ending code}}, hence \cs{@@_replace_wrap:w} finds +% \enquote{\{ \} \{ \} \meta{token list}} as |##1| and the +% \meta{ending code} as~|##2|. It leaves the \meta{token list} into +% the assignment and unbraces the \meta{ending code} which removes +% what remains (essentially the \meta{delimiter} and +% \meta{replacement}). +% \begin{macrocode} +\cs_new_protected:Npn \@@_replace_auxii:nNNNnn #1#2#3#4#5#6 + { + \group_align_safe_begin: + \cs_set:Npn \@@_replace_wrap:w ##1 #1 ##2 + { \exp_not:o { \use_none:nn ##1 } ##2 } + \cs_set:Npx \@@_replace_next:w ##1 #5 + { + \exp_not:N \@@_replace_wrap:w ##1 + \exp_not:n { #1 } + \exp_not:n { \exp_not:n {#6} } + \exp_not:n { #2 { } { } } + } + #3 #4 + { + \exp_after:wN \@@_replace_next:w + \exp_after:wN { \exp_after:wN } + \exp_after:wN { \exp_after:wN } + #4 + #1 + { + \if_false: { \fi: } + \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: + } + #5 + } + \group_align_safe_end: + } +\cs_new_eq:NN \@@_replace_wrap:w ? +\cs_new_eq:NN \@@_replace_next:w ? +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_remove_once:Nn, \tl_remove_once:cn} +% \begin{macro}{\tl_gremove_once:Nn, \tl_gremove_once:cn} +% Removal is just a special case of replacement. +% \begin{macrocode} +\cs_new_protected:Npn \tl_remove_once:Nn #1#2 + { \tl_replace_once:Nnn #1 {#2} { } } +\cs_new_protected:Npn \tl_gremove_once:Nn #1#2 + { \tl_greplace_once:Nnn #1 {#2} { } } +\cs_generate_variant:Nn \tl_remove_once:Nn { c } +\cs_generate_variant:Nn \tl_gremove_once:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_remove_all:Nn, \tl_remove_all:cn} +% \begin{macro}{\tl_gremove_all:Nn, \tl_gremove_all:cn} +% Removal is just a special case of replacement. +% \begin{macrocode} +\cs_new_protected:Npn \tl_remove_all:Nn #1#2 + { \tl_replace_all:Nnn #1 {#2} { } } +\cs_new_protected:Npn \tl_gremove_all:Nn #1#2 + { \tl_greplace_all:Nnn #1 {#2} { } } +\cs_generate_variant:Nn \tl_remove_all:Nn { c } +\cs_generate_variant:Nn \tl_gremove_all:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Token list conditionals} +% +% \begin{macro}[pTF]{\tl_if_blank:n, \tl_if_blank:V, \tl_if_blank:o} +% \begin{macro}{\@@_if_blank_p:NNw} +% \TeX{} skips spaces when reading a non-delimited arguments. Thus, +% a \meta{token list} is blank if and only if \cs{use_none:n} +% \meta{token list} |?| is empty after one expansion. The auxiliary +% \cs{@@_if_empty_if:o} is a fast emptyness test, converting its +% argument to a string (after one expansion) and using the test +% \cs{if_meaning:w} \cs{q_nil} |...| \cs{q_nil}. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_blank:n #1 { p , T , F , TF } + { + \@@_if_empty_if:o { \use_none:n #1 ? } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_blank:n + { V , o } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\tl_if_empty:N, \tl_if_empty:c} +% These functions check whether the token list in the argument is +% empty and execute the proper code from their argument(s). +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_empty:N #1 { p , T , F , TF } + { + \if_meaning:w #1 \c_empty_tl + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_empty:N + { c } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF]{\tl_if_empty:n, \tl_if_empty:V} +% Convert the argument to a string: this is empty if and only if +% the argument is. Then |\if_meaning:w \q_nil ... \q_nil| is +% \texttt{true} if and only if the string |...| is empty. +% It could be tempting to use |\if_meaning:w \q_nil #1 \q_nil| directly. +% This fails on a token +% list starting with \cs{q_nil} of course but more troubling is the +% case where argument is a complete conditional such as \cs{if_true:} +% a \cs{else:} b \cs{fi:} because then \cs{if_true:} is used by +% \cs{if_meaning:w}, the test turns out \texttt{false}, the \cs{else:} +% executes the \texttt{false} branch, the \cs{fi:} ends it and the +% \cs{q_nil} at the end +% starts executing\dots{} +% \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_generate_conditional_variant:Nnn \tl_if_empty:n + { V } { p , TF , T , F } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF,documented-as=\tl_if_empty:nTF]{\tl_if_empty:o} +% \begin{macro}[EXP]{\@@_if_empty_if:o} +% The auxiliary function \cs{@@_if_empty_if:o} is for use +% in various token list conditionals which reduce to testing +% if a given token list is empty after applying a simple function +% to it. +% The test for emptiness is based on \cs{tl_if_empty:nTF}, but +% the expansion is hard-coded for efficiency, as this auxiliary +% function is used in several places. +% We don't put \cs{prg_return_true:} and so on in the definition of +% the auxiliary, because that would prevent an optimization applied to +% conditionals that end with this code. +% \begin{macrocode} +\cs_new:Npn \@@_if_empty_if:o #1 + { + \exp_after:wN \if_meaning:w \exp_after:wN \q_nil + \__kernel_tl_to_str:w \exp_after:wN {#1} \q_nil + } +\prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } + { + \@@_if_empty_if:o {#1} + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \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} +% Returns \cs{c_true_bool} if and only if the two token list variables are +% equal. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_eq:NN #1#2 { p , T , F , TF } + { + \if_meaning:w #1 #2 + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_eq:NN + { Nc , c , cc } { p , TF , T , F } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\tl_if_eq:nn} +% \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl} +% A simple store and compare routine. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \tl_if_eq:nn #1#2 { T , F , TF } + { + \group_begin: + \tl_set:Nn \l_@@_internal_a_tl {#1} + \tl_set:Nn \l_@@_internal_b_tl {#2} + \exp_after:wN + \group_end: + \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\tl_new:N \l_@@_internal_a_tl +\tl_new:N \l_@@_internal_b_tl +% \end{macrocode} +% \end{variable} +% \end{macro} +% +% \begin{macro}[TF]{\tl_if_in:Nn, \tl_if_in:cn} +% See \cs{tl_if_in:nnTF} for further comments. Here we simply +% expand the token list variable and pass it to \cs{tl_if_in:nnTF}. +% \begin{macrocode} +\cs_new_protected:Npn \tl_if_in:NnT { \exp_args:No \tl_if_in:nnT } +\cs_new_protected:Npn \tl_if_in:NnF { \exp_args:No \tl_if_in:nnF } +\cs_new_protected:Npn \tl_if_in:NnTF { \exp_args:No \tl_if_in:nnTF } +\prg_generate_conditional_variant:Nnn \tl_if_in:Nn + { c } { T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\tl_if_in:nn, \tl_if_in:Vn, \tl_if_in:on, \tl_if_in:no} +% Once more, the test relies on the emptiness test for robustness. +% The function \cs{@@_tmp:w} removes tokens until the first occurrence +% of |#2|. If this does not appear in |#1|, then the final |#2| is removed, +% leaving an empty token list. Otherwise some tokens remain, and the +% test is \texttt{false}. See \cs{tl_if_empty:nTF} for details on +% the emptiness test. +% +% Treating correctly cases like +% |\tl_if_in:nnTF {a state}{states}|, where |#1#2| contains |#2| before +% the end, requires special care. +% To cater for this case, we insert |{}{}| between the two token +% lists. This marker may not appear in |#2| because of \TeX{} limitations +% on what can delimit a parameter, hence we are safe. Using two brace +% groups makes the test work also for empty arguments. +% The \cs{if_false:} constructions are a faster way to do +% \cs{group_align_safe_begin:} and \cs{group_align_safe_end:}. +% The \cs{scan_stop:} ensures that \texttt{f}-expanding +% \cs{tl_if_in:nn} does not lead to unbalanced braces. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \tl_if_in:nn #1#2 { T , F , TF } + { + \scan_stop: + \if_false: { \fi: + \cs_set:Npn \@@_tmp:w ##1 #2 { } + \tl_if_empty:oTF { \@@_tmp:w #1 {} {} #2 } + { \prg_return_false: } { \prg_return_true: } + \if_false: } \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_in:nn + { V , o , no } { T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF, EXP]{\tl_if_novalue:n} +% \begin{macro}[EXP]{\@@_if_novalue:w} +% Tests for |-NoValue-|: this is similar to \cs{tl_if_in:nn} but set +% up to be expandable and to check the value exactly. The question +% mark prevents the auxiliary from losing braces. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \prg_new_conditional:Npnn \tl_if_novalue:n ##1 + { p , T , F , TF } + { + \str_if_eq:onTF + { \@@_if_novalue:w ? ##1 { } #1 } + { ? { } #1 } + { \prg_return_true: } + { \prg_return_false: } + } + \cs_new:Npn \@@_if_novalue:w ##1 #1 {##1} + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +% \end{macrocode} +% \end{macro} +% \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} +% \begin{macro}[EXP,pTF]{\@@_if_single:n} +% This test is similar to \cs{tl_if_empty:nTF}. Expanding +% \cs{use_none:nn} |#1| |??| once yields an empty result if |#1| is +% blank, a single~|?| if |#1| has a single item, and otherwise yields +% some tokens ending with |??|. Then, \cs{tl_to_str:n} makes sure +% there are no odd category codes. An earlier version would compare +% the result to a single~|?| using string comparison, but the Lua call +% is slow in \LuaTeX{}. Instead, \cs{@@_if_single:nnw} picks the +% second token in front of it. If |#1| is empty, this token is +% the trailing~|?| and the catcode test yields \texttt{false}. If +% |#1| has a single item, the token is~|^| and the catcode test +% yields \texttt{true}. Otherwise, it is one of the characters +% resulting from \cs{tl_to_str:n}, and the catcode test yields +% \texttt{false}. Note that \cs{if_catcode:w} and +% \cs{__kernel_tl_to_str:w} are primitives that take care of +% expansion. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF } + { + \if_catcode:w ^ \exp_after:wN \@@_if_single:nnw + \__kernel_tl_to_str:w + \exp_after:wN { \use_none:nn #1 ?? } ^ ? \q_stop + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new:Npn \@@_if_single:nnw #1#2#3 \q_stop {#2} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \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 +% emptiness. For the next case, an empty token list is not a single +% token. Finally, we have a non-empty token list starting with a +% space or a brace group. Applying \texttt{f}-expansion yields an +% empty result if and only if the token list is a single space. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF } + { + \tl_if_head_is_N_type:nTF {#1} + { \@@_if_empty_if:o { \use_none:n #1 } } + { + \tl_if_empty:nTF {#1} + { \if_false: } + { \@@_if_empty_if:o { \exp:w \exp_end_continue_f:w #1 } } + } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP, noTF]{\tl_case:Nn, \tl_case:cn} +% \begin{macro}[EXP]{\@@_case:nnTF} +% \begin{macro}[EXP]{\@@_case:Nw} +% \begin{macro}[EXP]{\@@_case_end:nw} +% The aim here is to allow the case statement to be evaluated +% using a known number of expansion steps (two), and without +% needing to use an explicit \enquote{end of recursion} marker. +% That is achieved by using the test input as the final case, +% as this is always true. The trick is then to tidy up +% the output such that the appropriate case code plus either +% the \texttt{true} or \texttt{false} branch code is inserted. +% \begin{macrocode} +\cs_new:Npn \tl_case:Nn #1#2 + { + \exp:w + \@@_case:NnTF #1 {#2} { } { } + } +\cs_new:Npn \tl_case:NnT #1#2#3 + { + \exp:w + \@@_case:NnTF #1 {#2} {#3} { } + } +\cs_new:Npn \tl_case:NnF #1#2#3 + { + \exp:w + \@@_case:NnTF #1 {#2} { } {#3} + } +\cs_new:Npn \tl_case:NnTF #1#2 + { + \exp:w + \@@_case:NnTF #1 {#2} + } +\cs_new:Npn \@@_case:NnTF #1#2#3#4 + { \@@_case:Nw #1 #2 #1 { } \q_mark {#3} \q_mark {#4} \q_stop } +\cs_new:Npn \@@_case:Nw #1#2#3 + { + \tl_if_eq:NNTF #1 #2 + { \@@_case_end:nw {#3} } + { \@@_case:Nw #1 } + } +\cs_generate_variant:Nn \tl_case:Nn { c } +\prg_generate_conditional_variant:Nnn \tl_case:Nn + { c } { T , F , TF } +% \end{macrocode} +% To tidy up the recursion, there are two outcomes. If there was a hit to +% one of the cases searched for, then |#1| is the code to insert, +% |#2| is the \emph{next} case to check on and |#3| is all of +% the rest of the cases code. That means that |#4| is the \texttt{true} +% branch code, and |#5| tidies up the spare \cs{q_mark} and the +% \texttt{false} branch. On the other hand, if none of the cases matched +% then we arrive here using the \enquote{termination} case of comparing +% the search with itself. That means that |#1| is empty, |#2| is +% the first \cs{q_mark} and so |#4| is the \texttt{false} code (the +% \texttt{true} code is mopped up by |#3|). +% \begin{macrocode} +\cs_new:Npn \@@_case_end:nw #1#2#3 \q_mark #4#5 \q_stop + { \exp_end: #1 #4 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Mapping to token lists} +% +% \begin{macro}{\tl_map_function:nN} +% \begin{macro}{\tl_map_function:NN, \tl_map_function:cN} +% \begin{macro}{\@@_map_function:Nn} +% Expandable loop macro for token lists. These have the advantage of not +% needing to test if the argument is empty, because if it is, the stop +% marker is read immediately and the loop terminated. +% \begin{macrocode} +\cs_new:Npn \tl_map_function:nN #1#2 + { + \@@_map_function:Nn #2 #1 + \q_recursion_tail + \prg_break_point:Nn \tl_map_break: { } + } +\cs_new:Npn \tl_map_function:NN + { \exp_args:No \tl_map_function:nN } +\cs_new:Npn \@@_map_function:Nn #1#2 + { + \quark_if_recursion_tail_break:nN {#2} \tl_map_break: + #1 {#2} \@@_map_function:Nn #1 + } +\cs_generate_variant:Nn \tl_map_function:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_map_inline:nn} +% \begin{macro}{\tl_map_inline:Nn, \tl_map_inline:cn} +% The inline functions are straight forward by now. We use a little +% trick with the counter \cs{g__kernel_prg_map_int} to make +% them nestable. We can also make use of \cs{@@_map_function:Nn} +% from before. +% \begin{macrocode} +\cs_new_protected:Npn \tl_map_inline:nn #1#2 + { + \int_gincr:N \g__kernel_prg_map_int + \cs_gset_protected:cpn + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2} + \exp_args:Nc \@@_map_function:Nn + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + #1 \q_recursion_tail + \prg_break_point:Nn \tl_map_break: + { \int_gdecr:N \g__kernel_prg_map_int } + } +\cs_new_protected:Npn \tl_map_inline:Nn + { \exp_args:No \tl_map_inline:nn } +\cs_generate_variant:Nn \tl_map_inline:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_map_variable:nNn} +% \begin{macro}{\tl_map_variable:NNn, \tl_map_variable:cNn} +% \begin{macro}{\@@_map_variable:Nnn} +% \cs{tl_map_variable:nNn} \meta{token list} \meta{tl~var} +% \meta{action} assigns \meta{tl~var} to each element and executes +% \meta{action}. The assignment to \meta{tl~var} is done after the +% quark test so that this variable does not get set to a quark. +% \begin{macrocode} +\cs_new_protected:Npn \tl_map_variable:nNn #1#2#3 + { + \@@_map_variable:Nnn #2 {#3} #1 + \q_recursion_tail + \prg_break_point:Nn \tl_map_break: { } + } +\cs_new_protected:Npn \tl_map_variable:NNn + { \exp_args:No \tl_map_variable:nNn } +\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3 + { + \quark_if_recursion_tail_break:nN {#3} \tl_map_break: + \tl_set:Nn #1 {#3} + \use:n {#2} + \@@_map_variable:Nnn #1 {#2} + } +\cs_generate_variant:Nn \tl_map_variable:NNn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_map_break:} +% \begin{macro}{\tl_map_break:n} +% The break statements use the general \cs{prg_map_break:Nn}. +% \begin{macrocode} +\cs_new:Npn \tl_map_break: + { \prg_map_break:Nn \tl_map_break: { } } +\cs_new:Npn \tl_map_break:n + { \prg_map_break:Nn \tl_map_break: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Using token lists} +% +% \begin{macro}{\tl_to_str:n, \tl_to_str:V} +% Another name for a primitive: defined in \pkg{l3basics}. +% \begin{macrocode} +\cs_generate_variant:Nn \tl_to_str:n { V } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_to_str:N, \tl_to_str:c} +% These functions return the replacement text of a token list as a +% string. +% \begin{macrocode} +\cs_new:Npn \tl_to_str:N #1 { \__kernel_tl_to_str:w \exp_after:wN {#1} } +\cs_generate_variant:Nn \tl_to_str:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_use:N, \tl_use:c} +% Token lists which are simply not defined give a clear \TeX{} +% error here. No such luck for ones equal to \cs{scan_stop:} so +% instead a test is made and if there is an issue an error is forced. +% \begin{macrocode} +\cs_new:Npn \tl_use:N #1 + { + \tl_if_exist:NTF #1 {#1} + { + \__kernel_msg_expandable_error:nnn + { kernel } { bad-variable } {#1} + } + } +\cs_generate_variant:Nn \tl_use:N { c } +% \end{macrocode} +% \end{macro} +% +% \subsection{Working with the contents of token lists} +% +% \begin{macro}{\tl_count:n, \tl_count:V, \tl_count:o} +% \begin{macro}{\tl_count:N, \tl_count:c} +% \begin{macro}{\@@_count:n} +% Count number of elements within a token list or token list +% variable. Brace groups within the list are read as a single +% element. Spaces are ignored. +% \cs{@@_count:n} grabs the element and replaces it by |+1|. +% The |0| ensures that it works on an empty list. +% \begin{macrocode} +\cs_new:Npn \tl_count:n #1 + { + \int_eval:n + { 0 \tl_map_function:nN {#1} \@@_count:n } + } +\cs_new:Npn \tl_count:N #1 + { + \int_eval:n + { 0 \tl_map_function:NN #1 \@@_count:n } + } +\cs_new:Npn \@@_count:n #1 { + 1 } +\cs_generate_variant:Nn \tl_count:n { V , o } +\cs_generate_variant:Nn \tl_count:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% \begin{macro}[EXP]{\tl_count_tokens:n} +% \begin{macro}[EXP]{\@@_act_count_normal:nN, +% \@@_act_count_group:nn, \@@_act_count_space:n} +% The token count 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{exp_end:} inserted by +% \cs{@@_act_end:wn} (which is technically implemented as \cs{c_zero_int}). +% Somewhat a hack! +% \begin{macrocode} +\cs_new:Npn \tl_count_tokens:n #1 + { + \int_eval:n + { + \@@_act:NNNnn + \@@_act_count_normal:nN + \@@_act_count_group:nn + \@@_act_count_space:n + { } + {#1} + } + } +\cs_new:Npn \@@_act_count_normal:nN #1 #2 { 1 + } +\cs_new:Npn \@@_act_count_space:n #1 { 1 + } +\cs_new:Npn \@@_act_count_group:nn #1 #2 + { 2 + \tl_count_tokens:n {#2} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_reverse_items:n} +% \begin{macro}{\@@_reverse_items:nwNwn} +% \begin{macro}{\@@_reverse_items:wn} +% Reversal of a token list is done by taking one item at a time +% and putting it after \cs{q_stop}. +% \begin{macrocode} +\cs_new:Npn \tl_reverse_items:n #1 + { + \@@_reverse_items:nwNwn #1 ? + \q_mark \@@_reverse_items:nwNwn + \q_mark \@@_reverse_items:wn + \q_stop { } + } +\cs_new:Npn \@@_reverse_items:nwNwn #1 #2 \q_mark #3 #4 \q_stop #5 + { + #3 #2 + \q_mark \@@_reverse_items:nwNwn + \q_mark \@@_reverse_items:wn + \q_stop { {#1} #5 } + } +\cs_new:Npn \@@_reverse_items:wn #1 \q_stop #2 + { \exp_not:o { \use_none:nn #2 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_trim_spaces:n, \tl_trim_spaces:o} +% \begin{macro}{\tl_trim_spaces_apply:nN, \tl_trim_spaces_apply:oN} +% \begin{macro} +% { +% \tl_trim_spaces:N, \tl_trim_spaces:c, +% \tl_gtrim_spaces:N, \tl_gtrim_spaces:c +% } +% Trimming spaces from around the input is deferred to an internal +% function whose first argument is the token list to trim, augmented +% by an initial \cs{q_mark}, and whose second argument is a +% \meta{continuation}, which receives as a braced argument +% \cs{use_none:n} \cs{q_mark} \meta{trimmed token list}. In the case +% at hand, we take \cs{exp_not:o} as our continuation, so that space +% trimming behaves correctly within an \texttt{x}-type expansion. +% \begin{macrocode} +\cs_new:Npn \tl_trim_spaces:n #1 + { \@@_trim_spaces:nn { \q_mark #1 } \exp_not:o } +\cs_generate_variant:Nn \tl_trim_spaces:n { o } +\cs_new:Npn \tl_trim_spaces_apply:nN #1#2 + { \@@_trim_spaces:nn { \q_mark #1 } { \exp_args:No #2 } } +\cs_generate_variant:Nn \tl_trim_spaces_apply:nN { o } +\cs_new_protected:Npn \tl_trim_spaces:N #1 + { \tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } +\cs_new_protected:Npn \tl_gtrim_spaces:N #1 + { \tl_gset:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } } +\cs_generate_variant:Nn \tl_trim_spaces:N { c } +\cs_generate_variant:Nn \tl_gtrim_spaces:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_trim_spaces:nn} +% \begin{macro} +% { +% \@@_trim_spaces_auxi:w, \@@_trim_spaces_auxii:w, +% \@@_trim_spaces_auxiii:w, \@@_trim_spaces_auxiv:w +% } +% Trimming spaces from around the input is done using delimited +% arguments and quarks, and to get spaces at odd places in the +% definitions, we nest those in \cs{@@_tmp:w}, which then receives +% a single space as its argument: |#1| is \verb*+ +. +% Removing leading spaces is done with \cs{@@_trim_spaces_auxi:w}, +% which loops until \cs{q_mark}\verb*+ + matches the end of the token +% list: then |##1| is the token list and |##3| is +% \cs{@@_trim_spaces_auxii:w}. This hands the relevant tokens to the +% loop \cs{@@_trim_spaces_auxiii:w}, responsible for trimming +% trailing spaces. The end is reached when \verb*+ + \cs{q_nil} +% matches the one present in the definition of \cs{tl_trim_spacs:n}. +% Then \cs{@@_trim_spaces_auxiv:w} puts the token list into a group, +% with \cs{use_none:n} placed there to gobble a lingering \cs{q_mark}, +% and feeds this to the \meta{continuation}. +% \begin{macrocode} +\cs_set:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_trim_spaces:nn ##1 + { + \@@_trim_spaces_auxi:w + ##1 + \q_nil + \q_mark #1 { } + \q_mark \@@_trim_spaces_auxii:w + \@@_trim_spaces_auxiii:w + #1 \q_nil + \@@_trim_spaces_auxiv:w + \q_stop + } + \cs_new:Npn \@@_trim_spaces_auxi:w ##1 \q_mark #1 ##2 \q_mark ##3 + { + ##3 + \@@_trim_spaces_auxi:w + \q_mark + ##2 + \q_mark #1 {##1} + } + \cs_new:Npn \@@_trim_spaces_auxii:w + \@@_trim_spaces_auxi:w \q_mark \q_mark ##1 + { + \@@_trim_spaces_auxiii:w + ##1 + } + \cs_new:Npn \@@_trim_spaces_auxiii:w ##1 #1 \q_nil ##2 + { + ##2 + ##1 \q_nil + \@@_trim_spaces_auxiii:w + } + \cs_new:Npn \@@_trim_spaces_auxiv:w ##1 \q_nil ##2 \q_stop ##3 + { ##3 { \use_none:n ##1 } } + } +\@@_tmp:w { ~ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% {\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn, \tl_sort:nN} +% Implemented in \pkg{l3sort}. +% \end{macro} +% +% \subsection{Token by token changes} +% +% \begin{variable}{\q_@@_act_mark, \q_@@_act_stop} +% The \cs[no-index]{@@_act_\ldots{}} functions may be applied to any token list. +% Hence, we use two private quarks, to allow any token, even quarks, +% in the token list. +% Only \cs{q_@@_act_mark} and \cs{q_@@_act_stop} may not appear +% in the token lists manipulated by \cs{@@_act:NNNnn} functions. +% No quark module yet, so do things by hand. +% \begin{macrocode} +\cs_new_nopar:Npn \q_@@_act_mark { \q_@@_act_mark } +\cs_new_nopar:Npn \q_@@_act_stop { \q_@@_act_stop } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[EXP]{\@@_act:NNNnn} +% \begin{macro}[EXP]{\@@_act_output:n, \@@_act_reverse_output:n} +% \begin{macro}[EXP]{\@@_act_loop:w} +% \begin{macro}[EXP]{\@@_act_normal:NwnNNN} +% \begin{macro}[EXP]{\@@_act_group:nwnNNN} +% \begin{macro}[EXP]{\@@_act_space:wwnNNN} +% \begin{macro}[EXP]{\@@_act_end:w} +% To help control the expansion, \cs{@@_act:NNNnn} should always +% be proceeded by \cs{exp:w} and ends by producing \cs{exp_end:} +% once the result has been obtained. Then loop over tokens, +% groups, and spaces in |#5|. The marker \cs{q_@@_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{@@_act_result:n}. +% \begin{macrocode} +\cs_new:Npn \@@_act:NNNnn #1#2#3#4#5 + { + \group_align_safe_begin: + \@@_act_loop:w #5 \q_@@_act_mark \q_@@_act_stop + {#4} #1 #2 #3 + \@@_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_@@_act_mark}, the end of the list. Then +% leave \cs{exp_end:} and the result in the input stream, +% to terminate the expansion of \cs{exp:w}. +% 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{@@_act_space:wwnNNN} gobble the space. +% \begin{macrocode} +\cs_new:Npn \@@_act_loop:w #1 \q_@@_act_stop + { + \tl_if_head_is_N_type:nTF {#1} + { \@@_act_normal:NwnNNN } + { + \tl_if_head_is_group:nTF {#1} + { \@@_act_group:nwnNNN } + { \@@_act_space:wwnNNN } + } + #1 \q_@@_act_stop + } +\cs_new:Npn \@@_act_normal:NwnNNN #1 #2 \q_@@_act_stop #3#4 + { + \if_meaning:w \q_@@_act_mark #1 + \exp_after:wN \@@_act_end:wn + \fi: + #4 {#3} #1 + \@@_act_loop:w #2 \q_@@_act_stop + {#3} #4 + } +\cs_new:Npn \@@_act_end:wn #1 \@@_act_result:n #2 + { \group_align_safe_end: \exp_end: #2 } +\cs_new:Npn \@@_act_group:nwnNNN #1 #2 \q_@@_act_stop #3#4#5 + { + #5 {#3} {#1} + \@@_act_loop:w #2 \q_@@_act_stop + {#3} #4 #5 + } +\exp_last_unbraced:NNo + \cs_new:Npn \@@_act_space:wwnNNN \c_space_tl #1 \q_@@_act_stop #2#3#4#5 + { + #5 {#2} + \@@_act_loop:w #1 \q_@@_act_stop + {#2} #3 #4 #5 + } +% \end{macrocode} +% Typically, the output is done to the right of what was already output, +% using \cs{@@_act_output:n}, but for the \cs{@@_act_reverse} functions, +% it should be done to the left. +% \begin{macrocode} +\cs_new:Npn \@@_act_output:n #1 #2 \@@_act_result:n #3 + { #2 \@@_act_result:n { #3 #1 } } +\cs_new:Npn \@@_act_reverse_output:n #1 #2 \@@_act_result:n #3 + { #2 \@@_act_result:n { #1 #3 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\tl_reverse:n, \tl_reverse:o, \tl_reverse:V} +% \begin{macro}[EXP]{\@@_reverse_normal:nN} +% \begin{macro}[EXP]{\@@_reverse_group_preserve:nn} +% \begin{macro}[EXP]{\@@_reverse_space:n} +% The goal here is to reverse without losing spaces nor braces. +% This is done using the general internal function \cs{@@_act:NNNnn}. +% Spaces and \enquote{normal} tokens are output on the left of the current +% output. Grouped tokens are output to the left but without any reversal +% within the group. All of the internal functions here drop one argument: +% this is needed by \cs{@@_act:NNNnn} when changing case (to record +% which direction the change is in), but not when reversing the tokens. +% \begin{macrocode} +\cs_new:Npn \tl_reverse:n #1 + { + \__kernel_exp_not:w \exp_after:wN + { + \exp:w + \@@_act:NNNnn + \@@_reverse_normal:nN + \@@_reverse_group_preserve:nn + \@@_reverse_space:n + { } + {#1} + } + } +\cs_generate_variant:Nn \tl_reverse:n { o , V } +\cs_new:Npn \@@_reverse_normal:nN #1#2 + { \@@_act_reverse_output:n {#2} } +\cs_new:Npn \@@_reverse_group_preserve:nn #1#2 + { \@@_act_reverse_output:n { {#2} } } +\cs_new:Npn \@@_reverse_space:n #1 + { \@@_act_reverse_output:n { ~ } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c} +% This reverses the list, leaving \cs{exp_stop_f:} in front, +% which stops the \texttt{f}-expansion. +% \begin{macrocode} +\cs_new_protected:Npn \tl_reverse:N #1 + { \tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } +\cs_new_protected:Npn \tl_greverse:N #1 + { \tl_gset:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } } +\cs_generate_variant:Nn \tl_reverse:N { c } +\cs_generate_variant:Nn \tl_greverse:N { c } +% \end{macrocode} +% \end{macro} +% +% \subsection{The first token from a token list} +% +% \begin{macro}{\tl_head:N, \tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f} +% \begin{macro}{\@@_head_auxi:nw, \@@_head_auxii:n} +% \begin{macro}{\tl_head:w} +% \begin{macro}{\tl_tail:N, \tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f} +% Finding the head of a token list expandably always strips braces, which +% is fine as this is consistent with for example mapping to a list. The +% empty brace groups in \cs{tl_head:n} ensure that a blank argument gives an +% empty result. The result is returned within the \tn{unexpanded} primitive. +% The approach here is to use \cs{if_false:} to allow us to use |}| as +% the closing delimiter: this is the only safe choice, as any other token +% would not be able to parse it's own code. Using a marker, we can see if +% what we are grabbing is exactly the marker, or there is anything else to +% deal with. Is there is, there is a loop. If not, tidy up and leave the +% item in the output stream. More detail in +% \url{http://tex.stackexchange.com/a/70168}. +% \begin{macrocode} +\cs_new:Npn \tl_head:n #1 + { + \__kernel_exp_not:w + \if_false: { \fi: \@@_head_auxi:nw #1 { } \q_stop } + } +\cs_new:Npn \@@_head_auxi:nw #1#2 \q_stop + { + \exp_after:wN \@@_head_auxii:n \exp_after:wN { + \if_false: } \fi: {#1} + } +\cs_new:Npn \@@_head_auxii:n #1 + { + \exp_after:wN \if_meaning:w \exp_after:wN \q_nil + \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #1 } \q_nil + \exp_after:wN \use_i:nn + \else: + \exp_after:wN \use_ii:nn + \fi: + {#1} + { \if_false: { \fi: \@@_head_auxi:nw #1 } } + } +\cs_generate_variant:Nn \tl_head:n { V , v , f } +\cs_new:Npn \tl_head:w #1#2 \q_stop {#1} +\cs_new:Npn \tl_head:N { \exp_args:No \tl_head:n } +% \end{macrocode} +% To correctly leave the tail of a token list, it's important \emph{not} to +% absorb any of the tail part as an argument. For example, the simple +% definition +% \begin{verbatim} +% \cs_new:Npn \tl_tail:n #1 { \tl_tail:w #1 \q_stop } +% \cs_new:Npn \tl_tail:w #1#2 \q_stop +% \end{verbatim} +% would give the wrong result for |\tl_tail:n { a { bc } }| (the braces would +% be stripped). Thus the only safe way to proceed is to first check that +% there is an item to grab (\emph{i.e.}~that the argument is not blank) and +% assuming there is to dispose of the first item. As with \cs{tl_head:n}, +% the result is protected from further expansion by \tn{unexpanded}. +% While we could optimise the test here, this would leave some tokens +% \enquote{banned} in the input, which we do not have with this definition. +% \begin{macrocode} +\cs_new:Npn \tl_tail:n #1 + { + \__kernel_exp_not:w + \tl_if_blank:nTF {#1} + { { } } + { \exp_after:wN { \use_none:n #1 } } + } +\cs_generate_variant:Nn \tl_tail:n { V , v , f } +\cs_new:Npn \tl_tail:N { \exp_args:No \tl_tail:n } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\tl_if_head_eq_meaning:nN} +% \begin{macro}[pTF]{\tl_if_head_eq_charcode:nN} +% \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 three cases: +% when it has category code $1$ (begin-group token), when it is an +% explicit space, with category code $10$ and character code $32$, or +% when the token list is empty (obviously). +% +% Forgetting temporarily about this issue we would use the following +% test in \cs{tl_if_head_eq_charcode:nN}. Here, \cs{tl_head:w} yields +% the first token of the token list, then passed to \cs{exp_not:N}. +% \begin{verbatim} +% \if_charcode:w +% \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \q_stop +% \exp_not:N #2 +% \end{verbatim} +% The two first special cases are detected by testing if the token +% list starts with an \texttt{N}-type token (the extra |?| sends empty +% token lists to the \texttt{true} branch of this test). 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 it is a space character). An empty argument +% results in \cs{tl_head:w} leaving two tokens: |?| which is taken in +% the \cs{if_charcode:w} test, and \cs{use_none:nn}, which ensures +% that \cs{prg_return_false:} is returned regardless of whether the +% charcode test was \texttt{true} or \texttt{false}. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 { p , T , F , TF } + { + \if_charcode:w + \exp_not:N #2 + \tl_if_head_is_N_type:nTF { #1 ? } + { + \exp_after:wN \exp_not:N + \tl_head:w #1 { ? \use_none:nn } \q_stop + } + { \str_head:n {#1} } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \tl_if_head_eq_charcode:nN + { f } { p , TF , T , F } +% \end{macrocode} +% For \cs{tl_if_head_eq_catcode:nN}, again we detect special cases +% with a \cs{tl_if_head_is_N_type:n}. 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}. Again, for an empty argument, a hack is used, +% removing \cs{prg_return_true:} and \cs{else:} with \cs{use_none:nn} +% in case the catcode test with the (arbitrarily chosen) |?| is +% \texttt{true}. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1 #2 { p , T , F , TF } + { + \if_catcode:w + \exp_not:N #2 + \tl_if_head_is_N_type:nTF { #1 ? } + { + \exp_after:wN \exp_not:N + \tl_head:w #1 { ? \use_none:nn } \q_stop + } + { + \tl_if_head_is_group:nTF {#1} + { \c_group_begin_token } + { \c_space_token } + } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \end{macrocode} +% For \cs{tl_if_head_eq_meaning:nN}, again, detect special cases. In +% the normal case, use \cs{tl_head:w}, with no \cs{exp_not:N} this +% time, since \cs{if_meaning:w} causes no expansion. With an empty +% argument, the test is \texttt{true}, and \cs{use_none:nnn} removes +% |#2| and the usual \cs{prg_return_true:} and \cs{else:}. +% In the special cases, we know that the first token is a character, +% hence \cs{if_charcode:w} and \cs{if_catcode:w} together are enough. +% We combine them in some order, hopefully faster than the reverse. +% Tests are not nested because the arguments may contain unmatched +% primitive conditionals. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 { p , T , F , TF } + { + \tl_if_head_is_N_type:nTF { #1 ? } + { \@@_if_head_eq_meaning_normal:nN } + { \@@_if_head_eq_meaning_special:nN } + {#1} #2 + } +\cs_new:Npn \@@_if_head_eq_meaning_normal:nN #1 #2 + { + \exp_after:wN \if_meaning:w + \tl_head:w #1 { ?? \use_none:nnn } \q_stop #2 + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new:Npn \@@_if_head_eq_meaning_special:nN #1 #2 + { + \if_charcode:w \str_head:n {#1} \exp_not:N #2 + \exp_after:wN \use:n + \else: + \prg_return_false: + \exp_after:wN \use_none:n + \fi: + { + \if_catcode:w \exp_not:N #2 + \tl_if_head_is_group:nTF {#1} + { \c_group_begin_token } + { \c_space_token } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\tl_if_head_is_N_type:n} +% \begin{macro}[EXP]{\@@_if_head_is_N_type:w} +% A token list can be empty, can start with an explicit space +% character (catcode 10 and charcode 32), can start with a begin-group +% token (catcode 1), or start with an \texttt{N}-type argument. In +% the first two cases, the line involving \cs{@@_if_head_is_N_type:w} +% produces~|^| (and otherwise nothing). In the third case +% (begin-group token), the lines involving \cs{exp_after:wN} produce a +% single closing brace. The category code test is thus true exactly +% in the fourth case, which is what we want. One cannot optimize by +% moving one of the |*| to the beginning: if |#1| contains primitive +% conditionals, all of its occurrences must be dealt with before the +% \cs{if_catcode:w} tries to skip the \texttt{true} branch of the +% conditional. +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_head_is_N_type:n #1 { p , T , F , TF } + { + \if_catcode:w + \if_false: { \fi: \@@_if_head_is_N_type:w ? #1 ~ } + \exp_after:wN \use_none:n + \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } } + * * + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new:Npn \@@_if_head_is_N_type:w #1 ~ + { + \tl_if_empty:oTF { \use_none:n #1 } { ^ } { } + \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,pTF]{\tl_if_head_is_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. This could be made faster, but we need all brace 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_is_group:n #1 { p , T , F , TF } + { + \if_catcode:w + \exp_after:wN \use_none:n + \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } } + * * + \prg_return_false: + \else: + \prg_return_true: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP,pTF]{\tl_if_head_is_space:n} +% \begin{macro}[EXP]{\@@_if_head_is_space:w} +% The auxiliary's argument is all that is before the first explicit +% space in |?#1?~|. If that is a single~|?| the test yields +% \texttt{true}. Otherwise, that is more than one token, and the test +% yields \texttt{false}. The work is done within braces (with an +% |\if_false: { \fi: ... }| construction) both to hide potential +% alignment tab characters from \TeX{} in a table, and to allow for +% removing what remains of the token list after its first space. The +% \cs{exp:w} and \cs{exp_end:} ensure that the result of a +% single step of expansion directly yields a balanced token list (no +% trailing closing brace). +% \begin{macrocode} +\prg_new_conditional:Npnn \tl_if_head_is_space:n #1 { p , T , F , TF } + { + \exp:w \if_false: { \fi: + \@@_if_head_is_space:w ? #1 ? ~ } + } +\cs_new:Npn \@@_if_head_is_space:w #1 ~ + { + \tl_if_empty:oTF { \use_none:n #1 } + { \exp_after:wN \exp_end: \exp_after:wN \prg_return_true: } + { \exp_after:wN \exp_end: \exp_after:wN \prg_return_false: } + \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Using a single item} +% +% \begin{macro}{\tl_item:nn, \tl_item:Nn, \tl_item:cn} +% \begin{macro}{\@@_item_aux:nn, \@@_item:nn} +% The idea here is to find the offset of the item from the left, then use +% a loop to grab the correct item. If the resulting offset is too large, +% then \cs{quark_if_recursion_tail_stop:n} terminates the loop, and returns +% nothing at all. +% \begin{macrocode} +\cs_new:Npn \tl_item:nn #1#2 + { + \exp_args:Nf \@@_item:nn + { \exp_args:Nf \@@_item_aux:nn { \int_eval:n {#2} } {#1} } + #1 + \q_recursion_tail + \prg_break_point: + } +\cs_new:Npn \@@_item_aux:nn #1#2 + { + \int_compare:nNnTF {#1} < 0 + { \int_eval:n { \tl_count:n {#2} + 1 + #1 } } + {#1} + } +\cs_new:Npn \@@_item:nn #1#2 + { + \quark_if_recursion_tail_break:nN {#2} \prg_break: + \int_compare:nNnTF {#1} = 1 + { \prg_break:n { \exp_not:n {#2} } } + { \exp_args:Nf \@@_item:nn { \int_eval:n { #1 - 1 } } } + } +\cs_new:Npn \tl_item:Nn { \exp_args:No \tl_item:nn } +\cs_generate_variant:Nn \tl_item:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_rand_item:n, \tl_rand_item:N, \tl_rand_item:c} +% Importantly \cs{tl_item:nn} only evaluates its argument once. +% \begin{macrocode} +\cs_new:Npn \tl_rand_item:n #1 + { + \tl_if_blank:nF {#1} + { \tl_item:nn {#1} { \int_rand:nn { 1 } { \tl_count:n {#1} } } } + } +\cs_new:Npn \tl_rand_item:N { \exp_args:No \tl_rand_item:n } +\cs_generate_variant:Nn \tl_rand_item:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_range:Nnn, \tl_range:cnn, \tl_range:nnn} +% \begin{macro} +% { +% \@@_range:Nnnn, \@@_range:nnnNn, \@@_range:nnNn, \@@_range_skip:w, +% \@@_range:w, \@@_range_skip_spaces:n, \@@_range_collect:nn, +% \@@_range_collect:ff, \@@_range_collect_space:nw, +% \@@_range_collect_N:nN, \@@_range_collect_group:nN, +% } +% To avoid checking for the end of the token list at every step, start +% by counting the number $l$ of items and \enquote{normalizing} the +% bounds, namely clamping them to the interval $[0,l]$ and dealing +% with negative indices. More precisely, \cs{@@_range_items:nnNn} +% receives the number of items to skip at the beginning of the token +% list, the index of the last item to keep, a function which is either +% \cs{@@_range:w} or the token list itself. If nothing should be kept, +% leave |{}|: this stops the \texttt{f}-expansion of \cs{tl_head:f} and that +% function produces an empty result. Otherwise, repeatedly call +% \cs{@@_range_skip:w} to delete |#1| items from the input stream (the +% extra brace group avoids an off-by-one shift). For the braced +% version \cs{@@_range_braced:w} sets up +% \cs{@@_range_collect_braced:w} which stores items one by one in an +% argument after the semicolon. Depending on the first token of the tail, +% either just move it (if it is a space) or also decrement the number of +% items left to find. Eventually, the result is a brace group followed by +% the rest of the token list, and \cs{tl_head:f} cleans up and gives the +% result in \cs{exp_not:n}. +% \begin{macrocode} +\cs_new:Npn \tl_range:Nnn { \exp_args:No \tl_range:nnn } +\cs_generate_variant:Nn \tl_range:Nnn { c } +\cs_new:Npn \tl_range:nnn { \@@_range:Nnnn \@@_range:w } +\cs_new:Npn \@@_range:Nnnn #1#2#3#4 + { + \tl_head:f + { + \exp_args:Nf \@@_range:nnnNn + { \tl_count:n {#2} } {#3} {#4} #1 {#2} + } + } +\cs_new:Npn \@@_range:nnnNn #1#2#3 + { + \exp_args:Nff \@@_range:nnNn + { + \exp_args:Nf \@@_range_normalize:nn + { \int_eval:n { #2 - 1 } } {#1} + } + { + \exp_args:Nf \@@_range_normalize:nn + { \int_eval:n {#3} } {#1} + } + } +\cs_new:Npn \@@_range:nnNn #1#2#3#4 + { + \if_int_compare:w #2 > #1 \exp_stop_f: \else: + \exp_after:wN { \exp_after:wN } + \fi: + \exp_after:wN #3 + \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ; + \exp_after:wN { \exp:w \@@_range_skip:w #1 ; { } #4 } + } +\cs_new:Npn \@@_range_skip:w #1 ; #2 + { + \if_int_compare:w #1 > 0 \exp_stop_f: + \exp_after:wN \@@_range_skip:w + \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ; + \else: + \exp_after:wN \exp_end: + \fi: + } +\cs_new:Npn \@@_range:w #1 ; #2 + { + \exp_args:Nf \@@_range_collect:nn + { \@@_range_skip_spaces:n {#2} } {#1} + } +\cs_new:Npn \@@_range_skip_spaces:n #1 + { + \tl_if_head_is_space:nTF {#1} + { \exp_args:Nf \@@_range_skip_spaces:n {#1} } + { { } #1 } + } +\cs_new:Npn \@@_range_collect:nn #1#2 + { + \int_compare:nNnTF {#2} = 0 + {#1} + { + \exp_args:No \tl_if_head_is_space:nTF { \use_none:n #1 } + { + \exp_args:Nf \@@_range_collect:nn + { \@@_range_collect_space:nw #1 } + {#2} + } + { + \@@_range_collect:ff + { + \exp_args:No \tl_if_head_is_N_type:nTF { \use_none:n #1 } + { \@@_range_collect_N:nN } + { \@@_range_collect_group:nn } + #1 + } + { \int_eval:n { #2 - 1 } } + } + } + } +\cs_new:Npn \@@_range_collect_space:nw #1 ~ { { #1 ~ } } +\cs_new:Npn \@@_range_collect_N:nN #1#2 { { #1 #2 } } +\cs_new:Npn \@@_range_collect_group:nn #1#2 { { #1 {#2} } } +\cs_generate_variant:Nn \@@_range_collect:nn { ff } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_range_normalize:nn} +% This function converts an \meta{index} argument into an explicit +% position in the token list (a result of $0$ denoting \enquote{out of +% bounds}). Expects two explicit integer arguments: the \meta{index} +% |#1| and the string count~|#2|. If |#1| is negative, replace it by +% $|#1| + |#2| + 1$, then limit to the range $[0, |#2|]$. +% \begin{macrocode} +\cs_new:Npn \@@_range_normalize:nn #1#2 + { + \int_eval:n + { + \if_int_compare:w #1 < 0 \exp_stop_f: + \if_int_compare:w #1 < -#2 \exp_stop_f: + 0 + \else: + #1 + #2 + 1 + \fi: + \else: + \if_int_compare:w #1 < #2 \exp_stop_f: + #1 + \else: + #2 + \fi: + \fi: + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Viewing token lists} +% +% \begin{macro}{\tl_show:N, \tl_show:c, \tl_log:N, \tl_log:c, \@@_show:NN} +% Showing token list variables is done after checking that the +% variable is defined (see \cs{__kernel_register_show:N}). +% \begin{macrocode} +\cs_new_protected:Npn \tl_show:N { \@@_show:NN \tl_show:n } +\cs_generate_variant:Nn \tl_show:N { c } +\cs_new_protected:Npn \tl_log:N { \@@_show:NN \tl_log:n } +\cs_generate_variant:Nn \tl_log:N { c } +\cs_new_protected:Npn \@@_show:NN #1#2 + { + \__kernel_chk_defined:NT #2 + { \exp_args:Nx #1 { \token_to_str:N #2 = \exp_not:o {#2} } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_show:n, \@@_show:n} +% \begin{macro}[EXP]{\@@_show:w} +% Many |show| functions are based on \cs{tl_show:n}. +% The argument of \cs{tl_show:n} is line-wrapped using +% \cs{iow_wrap:nnnN} but with a leading |>~| and trailing period, both +% removed before passing the wrapped text to the \tn{showtokens} +% primitive. This primitive shows the result with a leading |>~| and +% trailing period. +% +% The token list \cs{l_@@_internal_a_tl} containing the result +% of all these manipulations is displayed to the terminal using +% \cs{tex_showtokens:D} and an odd \cs{exp_after:wN} which expand the +% closing brace to improve the output slightly. The calls to +% \cs{__kernel_iow_with:Nnn} ensure that the \tn{newlinechar} is set to~$10$ +% so that the \cs{iow_newline:} inserted by the line-wrapping code +% are correctly recognized by \TeX{}, and that \tn{errorcontextlines} +% is $-1$ to avoid printing irrelevant context. +% \begin{macrocode} +\cs_new_protected:Npn \tl_show:n #1 + { \iow_wrap:nnnN { >~ \tl_to_str:n {#1} . } { } { } \@@_show:n } +\cs_new_protected:Npn \@@_show:n #1 + { + \tl_set:Nf \l_@@_internal_a_tl { \@@_show:w #1 \q_stop } + \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 } + { + \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 } + { + \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN + { \exp_after:wN \l_@@_internal_a_tl } + } + } + } +\cs_new:Npn \@@_show:w #1 > #2 . \q_stop {#2} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tl_log:n} +% Logging is much easier, simply line-wrap. The |>~| and trailing +% period is there to match the output of \cs{tl_show:n}. +% \begin{macrocode} +\cs_new_protected:Npn \tl_log:n #1 + { \iow_wrap:nnnN { > ~ \tl_to_str:n {#1} . } { } { } \iow_log:n } +% \end{macrocode} +% \end{macro} +% +% \subsection{Scratch token lists} +% +% \begin{variable}{\g_tmpa_tl, \g_tmpb_tl} +% Global temporary token list variables. +% They are supposed to be set and used immediately, +% with no delay between the definition and the use because you +% can't count on other macros not to redefine them from under you. +% \begin{macrocode} +\tl_new:N \g_tmpa_tl +\tl_new:N \g_tmpb_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_tmpa_tl, \l_tmpb_tl} +% These are local temporary token list variables. Be sure not to assume +% that the value you put into them will survive for +% long---see discussion above. +% \begin{macrocode} +\tl_new:N \l_tmpa_tl +\tl_new:N \l_tmpb_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex -- cgit v1.2.3