% \iffalse meta-comment % %% File: l3tl.dtx Copyright (C) 1990-2015 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% This file is part of the "l3kernel bundle" (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX3 Project. %% %% ----------------------------------------------------------------------- % %<*driver> \documentclass[full]{l3doc} % %<*driver|package> \GetIdInfo$Id: l3tl.dtx 5500 2015-01-27 14:37:04Z joseph $ {L3 Token lists} % %<*driver> \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3tl} package\\ Token lists^^A % \thanks{This file describes v\ExplFileVersion, % last revised \ExplFileDate.}^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released \ExplFileDate} % % \maketitle % % \begin{documentation} % % \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 an manipulate the lists % of tokens, and these have the module prefix \texttt{tl}. % In many cases, function 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. % % ^^A todo: perhaps move to another module, l3token or l3basics? % \begin{texnote} % When \TeX{} fetches an undelimited argument from the input stream, % explicit character tokens with character code $32$ (space) and % category code $10$ (space), which we here call \enquote{explicit % space characters}, are ignored. If the following token is an % explicit character token with category code $1$ (begin-group) and an % arbitrary character code, then \TeX{} scans ahead to obtain an equal % number of explicit character tokens with category code $1$ % (begin-group) and $2$ (end-group), and the resulting list of tokens % (with outer braces removed) becomes the argument. Otherwise, a % single token is taken as the argument for the macro: we call such % single tokens \enquote{N-type}, as they are suitable to be used as % an argument for a function with the signature~\texttt{:N}. % % When \TeX{} reads a character of category code $10$ for the first % time, it is converted to an explicit space character, with character % code $32$, regardless of the initial character code. % \enquote{Funny} spaces with a different category code, can be % produced using \cs{tl_to_lowercase:n} or \cs{tl_to_uppercase:n}. % Explicit space characters are also produced as a result of % \cs{token_to_str:N}, \cs{tl_to_str:n}, etc. % \end{texnote} % % \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} will initially be 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} will be 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{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} % will be 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} % will result in \cs{l_tmpa_tl} containing \texttt{abcd}. % \end{function} % % \section{Reassigning token list category codes} % % \begin{function}[updated = 2011-12-18] % { % \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. This allows the % \meta{tl~var} to contain material with category codes % other than those that apply when \meta{tokens} are absorbed. % Trailing spaces at the end of the \meta{tokens} are discarded % in the rescanning process. The \meta{setup} is not limited to % changes of category code but may contain any valid input, for % example assignment of the expansion of active tokens. % See also \cs{tl_rescan:nn}. % \end{function} % % \begin{function}[updated = 2011-12-18]{\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. Trailing spaces at the end of the \meta{tokens} are discarded % in the rescanning process. The \meta{setup} is not limited to % changes of category code but may contain any valid input, for % example assignment of the expansion of active tokens. % See also \cs{tl_set_rescan:Nnn}. % \end{function} % % \section{Reassigning token list character codes} % % \begin{function}[updated = 2012-09-08]{\tl_to_lowercase:n} % \begin{syntax} % \cs{tl_to_lowercase:n} \Arg{tokens} % \end{syntax} % Works through all of the \meta{tokens}, replacing each character token % with the lower case equivalent as defined by \cs{char_set_lccode:nn}. % Characters with no defined lower case character code are left % unchanged. This process does not alter the category code assigned % to the \meta{tokens}. % \begin{texnote} % This is a wrapper around the \TeX{} primitive \tn{lowercase}. % \end{texnote} % \end{function} % % \begin{function}[updated = 2012-09-08]{\tl_to_uppercase:n} % \begin{syntax} % \cs{tl_to_uppercase:n} \Arg{tokens} % \end{syntax} % Works through all of the \meta{tokens}, replacing each character token % with the upper case equivalent as defined by \cs{char_set_uccode:nn}. % Characters with no defined upper case character code are left % unchanged. This process does not alter the category code assigned % to the \meta{tokens}. % \begin{texnote} % This is a wrapper around the \TeX{} primitive \tn{uppercase}. % \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} \Arg{tl~var_1} \Arg{tl~var_2} % \cs{tl_if_eq:NNTF} \Arg{tl~var_1} \Arg{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} \meta{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}[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}[added = 2013-07-24, EXP, TF]{\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. 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} % % \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} will receive one argument for each iteration. % This may be a number of tokens if the \meta{item} was stored within % braces. Hence the \meta{function} should anticipate receiving % \texttt{n}-type arguments. See also \cs{tl_map_function:nN}. % \end{function} % % \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_function:nN} % \begin{syntax} % \cs{tl_map_function:nN} \meta{token list} \meta{function} % \end{syntax} % Applies \meta{function} to every \meta{item} in the \meta{token list}, % The \meta{function} will receive one argument for each iteration. % This may be a number of tokens if the \meta{item} was stored within % braces. Hence the \meta{function} should anticipate receiving % \texttt{n}-type arguments. See also \cs{tl_map_function:NN}. % \end{function} % % \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 % will receive the \meta{item} as |#1|. One in line mapping can be nested % inside another. See also \cs{tl_map_function:NN}. % \end{function} % % \begin{function}[updated = 2012-06-29]{\tl_map_inline:nn} % \begin{syntax} % \cs{tl_map_inline:nn} \meta{token list} \Arg{inline function} % \end{syntax} % Applies the \meta{inline function} to every \meta{item} stored within the % \meta{token list}. The \meta{inline function} should consist of code which % will receive the \meta{item} as |#1|. One in line mapping can be nested % inside another. See also \cs{tl_map_function:nN}. % \end{function} % % \begin{function}[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{function} % \end{syntax} % Applies the \meta{function} to every \meta{item} stored % within the \meta{tl~var}. The \meta{function} should consist of code % which will receive the \meta{item} stored in the \meta{variable}. % One variable mapping can be nested inside another. 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} \meta{token list} \meta{variable} \Arg{function} % \end{syntax} % Applies the \meta{function} to every \meta{item} stored % within the \meta{token list}. The \meta{function} should consist of code % which will receive the \meta{item} stored in the \meta{variable}. % One variable mapping can be nested inside another. 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{tl_map_\ldots} function before all % entries in the \meta{token list variable} have been processed. This % will normally take 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{tl_map_\ldots} scenario will lead to low % level \TeX{} errors. % \begin{texnote} % When the mapping is broken, additional tokens may be inserted by the % internal macro \cs{__prg_break_point:Nn} before the \meta{tokens} are % inserted into the input stream. % This will depend 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{tokens} % \end{syntax} % Used to terminate a \cs{tl_map_\ldots} function before all % entries in the \meta{token list variable} have been processed, inserting % the \meta{tokens} after the mapping has ended. This will % normally take 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{tl_map_\ldots} scenario will lead to low % level \TeX{} errors. % \begin{texnote} % When the mapping is broken, additional tokens may be inserted by the % internal macro \cs{__prg_break_point:Nn} before the \meta{tokens} are % inserted into the input stream. % This will depend on the design of the mapping function. % \end{texnote} % \end{function} % % \section{Using token lists} % % \begin{function}[EXP]{\tl_to_str:n} % \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). % \begin{texnote} % 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 will be 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 will % ignore 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 will ignore 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}[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 will preserve unprotected space within the % \meta{token list}. Tokens are not reversed within braced token % groups, which keep their outer set of braces. % In situations where performance is important, % consider \cs{tl_reverse_items:n}. % See also \cs{tl_reverse:N}. % \begin{texnote} % The result is returned within \tn{unexpanded}, which means that the token % list will 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 will preserve 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 will remove any unprotected space within the % \meta{token list}. Braced token groups are copied without % reversing the order of tokens, and keep the outer set of braces. % Items which are initially not braced are copied with braces in % the result. In cases where preserving spaces is important, % consider the slower function \cs{tl_reverse:n}. % \begin{texnote} % The result is returned within \tn{unexpanded}, which means that the token % list will 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} % \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 will not expand further when appearing in an \texttt{x}-type % argument expansion. % \end{texnote} % \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} % % \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} % will both leave |a| in the input stream. If the \enquote{head} is a % brace group, rather than a single token, the braces will be 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}) will result 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 will 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) % will result 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} % will both leave \verb*| {bc}d| in the input stream. A blank % \meta{token list} (see \cs{tl_if_blank:nTF}) will result % 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 will 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 will always be \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 will always be \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 will always be \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 will evaluate the \meta{integer expression} and leave 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 thr 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} % will not expand further when appearing in an \texttt{x}-type % argument expansion. % \end{texnote} % \end{function} % % \section{Viewing token lists} % % \begin{function}[updated = 2012-09-09]{\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 = 2012-09-09]{\tl_show:n} % \begin{syntax} % \cs{tl_show:n} \meta{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} % % \section{Constant token lists} % % \begin{variable}{\c_empty_tl} % Constant that is always empty. % \end{variable} % % \begin{variable}[updated = 2011-08-18]{\c_job_name_tl} % Constant that gets the \enquote{job name} assigned when \TeX{} starts. % \begin{texnote} % This copies the contents of the primitive \tn{jobname}. It is a constant % that is set by \TeX{} and should not be overwritten by the package. % \end{texnote} % \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} % % \section{Internal functions} % % \begin{function}{\__tl_trim_spaces:nn} % \begin{syntax} % \cs{__tl_trim_spaces:nn} |{ \q_mark| \meta{token list} |}| \Arg{continuation} % \end{syntax} % This function removes all leading and trailing explicit space % characters from the \meta{token list}, and expands to the % \meta{continuation}, followed by a brace group containing % \cs{use_none:n} \cs{q_mark} \meta{trimmed token list}. For % instance, \cs{tl_trim_spaces:n} is implemented by taking the % \meta{continuation} to be \cs{exp_not:o}, and the \texttt{o}-type % expansion removes the \cs{q_mark}. This function is also used in % \pkg{l3clist} and \pkg{l3candidates}. % \end{function} % % \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 { \__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} \cs_new_protected:Npn \tl_const:Nn #1#2 { \__chk_if_free_cs:N #1 \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } } \cs_new_protected:Npn \tl_const:Nx #1#2 { \__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 will be 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 will be 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. % \begin{macrocode} \cs_new_eq:NN \tl_set_eq:NN \cs_set_eq:NN \cs_new_eq:NN \tl_set_eq:cN \cs_set_eq:cN \cs_new_eq:NN \tl_set_eq:Nc \cs_set_eq:Nc \cs_new_eq:NN \tl_set_eq:cc \cs_set_eq:cc \cs_new_eq:NN \tl_gset_eq:NN \cs_gset_eq:NN \cs_new_eq:NN \tl_gset_eq:cN \cs_gset_eq:cN \cs_new_eq:NN \tl_gset_eq:Nc \cs_gset_eq:Nc \cs_new_eq:NN \tl_gset_eq:cc \cs_gset_eq: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. % \begin{macrocode} \cs_new_protected:Npn \tl_concat:NNN #1#2#3 { \tl_set:Nx #1 { \exp_not:o {#2} \exp_not:o {#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_job_name_tl} % Inherited from the \LaTeX3 name for the primitive: this needs to % actually contain the text of the job name rather than the name of % the primitive, of course. % \begin{macrocode} %<*initex> \tex_everyjob:D \exp_after:wN { \tex_the:D \tex_everyjob:D \tl_const:Nx \c_job_name_tl { \tex_jobname:D } } % %<*package> \tl_const:Nx \c_job_name_tl { \tex_jobname:D } % % \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. % \begin{macrocode} \cs_new_protected:Npn \tl_set:Nn #1#2 { \cs_set_nopar:Npx #1 { \exp_not:n {#2} } } \cs_new_protected:Npn \tl_set:No #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o {#2} } } \cs_new_protected:Npn \tl_set:Nx #1#2 { \cs_set_nopar:Npx #1 {#2} } \cs_new_protected:Npn \tl_gset:Nn #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } } \cs_new_protected:Npn \tl_gset:No #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} } } \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} \cs_new_protected:Npn \tl_put_left:Nn #1#2 { \cs_set_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } \cs_new_protected:Npn \tl_put_left:NV #1#2 { \cs_set_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } \cs_new_protected:Npn \tl_put_left:No #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } } \cs_new_protected:Npn \tl_put_left:Nx #1#2 { \cs_set_nopar:Npx #1 { #2 \exp_not:o #1 } } \cs_new_protected:Npn \tl_gput_left:Nn #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } } \cs_new_protected:Npn \tl_gput_left:NV #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } } \cs_new_protected:Npn \tl_gput_left:No #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #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} \cs_new_protected:Npn \tl_put_right:Nn #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } \cs_new_protected:Npn \tl_put_right:NV #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } \cs_new_protected:Npn \tl_put_right:No #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } \cs_new_protected:Npn \tl_put_right:Nx #1#2 { \cs_set_nopar:Npx #1 { \exp_not:o #1 #2 } } \cs_new_protected:Npn \tl_gput_right:Nn #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } } \cs_new_protected:Npn \tl_gput_right:NV #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } } \cs_new_protected:Npn \tl_gput_right:No #1#2 { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } } \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} % % When used as a package, there is an option to be picky and to % check definitions exist. This part of the process is done now, so that % variable types based on |tl| (for example |clist|, |seq| and |prop|) will % inherit the appropriate definitions. % No \cs{tl_map_\ldots} yet as the mechanisms are not fully in place. Thus % instead do a more low level set up for a mapping, as in \pkg{l3basics}. % \begin{macrocode} %<*package> \tex_ifodd:D \l@expl@check@declarations@bool \cs_set_protected:Npn \__cs_tmp:w #1 { \if_meaning:w ? #1 \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \use:x { \cs_set_protected:Npn #1 \exp_not:n { ##1 ##2 } { \__chk_if_exist_var:N \exp_not:n {##1} \exp_not:o { #1 {##1} {##2} } } } \__cs_tmp:w } \__cs_tmp:w \tl_set:Nn \tl_set:No \tl_set:Nx \tl_gset:Nn \tl_gset:No \tl_gset:Nx \tl_put_left:Nn \tl_put_left:NV \tl_put_left:No \tl_put_left:Nx \tl_gput_left:Nn \tl_gput_left:NV \tl_gput_left:No \tl_gput_left:Nx \tl_put_right:Nn \tl_put_right:NV \tl_put_right:No \tl_put_right:Nx \tl_gput_right:Nn \tl_gput_right:NV \tl_gput_right:No \tl_gput_right:Nx ? \q_recursion_stop % % \end{macrocode} % The two \texttt{set_eq} functions are done by hand as the internals there % are a bit different. % \begin{macrocode} %<*package> \cs_set_protected:Npn \tl_set_eq:NN #1#2 { \__chk_if_exist_var:N #1 \__chk_if_exist_var:N #2 \cs_set_eq:NN #1 #2 } \cs_set_protected:Npn \tl_gset_eq:NN #1#2 { \__chk_if_exist_var:N #1 \__chk_if_exist_var:N #2 \cs_gset_eq:NN #1 #2 } % % \end{macrocode} % There is also a need to check all three arguments of the % \texttt{concat} functions: a token list |#2| or |#3| equal to % \cs{scan_stop:} would lead to problems later on. % \begin{macrocode} %<*package> \cs_set_protected:Npn \tl_concat:NNN #1#2#3 { \__chk_if_exist_var:N #1 \__chk_if_exist_var:N #2 \__chk_if_exist_var:N #3 \tl_set:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } \cs_set_protected:Npn \tl_gconcat:NNN #1#2#3 { \__chk_if_exist_var:N #1 \__chk_if_exist_var:N #2 \__chk_if_exist_var:N #3 \tl_gset:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } } \tex_fi:D % % \end{macrocode} % % \subsection{Reassigning token list category codes} % % \begin{variable}{\c_@@_rescan_marker_tl} % The rescanning code needs a special token list containing the same % character with two different category codes. This is set up here, % while the detail is described below. Note that we are sure that the % colon has category letter at this stage. % \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}[aux]{\@@_set_rescan:NNnn} % \begin{macro}[aux]{\@@_rescan:w} % The idea here is to deal cleanly with the problem 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} % When expanding a token list this can be handled using \cs{exp_not:N} % but this fails if the token list is not being expanded. So instead % a delimited argument is used with an end marker which cannot appear % within the token list which is scanned: two `|:|' symbols with different % category codes. The rescanned token list cannot contain the end marker, % because all `|:|' present in the token list are read with the same category % code. As every character with charcode \tn{newlinechar} is replaced % by the \tn{endlinechar}, and an extra \tn{endlinechar} is % added at the end, we need to set both of those to $-1$, % \enquote{unprintable}. % To be safe, the \meta{setup} |#3| is followed by \cs{scan_stop:}. % \begin{macrocode} \cs_new_protected_nopar:Npn \tl_set_rescan:Nnn { \@@_set_rescan:NNnn \tl_set:Nn } \cs_new_protected_nopar:Npn \tl_gset_rescan:Nnn { \@@_set_rescan:NNnn \tl_gset:Nn } \cs_new_protected_nopar:Npn \tl_rescan:nn { \@@_set_rescan:NNnn \prg_do_nothing: \use:n } \cs_new_protected:Npn \@@_set_rescan:NNnn #1#2#3#4 { \group_begin: \exp_args:No \etex_everyeof:D { \c_@@_rescan_marker_tl \exp_not:N } \tex_endlinechar:D \c_minus_one \tex_newlinechar:D \c_minus_one #3 \scan_stop: \use:x { \group_end: #1 \exp_not:N #2 { \exp_after:wN \@@_rescan:w \exp_after:wN \prg_do_nothing: \etex_scantokens:D {#4} } } } \use:x { \cs_new:Npn \exp_not:N \@@_rescan:w ##1 \c_@@_rescan_marker_tl { \exp_not:N \exp_not:o { ##1 } } } \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} % % \subsection{Reassigning token list character codes} % % \begin{macro}{\tl_to_lowercase:n} % \begin{macro}{\tl_to_uppercase:n} % Just some names for a few primitives: we take care or wrapping the % argument in braces. % \begin{macrocode} \cs_new_protected:Npn \tl_to_lowercase:n #1 { \tex_lowercase:D {#1} } \cs_new_protected:Npn \tl_to_uppercase:n #1 { \tex_uppercase:D {#1} } % \end{macrocode} % \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_nopar:Npn \tl_replace_once:Nnn { \@@_replace:NnNNNnn \q_mark ? \@@_replace_wrap:w \tl_set:Nx } \cs_new_protected_nopar:Npn \tl_greplace_once:Nnn { \@@_replace:NnNNNnn \q_mark ? \@@_replace_wrap:w \tl_gset:Nx } \cs_new_protected_nopar:Npn \tl_replace_all:Nnn { \@@_replace:NnNNNnn \q_mark ? \@@_replace_next:w \tl_set:Nx } \cs_new_protected_nopar: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}[aux] % { % \@@_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 will 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} will simply be \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 |\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} { \__msg_kernel_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: \Arg{delimiter} % \meta{function} \meta{assignment} \meta{tl~var} \Arg{pattern} % \Arg{replacement}. 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 wraps it using \cs{exp_not:o} for consumption by % 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 \cs{prg_do_nothing:} to avoid losing braces when % grabbing delimited arguments, hence the use of \cs{exp_not:o} rather % than \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{\cs{prg_do_nothing:} \meta{token list} \meta{delimiter} % \Arg{ending code}}, hence \cs{@@_replace_wrap:w} finds % \enquote{\cs{prg_do_nothing:} \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 {##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 \prg_do_nothing: } } #3 #4 { \exp_after:wN \@@_replace_next:w \exp_after:wN \prg_do_nothing: #4 #1 { \if_false: { \fi: } \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi: } #5 \q_recursion_stop } \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} % % \subsection{Token list conditionals} % % \begin{macro}[pTF]{\tl_if_blank:n,\tl_if_blank:V,\tl_if_blank:o} % \begin{macro}[aux]{\@@_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_return: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_return:o { \use_none:n #1 ? } } \cs_generate_variant:Nn \tl_if_blank_p:n { V } \cs_generate_variant:Nn \tl_if_blank:nT { V } \cs_generate_variant:Nn \tl_if_blank:nF { V } \cs_generate_variant:Nn \tl_if_blank:nTF { V } \cs_generate_variant:Nn \tl_if_blank_p:n { o } \cs_generate_variant:Nn \tl_if_blank:nT { o } \cs_generate_variant:Nn \tl_if_blank:nF { o } \cs_generate_variant:Nn \tl_if_blank:nTF { o } % \end{macrocode} % \end{macro} % \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: } \cs_generate_variant:Nn \tl_if_empty_p:N { c } \cs_generate_variant:Nn \tl_if_empty:NT { c } \cs_generate_variant:Nn \tl_if_empty:NF { c } \cs_generate_variant:Nn \tl_if_empty:NTF { c } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\tl_if_empty:n,\tl_if_empty:V} % Convert the argument to a string: this will be 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: } \cs_generate_variant:Nn \tl_if_empty_p:n { V } \cs_generate_variant:Nn \tl_if_empty:nTF { V } \cs_generate_variant:Nn \tl_if_empty:nT { V } \cs_generate_variant:Nn \tl_if_empty:nF { V } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\tl_if_empty:o} % \begin{macro}[EXP,aux]{\@@_if_empty_return:o} % The auxiliary function \cs{@@_if_empty_return: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:n(TF)}, but % the expansion is hard-coded for efficiency, as this auxiliary % function is used in many places. % Note that this works because \cs{tl_to_str:n} expands tokens % that follow until reading a catcode $1$ (begin-group) token. % \begin{macrocode} \cs_new:Npn \@@_if_empty_return:o #1 { \exp_after:wN \if_meaning:w \exp_after:wN \q_nil \tl_to_str:n \exp_after:wN {#1} \q_nil \prg_return_true: \else: \prg_return_false: \fi: } \prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } { \@@_if_empty_return:o {#1} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc} % 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: } \cs_generate_variant:Nn \tl_if_eq_p:NN { Nc , c , cc } \cs_generate_variant:Nn \tl_if_eq:NNTF { Nc , c , cc } \cs_generate_variant:Nn \tl_if_eq:NNT { Nc , c , cc } \cs_generate_variant:Nn \tl_if_eq:NNF { Nc , c , cc } % \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} \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl \group_end: \prg_return_true: \else: \group_end: \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:nn(TF)} for further comments. Here we simply % expand the token list variable and pass it to \cs{tl_if_in:nn(TF)}. % \begin{macrocode} \cs_new_protected_nopar:Npn \tl_if_in:NnT { \exp_args:No \tl_if_in:nnT } \cs_new_protected_nopar:Npn \tl_if_in:NnF { \exp_args:No \tl_if_in:nnF } \cs_new_protected_nopar:Npn \tl_if_in:NnTF { \exp_args:No \tl_if_in:nnTF } \cs_generate_variant:Nn \tl_if_in:NnT { c } \cs_generate_variant:Nn \tl_if_in:NnF { c } \cs_generate_variant:Nn \tl_if_in:NnTF { c } % \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:n(TF)} 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:}. % \begin{macrocode} \prg_new_protected_conditional:Npnn \tl_if_in:nn #1#2 { T , F , TF } { \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: } \cs_generate_variant:Nn \tl_if_in:nnT { V , o , no } \cs_generate_variant:Nn \tl_if_in:nnF { V , o , no } \cs_generate_variant:Nn \tl_if_in:nnTF { V , o , no } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP,pTF]{\tl_if_single:N} % Expand the token list and feed it to \cs{tl_if_single:n}. % \begin{macrocode} \cs_new:Npn \tl_if_single_p:N { \exp_args:No \tl_if_single_p:n } \cs_new:Npn \tl_if_single:NT { \exp_args:No \tl_if_single:nT } \cs_new:Npn \tl_if_single:NF { \exp_args:No \tl_if_single:nF } \cs_new:Npn \tl_if_single:NTF { \exp_args:No \tl_if_single:nTF } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP,pTF]{\tl_if_single:n} % \begin{macro}[EXP,pTF,aux]{\@@_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 will be % the trailing~|?| and the catcode test yields \texttt{false}. If % |#1| has a single item, the token will be~|^| and the catcode test % yields \texttt{true}. Otherwise, it will be one of the characters % resulting from \cs{tl_to_str:n}, and the catcode test yields % \texttt{false}. Note that \cs{if_catcode:w} takes care of the % expansions, and that \cs{tl_to_str:n} (the \tn{detokenize} % primitive) actually expands tokens until finding a begin-group % token. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF } { \if_catcode:w ^ \exp_after:wN \@@_if_single:nnw \tl_to_str:n \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]{\tl_case:Nn, \tl_case:cn} % \begin{macro}[EXP, TF]{\tl_case:Nn, \tl_case:cn} % \begin{macro}[EXP, aux]{\@@_case:nnTF} % \begin{macro}[aux, EXP]{\@@_case:Nw} % \begin{macro}[int, EXP]{\__prg_case_end:nw} % \begin{macro}[aux, 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 will always be 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 { \tex_romannumeral:D \@@_case:NnTF #1 {#2} { } { } } \cs_new:Npn \tl_case:NnT #1#2#3 { \tex_romannumeral:D \@@_case:NnTF #1 {#2} {#3} { } } \cs_new:Npn \tl_case:NnF #1#2#3 { \tex_romannumeral:D \@@_case:NnTF #1 {#2} { } {#3} } \cs_new:Npn \tl_case:NnTF #1#2 { \tex_romannumeral:D \@@_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 } \cs_generate_variant:Nn \tl_case:NnT { c } \cs_generate_variant:Nn \tl_case:NnF { c } \cs_generate_variant:Nn \tl_case:NnTF { c } % \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| will be the code to insert, % |#2| will be the \emph{next} case to check on and |#3| will be all of % the rest of the cases code. That means that |#4| will be the \texttt{true} % branch code, and |#5| will be tidy 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| will be empty, |#2| will be % the first \cs{q_mark} and so |#4| will be the \texttt{false} code (the % \texttt{true} code is mopped up by |#3|). % \begin{macrocode} \cs_new:Npn \__prg_case_end:nw #1#2#3 \q_mark #4#5 \q_stop { \c_zero #1 #4 } \cs_new_eq:NN \@@_case_end:nw \__prg_case_end:nw % \end{macrocode} % \end{macro} % \end{macro} % \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}[aux]{\@@_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 will be 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_nopar: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__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__prg_map_int \cs_gset:cpn { __prg_map_ \int_use:N \g__prg_map_int :w } ##1 {#2} \exp_args:Nc \@@_map_function:Nn { __prg_map_ \int_use:N \g__prg_map_int :w } #1 \q_recursion_tail \__prg_break_point:Nn \tl_map_break: { \int_gdecr:N \g__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}[aux]{\@@_map_variable:Nnn} % \cs{tl_map_variable:nNn} \meta{token list} \meta{temp} \meta{action} % assigns % \meta{temp} to each element and executes \meta{action}. % \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_nopar:Npn \tl_map_variable:NNn { \exp_args:No \tl_map_variable:nNn } \cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3 { \tl_set:Nn #1 {#3} \__quark_if_recursion_tail_break:NN #1 \tl_map_break: \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_nopar:Npn \tl_map_break: { \__prg_map_break:Nn \tl_map_break: { } } \cs_new_nopar: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} % Another name for a primitive. % \begin{macrocode} \cs_new_eq:NN \tl_to_str:n \etex_detokenize:D % \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 { \etex_detokenize:D \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 will 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} { \__msg_kernel_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}[aux]{\@@_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 { + \c_one } \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}{\tl_reverse_items:n} % \begin{macro}[aux]{\@@_reverse_items:nwNwn} % \begin{macro}[aux]{\@@_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} % \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 will receive 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 will behave 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_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}[int]{\@@_trim_spaces:nn} % \begin{macro}[aux] % { % \@@_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} % % \subsection{Token by token changes} % % \begin{variable}{\q_@@_act_mark,\q_@@_act_stop} % The \cs{tl_act} functions may be applied to any token list. % Hence, we use two private quarks, to allow any token, even quarks, % in the token list.^^A in particular critical for future \::e. % Only \cs{q_@@_act_mark} and \cs{q_@@_act_stop} may not appear % in the token lists manipulated by \cs{@@_act:NNNnn} functions. The quarks % are effectively defined in \pkg{l3quark}. % \end{variable} % % \begin{macro}[EXP,int]{\@@_act:NNNnn} % \begin{macro}[EXP,aux]{\@@_act_output:n, \@@_act_reverse_output:n} % \begin{macro}[EXP,aux]{\@@_act_loop:w} % \begin{macro}[EXP,aux]{\@@_act_normal:NwnNNN} % \begin{macro}[EXP,aux]{\@@_act_group:nwnNNN} % \begin{macro}[EXP,aux]{\@@_act_space:wwnNNN} % \begin{macro}[EXP,aux]{\@@_act_end:w} % To help control the expansion, \cs{@@_act:NNNnn} should always % be proceeded by \tn{romannumeral} and ends by producing \cs{c_zero} % once the result has been obtained. Then loop over tokens, % groups, and spaces in |#5|. The marker \cs{q_@@_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{c_zero} and the result in the input stream, % to terminate the expansion of \tn{romannumeral}. % 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: \c_zero #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,aux]{\@@_reverse_normal:nN} % \begin{macro}[EXP,aux]{\@@_reverse_group_preserve:nn} % \begin{macro}[EXP,aux]{\@@_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 { \etex_unexpanded:D \exp_after:wN { \tex_romannumeral:D \@@_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}[aux]{\@@_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 will always strip 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 { \etex_unexpanded:D \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 \tl_to_str:n \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_nopar:Npn \tl_head:N { \exp_args:No \tl_head:n } % \end{macrocode} % To corrected 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} % will give the wrong result for |\tl_tail:n { a { bc } }| (the braces will % 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 { \etex_unexpanded:D \tl_if_blank:nTF {#1} { { } } { \exp_after:wN { \use_none:n #1 } } } \cs_generate_variant:Nn \tl_tail:n { V , v , f } \cs_new_nopar: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 will % result 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: } \cs_generate_variant:Nn \tl_if_head_eq_charcode_p:nN { f } \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNTF { f } \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNT { f } \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNF { f } % \end{macrocode} % For \cs{tl_if_head_eq_catcode:nN}, again we detect special cases % with a \cs{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,aux]{\@@_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.\footnote{Bruno: this could % be made faster, but we don't: if we hope to ever have % an e-type argument, we need all brace \enquote{tricks} % to happen in one step of expansion, keeping the token % list brace balanced at all times.} % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_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,aux]{\@@_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{tex_romannumeral:D} and \cs{c_zero} 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 } { \tex_romannumeral:D \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 \c_zero \exp_after:wN \prg_return_true: } { \exp_after:wN \c_zero \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}[aux]{\@@_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 { \int_eval:n { \int_compare:nNnT {#2} < \c_zero { \tl_count:n {#1} + \c_one + } #2 } } #1 \q_recursion_tail \__prg_break_point: } \cs_new:Npn \@@_item:nn #1#2 { \__quark_if_recursion_tail_break:nN {#2} \__prg_break: \int_compare:nNnTF {#1} = \c_one { \__prg_break:n { \exp_not:n {#2} } } { \exp_args:Nf \@@_item:nn { \int_eval:n { #1 - 1 } } } } \cs_new_nopar:Npn \tl_item:Nn { \exp_args:No \tl_item:nn } \cs_generate_variant:Nn \tl_item:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Viewing token lists} % % \begin{macro}{\tl_show:N, \tl_show:c} % 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 #1 { \tl_if_exist:NTF #1 { \cs_show:N #1 } { \__msg_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_generate_variant:Nn \tl_show:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_show:n} % The \cs{__msg_show_variable:n} internal function performs % line-wrapping, removes a leading \verb*|> |, then shows the result % using the \cs{etex_showtokens:D} primitive. Since \cs{tl_to_str:n} % is expanded within the line-wrapping code, the escape character is % always a backslash. % \begin{macrocode} \cs_new_protected:Npn \tl_show:n #1 { \__msg_show_variable:n { > ~ \tl_to_str:n {#1} } } % \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} % % \subsection{Deprecated functions} % % \begin{macro}{\tl_case:Nnn, \tl_case:cnn} % Deprecated 2013-07-15. % \begin{macrocode} \cs_new_eq:NN \tl_case:Nnn \tl_case:NnF \cs_new_eq:NN \tl_case:cnn \tl_case:cnF % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex