% \iffalse %% File: gtl.dtx Copyright (C) 2013 Bruno Le Floch %% %% 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 %% %% ----------------------------------------------------------------------- % %<*driver> %\fi %\iffalse \documentclass[full]{l3doc} \usepackage{gtl} \begin{document} \DocInput{gtl.dtx} \end{document} % % \fi % % \title{The \textsf{gtl} package: \\ % manipulate unbalanced lists of tokens\thanks{This % file has version number 0.0a, last revised 2013/07/28.}} % \author{Bruno Le Floch} % \date{2013/07/28} % % \maketitle % \tableofcontents % % \begin{documentation} % % \section{\pkg{gtl} documentation} % % The \texttt{expl3} programming language provides various tools to % manipulate lists of tokens (package \pkg{l3tl}). However, those token % lists must have balanced braces, or more precisely balanced % begin-group and end-group characters. The \pkg{gtl} package % manipulates instead lists of tokens which may be unbalanced, with more % begin-group or more end-group characters. % % \subsection{Creating and initialising extended token lists} % % \begin{function}{\gtl_new:N} % \begin{syntax} % \cs{gtl_new:N} \meta{gtl~var} % \end{syntax} % Creates a new \meta{gtl~var} or raises an error if the name is % already taken. The declaration is global. The \meta{gtl~var} will % initially be empty. % \end{function} % % \begin{function}{\gtl_const:Nn, \gtl_const:Nx} % \begin{syntax} % \cs{gtl_const:Nn} \meta{gtl~var} \Arg{token list} % \end{syntax} % Creates a new constant \meta{gtl~var} or raises an error if the name % is already taken. The value of the \meta{gtl~var} will be set % globally to the balanced \meta{token list}. % \end{function} % % \begin{function}{\gtl_clear:N, \gtl_gclear:N} % \begin{syntax} % \cs{gtl_clear:N} \meta{gtl~var} % \end{syntax} % Empties the \meta{gtl~var}, locally or globally. % \end{function} % % \begin{function}{\gtl_clear_new:N, \gtl_gclear_new:N} % \begin{syntax} % \cs{gtl_clear_new:N} \meta{gtl~var} % \end{syntax} % Ensures that the \meta{gtl~var} exists globally by applying % \cs{gtl_new:N} if necessary, then applies \cs{gtl_(g)clear:N} to % leave the \meta{gtl~var} empty. % \end{function} % % \begin{function}{\gtl_set_eq:NN, \gtl_gset_eq:NN} % \begin{syntax} % \cs{gtl_set_eq:NN} \meta{gtl~var_1} \meta{gtl~var_2} % \end{syntax} % Sets the content of \meta{gtl~var_1} equal to that of % \meta{gtl~var_2}. % \end{function} % % \begin{function}{\gtl_concat:NNN, \gtl_gconcat:NNN} % \begin{syntax} % \cs{gtl_concat:NNN} \meta{gtl~var_1} \meta{gtl~var_2} \meta{gtl~var_3} % \end{syntax} % Concatenates the content of \meta{gtl~var_2} and \meta{gtl~var_3} % together and saves the result in \meta{gtl~var_1}. The % \meta{gtl~var_2} will be placed at the left side of the new extended % token list. % \end{function} % % \begin{function}[EXP, pTF]{\gtl_if_exist:N} % \begin{syntax} % \cs{gtl_if_exist_p:N} \meta{gtl~var} % \cs{gtl_if_exist:NTF} \meta{gtl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{gtl~var} is currently defined. This does % not check that the \meta{gtl~var} really is an extended token list % variable. % \end{function} % % \subsection{Adding data to token list variables} % % \begin{function}{\gtl_set:Nn, \gtl_set:Nx, \gtl_gset:Nn, \gtl_gset:Nx} % \begin{syntax} % \cs{gtl_set:Nn} \meta{gtl~var} \Arg{token list} % \end{syntax} % Sets \meta{gtl~var} to contain the balanced \meta{token list}, % removing any previous content from the variable. % \end{function} % % \begin{function}{\gtl_put_left:Nn, \gtl_gput_left:Nn} % \begin{syntax} % \cs{gtl_put_left:Nn} \meta{gtl~var} \Arg{token list} % \end{syntax} % Appends the balanced \meta{token list} to the left side of the % current content of \meta{gtl~var}. % \end{function} % % \begin{function}{\gtl_put_right:Nn, \gtl_gput_right:Nn} % \begin{syntax} % \cs{gtl_put_right:Nn} \meta{gtl~var} \Arg{token list} % \end{syntax} % Appends the balanced \meta{token list} to the right side of the % current content of \meta{gtl~var}. % \end{function} % % \subsection{Extended token list conditionals} % % \begin{function}[EXP, pTF]{\gtl_if_blank:N} % \begin{syntax} % \cs{gtl_if_blank_p:N} \Arg{gtl~var} % \cs{gtl_if_blank:NTF} \Arg{gtl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{gtl~var} consists only of blank spaces. The test % is \texttt{true} if \meta{gtl~var} consists of 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]{\gtl_if_empty:N} % \begin{syntax} % \cs{gtl_if_empty_p:N} \meta{gtl~var} % \cs{gtl_if_empty:NTF} \meta{gtl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{gtl~var} is entirely empty (\emph{i.e.}~contains % no tokens at all). % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_eq:NN} % \begin{syntax} % \cs{gtl_if_eq_p:NN} \Arg{gtl~var_1} \Arg{gtl~var_2} % \cs{gtl_if_eq:NNTF} \Arg{gtl~var_1} \Arg{gtl~var_2} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if \meta{gtl~var_1} and \meta{gtl~var_2} have the same % content. The test is \texttt{true} if the two contain the same list % of tokens (identical in both character code and category code). % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_single_token:N} % \begin{syntax} % \cs{gtl_if_single_token_p:N} \meta{gtl~var} % \cs{gtl_if_single_token:NTF} \meta{gtl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the content of the \meta{gtl~var} consists of a single % token. Such a token list has token count $1$ according to % \cs{gtl_count_tokens:N}. % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_tl:N} % \begin{syntax} % \cs{gtl_if_tl_p:N} \meta{gtl~var} % \cs{gtl_if_tl:NTF} \meta{gtl~var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{gtl~var} is balanced. % \end{function} % % \subsection{The first token from an extended token list} % % \begin{function}[EXP]{\gtl_head:N} % \begin{syntax} % \cs{gtl_head:N} \meta{gtl~var} % \end{syntax} % Leaves in the input stream the first token in the \meta{gtl~var}. % If the \meta{gtl~var} is empty, nothing is left in the input stream. % \end{function} % % \begin{function}[EXP]{\gtl_head_do:NN} % \begin{syntax} % \cs{gtl_head_do:NN} \meta{gtl~var} \meta{cs} % \end{syntax} % Leaves in the input stream the \meta{control sequence} followed by % the first token in \meta{gtl~var}. If the \meta{gtl~var} is empty, % the \meta{cs} is followed by \cs{q_no_value}. % \end{function} % % \begin{function}{\gtl_get_left:NN} % \begin{syntax} % \cs{gtl_get_left:NN} \meta{gtl~var_1} \meta{gtl~var_2} % \end{syntax} % Stores the first token from \meta{gtl~var_1} in \meta{gtl~var_2} as % an single-token extended token list, without removing it from % \meta{gtl~var_1}. % \end{function} % % \begin{function}{\gtl_pop_left:N, \gtl_gpop_left:N} % \begin{syntax} % \cs{gtl_pop_left:N} \meta{gtl~var} % \end{syntax} % Remove the first token from \meta{gtl~var_1}. % \end{function} % % \begin{function}{\gtl_pop_left:NN, \gtl_gpop_left:NN} % \begin{syntax} % \cs{gtl_pop_left:NN} \meta{gtl~var_1} \meta{gtl~var_2} % \end{syntax} % Stores the first token from \meta{gtl~var_1} in \meta{gtl~var_2} as % an single-token extended token list, and remove it from % \meta{gtl~var_1}. % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_head_eq_catcode:NN} % \begin{syntax} % \cs{gtl_if_head_eq_catcode_p:NN} \Arg{gtl~var} \meta{test token} % \cs{gtl_if_head_eq_catcode:NNTF} \Arg{gtl~var} \meta{test token} % ~~\Arg{true code} \Arg{false code} % \end{syntax} % Tests if the first token in \meta{gtl~var} has the same category % code as the \meta{test token}. In the case where \meta{gtl~var} is % empty, the test will always be \texttt{false}. % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_head_eq_charcode:NN} % \begin{syntax} % \cs{gtl_if_head_eq_charcode_p:NN} \Arg{gtl~var} \meta{test token} % \cs{gtl_if_head_eq_charcode:NNTF} \Arg{gtl~var} \meta{test token} % ~~\Arg{true code} \Arg{false code} % \end{syntax} % Tests if the first token in \meta{gtl~var} has the same character % code as the \meta{test token}. In the case where \meta{gtl~var} is % empty, the test will always be \texttt{false}. % \end{function} % % \begin{function}[EXP,pTF]{\gtl_if_head_eq_meaning:NN} % \begin{syntax} % \cs{gtl_if_head_eq_meaning_p:NN} \Arg{gtl~var} \meta{test token} % \cs{gtl_if_head_eq_meaning:NNTF} \Arg{gtl~var} \meta{test token} % ~~\Arg{true code} \Arg{false code} % \end{syntax} % Tests if the first token in \meta{gtl~var} has the same meaning as % the \meta{test token}. In the case where \meta{gtl~var} is empty, % the test will always be \texttt{false}. % \end{function} % % \begin{function}[EXP,pTF] % { % \gtl_if_head_is_group_begin:N, % \gtl_if_head_is_group_end:N, % \gtl_if_head_is_N_type:N, % \gtl_if_head_is_space:N, % } % \begin{syntax} % \cs{gtl_if_head_is_group_begin_p:N} \Arg{gtl~var} % \cs{gtl_if_head_is_group_begin:NTF} \Arg{gtl~var} % ~~\Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the first token in \meta{gtl~var} is an explicit % begin-group character, an explicit end-group character, an |N|-type % token, or a space. In the case where \meta{gtl~var} is empty, the % test will always be \texttt{false}. % \end{function} % % \subsection{The first few tokens from an extended token list} % % \begin{function}[EXP]{\gtl_left_tl:N} % \begin{syntax} % \cs{gtl_left_tl:N} \meta{gtl~var} % \end{syntax} % Leaves in the input stream all tokens in \meta{gtl~var} until the % first extra begin-group or extra end-group character, within % \cs{exp_not:n}. This is the longest balanced token list starting % from the left of \meta{gtl~var}. % \end{function} % % \begin{function}{\gtl_pop_left_tl:N, \gtl_gpop_left_tl:N} % \begin{syntax} % \cs{gtl_pop_left_tl:N} \meta{gtl~var} % \end{syntax} % Remove from the \meta{gtl~var} all tokens before the first extra % begin-group or extra end-group character. The tokens that are % removed form the longest balanced token list starting from the left % of \meta{gtl~var}. % \end{function} % % \begin{function}[EXP]{\gtl_left_item:NF} % \begin{syntax} % \cs{gtl_left_item:NF} \meta{gtl~var} \Arg{false code} % \end{syntax} % Leaves in the input stream the first \meta{item} of the % \meta{gtl~var}: this is identical to \cs{tl_head:n} applied to the % result of \cs{gtl_left_tl:N}. If there is no such item, the % \meta{false code} is left in the input stream. % \end{function} % % \begin{function}[TF]{\gtl_pop_left_item:NN, \gtl_gpop_left_item:NN} % \begin{syntax} % \cs{gtl_pop_left_item:NNTF} \meta{gtl~var} \meta{tl~var} % \Arg{true~code} \Arg{false~code} % \end{syntax} % Stores the first item of \meta{gtl~var} in \meta{tl~var}, locally, % and removes it from \meta{gtl~var}, together with any space before % it. If there is no such item, the \meta{gtl~var} is not affected, % and the meta{tl~var} may or may not be affected. % \end{function} % % \begin{function}[EXP]{\gtl_left_text:NF} % \begin{syntax} % \cs{gtl_left_text:NF} \meta{gtl~var} \Arg{false code} % \end{syntax} % Starting from the first token in \meta{gtl~var}, this function finds % a pattern of the form \meta{tokens_1} \Arg{tokens_2}, where the % \meta{tokens_1} contain no begin-group nor end-group characters, % then leaves \meta{tokens_1} \Arg{tokens_2} in the input stream, % within \cs{exp_not:n}. If no such pattern exists (this happens if % the result of \cs{gtl_left_tl:N} contains no brace group), the % \meta{false code} is run instead. % \end{function} % % \begin{function}{\gtl_pop_left_text:N, \gtl_gpop_left_text:N} % \begin{syntax} % \cs{gtl_pop_left_text:N} \meta{gtl~var} % \end{syntax} % Starting from the first token in \meta{gtl~var}, this function finds % a pattern of the form \meta{tokens_1} \Arg{tokens_2}, where the % \meta{tokens_1} contain no begin-group nor end-group characters, % then removes \meta{tokens_1} \Arg{tokens_2} from \meta{gtl~var}. If % no such pattern exists (this happens if the result of % \cs{gtl_left_tl:N} contains no brace group), the \meta{gtl~var} is % not modified instead. % \end{function} % % \subsection{Working with the contents of extended token lists} % % \begin{function}[EXP]{\gtl_count_tokens:N} % \begin{syntax} % \cs{gtl_count_tokens:N} \meta{gtl~var} % \end{syntax} % Counts the number of tokens in the \meta{gtl~var} and leaves this % information in the input stream. % \end{function} % % \begin{function}[EXP]{\gtl_extra_begin:N, \gtl_extra_end:N} % \begin{syntax} % \cs{gtl_extra_begin:N} \meta{gtl~var} % \end{syntax} % Counts the number of explicit extra begin-group (or end-group) % characters in the \meta{gtl~var} and leaves this information in the % input stream. % \end{function} % % \begin{function}{\gtl_show:N} % \begin{syntax} % \cs{gtl_show:N} \meta{gtl~var} % \end{syntax} % Displays the content of the \meta{gtl~var} on the terminal. % \end{function} % % \begin{function}[EXP]{\gtl_to_str:N} % \begin{syntax} % \cs{gtl_to_str:N} \meta{gtl~var} % \end{syntax} % Converts the content of the \meta{gtl~var} into a series of % characters with category code $12$ (other) with the exception of % spaces, which retain category code $10$ (space). This string is % then left in the input stream. % \end{function} % % \subsection{Constant extended token lists} % % \begin{variable}{\c_empty_gtl} % Constant that is always empty. % \end{variable} % % \begin{variable}{\c_group_begin_gtl} % An explicit begin-group character contained in an extended token % list. % \end{variable} % % \begin{variable}{\c_group_end_gtl} % An explicit end-group character contained in an extended token list. % \end{variable} % % \subsection{Future perhaps} % % \begin{itemize} % \item Test if a token appears in an extended token list. % \item Test if an extended token list appears in another. % \item Remove an extended token list from another, once or every time % it appears. % \item Replace an extended token list by another in a third: once, or % every time it appears. % \item Case statement. % \item Mapping? % \item Inserting an extended token list into the input stream, with all % its glorious unbalanced braces. % \item Convert in various ways to a token list. % \item Reverse the order of tokens. % \item Extract a token given its position. % \item Extract a range of tokens given their position. % \item Trim spaces. % \item Crazy idea below. % \end{itemize} % % We could add (with lots of work) the expandable function % \begin{syntax} % \cs{gtl_concat:nF} % \{ % \Arg{tl_1} \Arg{start_1} \Arg{stop_1} % \Arg{tl_2} \Arg{start_2} \Arg{stop_2} % \ldots{} % \Arg{tl_n} \Arg{start_n} \Arg{stop_n} % \} % \Arg{false code} % \end{syntax} % For each triplet, this function builds the sub-token list of % corresponding to the tokens ranging from position to % position of . The results obtained for each triplet % are then concatenated. If nothing bad happens (see below), the % concatenation is left in the input stream, and the is % removed. Two cases can lead to running the (and dropping % the first argument altogether). The first case is when the number of % brace groups in \cs{gtl_concat:nF} is not a multiple of 3. The second % case is when the concatenation gives rise to an unbalanced token list: % then the result is not a valid token list. Note that each part is % allowed to be unbalanced: only the full result must be balanced. % % \end{documentation} % % \begin{implementation} % % \section{\pkg{gtl} implementation} % % Some support packages are loaded first, then we declare the package's % name, date, version, and purpose. % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} \RequirePackage{expl3}[2013/07/01] \ProvidesExplPackage {gtl} {2013/07/28} {0.0a} {Manipulate unbalanced lists of tokens} % \end{macrocode} % % \begin{macrocode} %<@@=gtl> % \end{macrocode} % % \subsection{Helpers} % % \begin{macrocode} \cs_generate_variant:Nn \use:nn { no } % \end{macrocode} % % \begin{macro}[EXP, int]{\@@_exp_not_n:N} % Used in one case where we need to prevent expansion of a token % within an |x|-expanding definition. Using \cs{exp_not:N} there % would fail when the argument is a macro parameter character. % \begin{macrocode} \cs_new:Npn \@@_exp_not_n:N #1 { \exp_not:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, int]{\@@_brace:nn, \@@_brace_swap:nn} % Those functions are used to add some tokens, |#1|, to an item |#2| % in an extended token list: \cs{@@_brace:nn} adds tokens on the left, % while \cs{@@_brace_swap:nn} adds them on the right. % \begin{macrocode} \cs_new:Npn \@@_brace:nn #1#2 { { #1 #2 } } \cs_new:Npn \@@_brace_swap:nn #1#2 { { #2 #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP,aux]{\@@_strip_nil_mark:w,} % \begin{macro}[EXP,aux]{\@@_strip_nil_mark_aux:w} % Removes the following \cs{q_nil} \cs{q_mark} without losing any % braces, and places the result into \cs{exp_not:n}. % \begin{macrocode} \cs_new_nopar:Npn \@@_strip_nil_mark:w { \@@_strip_nil_mark_aux:w \prg_do_nothing: } \cs_new:Npn \@@_strip_nil_mark_aux:w #1 \q_nil \q_mark { \exp_not:o {#1} } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Structure of extended token lists} % % Token lists must have balanced braces (or rather, begin-group and % end-group characters). Extended token lists lift this requirement, % and can represent arbitrary lists of tokens. A list of tokens can % fail to be balanced in two ways: one may encounter too many end-group % characters near the beginning of the list, or too many begin-group % characters near the end of the list. In fact, a list of tokens always % has the form % \begin{quote}\ttfamily % \meta{b_1} \} \ldots{} \meta{b_n} \} % \meta{c} % \{ \meta{e_1} \ldots{} \{ \meta{e_p} % \end{quote} % where the \meta{b_i}, \meta{c}, and \meta{e_i} are all balanced token % lists. This can be seen by listing the tokens, and keeping track of a % counter, which starts at~$0$, and is incremented at each begin-group % character, and decremented at each end-group character: then the % \meta{b_i} are delimited by positions where the counter reaches a new % minimum, whereas the \meta{e_i} are delimited by positions where the % counter last takes a given negative value. Such a token list is % stored as % \begin{quote}\ttfamily % \cs{s_@@} % \{ \Arg{b_1} \ldots{} \Arg{b_n} \} % \Arg{c} % \{ \Arg{e_p} \ldots{} \Arg{e_1} \} % \cs{s__stop} % \end{quote} % Note that the \meta{e_i} are in a reversed order, as this makes the % ends of extended token lists more accessible. Balanced token lists % have $n = p = 0$: the first and third parts are empty, while the % second contains the tokens. % % \begin{variable}{\s_@@} % This marker appears at the start of extended token lists. % \begin{macrocode} \__scan_new:N \s_@@ % \end{macrocode} % \end{variable} % % \begin{macro} % { % \gtl_set:Nn, \gtl_gset:Nn, \gtl_const:Nn, % \gtl_set:Nx, \gtl_gset:Nx, \gtl_const:Nx % } % Storing a balanced token list into an extended token list variable % simply means adding \cs{s_@@}, \cs{s__stop}, and two empty brace % groups. % \begin{macrocode} \cs_new_protected_nopar:Npn \gtl_set:Nn { \@@_set:NNn \tl_set:Nn } \cs_new_protected_nopar:Npn \gtl_gset:Nn { \@@_set:NNn \tl_gset:Nn } \cs_new_protected_nopar:Npn \gtl_const:Nn { \@@_set:NNn \tl_const:Nn } \cs_new_protected_nopar:Npn \gtl_set:Nx { \@@_set:NNn \tl_set:Nx } \cs_new_protected_nopar:Npn \gtl_gset:Nx { \@@_set:NNn \tl_gset:Nx } \cs_new_protected_nopar:Npn \gtl_const:Nx { \@@_set:NNn \tl_const:Nx } \cs_new_protected:Npn \@@_set:NNn #1#2#3 { #1 #2 { \s_@@ { } {#3} { } \s__stop } } % \end{macrocode} % \end{macro} % % \begin{variable}{\c_empty_gtl} % An empty extended token list, obtained thanks to the % \cs{gtl_const:Nn} function just defined. % \begin{macrocode} \gtl_const:Nn \c_empty_gtl { } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_group_begin_gtl, \c_group_end_gtl} % An extended token list with exactly one begin-group/end-group % character. % \begin{macrocode} \tl_const:Nn \c_group_end_gtl { \s_@@ { { } } { } { } \s__stop } \tl_const:Nn \c_group_begin_gtl { \s_@@ { } { } { { } } \s__stop } % \end{macrocode} % \end{variable} % % \subsection{Creating extended token list variables} % % \begin{macro}{\gtl_new:N} % A new extended token list is created empty. % \begin{macrocode} \cs_new_protected:Npn \gtl_new:N #1 { \cs_new_eq:NN #1 \c_empty_gtl } % \end{macrocode} % \end{macro} % % \begin{macro}{\gtl_set_eq:NN, \gtl_gset_eq:NN} % All the data about an extended token list is stored as a single % token list, so copying is easy. % \begin{macrocode} \cs_new_eq:NN \gtl_set_eq:NN \tl_set_eq:NN \cs_new_eq:NN \gtl_gset_eq:NN \tl_gset_eq:NN % \end{macrocode} % \end{macro} % % \begin{macro}{\gtl_clear:N, \gtl_gclear:N} % Clearing an extended token list by setting it to the empty one. % \begin{macrocode} \cs_new_protected:Npn \gtl_clear:N #1 { \gtl_set_eq:NN #1 \c_empty_gtl } \cs_new_protected:Npn \gtl_gclear:N #1 { \gtl_gset_eq:NN #1 \c_empty_gtl } % \end{macrocode} % \end{macro} % % \begin{macro}{\gtl_clear_new:N, \gtl_gclear_new:N} % If the variable exists, clear it. Otherwise declare it. % \begin{macrocode} \cs_new_protected:Npn \gtl_clear_new:N #1 { \gtl_if_exist:NTF #1 { \gtl_clear:N #1 } { \gtl_new:N #1 } } \cs_new_protected:Npn \gtl_gclear_new:N #1 { \gtl_if_exist:NTF #1 { \gtl_gclear:N #1 } { \gtl_new:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_exist:N} % Again a copy of token list functions. % \begin{macrocode} \prg_new_eq_conditional:NNn \gtl_if_exist:N \tl_if_exist:N { p , T , F , TF } % \end{macrocode} % \end{macro} % % \subsection{Adding data to extended token list variables} % % \begin{macro}{\gtl_put_left:Nn, \gtl_gput_left:Nn} % \begin{macro}[aux, rEXP]{\@@_put_left:wn} % \begin{macrocode} \cs_new_protected:Npn \gtl_put_left:Nn #1#2 { \tl_set:Nx #1 { \exp_after:wN \@@_put_left:wn #1 {#2} } } \cs_new_protected:Npn \gtl_gput_left:Nn #1#2 { \tl_gset:Nx #1 { \exp_after:wN \@@_put_left:wn #1 {#2} } } \cs_new:Npn \@@_put_left:wn \s_@@ #1#2#3 \s__stop #4 { \tl_if_empty:nTF {#1} { \exp_not:n { \s_@@ { } { #4 #2 } {#3} \s__stop } } { \s_@@ { \exp_not:o { \@@_brace:nn {#4} #1 } } { \exp_not:n {#2} } { \exp_not:n {#3} } \s__stop } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_put_right:Nn, \gtl_gput_right:Nn} % \begin{macro}[aux, rEXP]{\@@_put_right:wn} % \begin{macrocode} \cs_new_protected:Npn \gtl_put_right:Nn #1#2 { \tl_set:Nx #1 { \exp_after:wN \@@_put_right:wn #1 {#2} } } \cs_new_protected:Npn \gtl_gput_right:Nn #1#2 { \tl_gset:Nx #1 { \exp_after:wN \@@_put_right:wn #1 {#2} } } \cs_new:Npn \@@_put_right:wn \s_@@ #1#2#3 \s__stop #4 { \tl_if_empty:nTF {#3} { \exp_not:n { \s_@@ {#1} { #2 #4 } { } \s__stop } } { \s_@@ { \exp_not:n {#1} } { \exp_not:n {#2} } { \exp_not:o { \@@_brace_swap:nn {#4} #3 } } \s__stop } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_concat:NNN, \gtl_gconcat:NNN} % \begin{macro}[rEXP, aux] % { % \@@_concat:ww, % \@@_concat_aux:nnnnnn, % \@@_concat_auxi:nnnnnn, % \@@_concat_auxii:nnnnnn, % \@@_concat_auxiii:w, % \@@_concat_auxiv:nnnn, % \@@_concat_auxv:wnwnn, % \@@_concat_auxvi:nnwnwnn % } % Concatenating two lists of tokens of the form % \begin{quote}\ttfamily % \cs{s_@@} % \{ \Arg{b_1} \ldots{} \Arg{b_n} \} % \Arg{c} % \{ \Arg{e_p} \ldots{} \Arg{e_1} \} % \cs{s__stop} % \end{quote} % is not an easy task. The \meta{e} parts of the first join with the % \meta{b} parts of the second to make balanced pairs, and the % follow-up depends on whether there were more \meta{e} parts or more % \meta{b} parts. % \begin{macrocode} \cs_new_protected:Npn \gtl_concat:NNN #1#2#3 { \tl_set:Nx #1 { \exp_last_two_unbraced:Noo \@@_concat:ww #2 #3 } } \cs_new_protected:Npn \gtl_gconcat:NNN #1#2#3 { \tl_gset:Nx #1 { \exp_last_two_unbraced:Noo \@@_concat:ww #2 #3 } } \cs_new:Npn \@@_concat:ww \s_@@ #1#2#3 \s__stop \s_@@ #4#5#6 \s__stop { \tl_if_blank:nTF {#3} { \tl_if_blank:nTF {#4} { \@@_concat_aux:nnnnnn } { \@@_concat_auxi:nnnnnn } } { \tl_if_blank:nTF {#4} { \@@_concat_auxii:nnnnnn } { \@@_concat_auxiv:nnnn } } {#1} {#2} {#3} {#4} {#5} {#6} \s__stop } \cs_new:Npn \@@_concat_aux:nnnnnn #1#2#3#4#5#6 { \exp_not:n { \s_@@ {#1} { #2 #5 } {#6} } } \cs_new:Npn \@@_concat_auxi:nnnnnn #1#2#3#4#5#6 { \s_@@ { \exp_not:n {#1} \exp_not:f { \@@_concat_auxiii:w \@@_brace:nn {#2} #4 ~ \q_stop } } { \exp_not:n {#5} } { \exp_not:n {#6} } } \cs_new:Npn \@@_concat_auxii:nnnnnn #1#2#3#4#5#6 { \s_@@ { \exp_not:n {#1} } { \exp_not:n {#2} } { \exp_not:n {#6} \exp_not:f { \@@_concat_auxiii:w \@@_brace_swap:nn {#5} #3 ~ \q_stop } } } \cs_new:Npn \@@_concat_auxiii:w #1 ~ #2 \q_stop {#1} \cs_new:Npn \@@_concat_auxiv:nnnn #1#2#3#4 { \tl_if_single:nTF {#3} { \@@_concat_auxv:wnwnn } { \@@_concat_auxvi:nnwnwnn } #3 ~ \q_mark #4 ~ \q_mark {#1} {#2} } \cs_new:Npn \@@_concat_auxv:wnwnn #1#2 \q_mark #3#4 \q_mark #5#6 { \@@_concat:ww \s_@@ {#5} { #6 { #1 #3 } } { } \s__stop \s_@@ {#4} } \cs_new:Npn \@@_concat_auxvi:nnwnwnn #1#2#3 \q_mark #4#5 \q_mark #6#7 { \@@_concat:ww \s_@@ {#6} {#7} { { #2 { #1 #4 } } #3 } \s__stop \s_@@ {#5} } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Showing extended token lists} % % ^^A todo: document \gtl_to_str:n % \begin{macro}[EXP]{\gtl_to_str:N, \gtl_to_str:n} % \begin{macro}[EXP, aux] % { % \@@_to_str:w, % \@@_to_str_loopi:nnw, % \@@_to_str_testi:nnw, % \@@_to_str_endi:nnn, % \@@_to_str_loopii:nnw, % \@@_to_str_endii:nnw % } % \begin{macrocode} \cs_new:Npn \gtl_to_str:N #1 { \exp_after:wN \@@_to_str:w #1 } \cs_new:Npn \gtl_to_str:n #1 { \@@_to_str:w #1 } \cs_new:Npn \@@_to_str:w \s_@@ #1#2#3 \s__stop { \@@_to_str_loopi:nnw { } #1 \q_nil \q_mark {#2} {#3} } \cs_new:Npx \@@_to_str_loopi:nnw #1#2 { \exp_not:N \quark_if_nil:nTF {#2} { \exp_not:N \@@_to_str_testi:nnw {#1} {#2} } { \exp_not:N \@@_to_str_loopi:nnw { #1 #2 \iow_char:N \} } } } \cs_new:Npx \@@_to_str_testi:nnw #1#2#3 \q_mark { \exp_not:N \tl_if_empty:nTF {#3} { \exp_not:N \@@_to_str_endi:nnn {#1} } { \exp_not:N \@@_to_str_loopi:nnw { #1 #2 \iow_char:N \} } #3 \exp_not:N \q_mark } } \cs_new:Npn \@@_to_str_endi:nnn #1#2#3 { \@@_to_str_loopii:nnw #3 { #1 #2 } \q_nil \q_stop } \cs_new:Npx \@@_to_str_loopii:nnw #1#2 { \exp_not:N \quark_if_nil:nTF {#2} { \exp_not:N \@@_to_str_testii:nnw {#1} {#2} } { \exp_not:N \@@_to_str_loopii:nnw { #2 \iow_char:N \{ #1 } } } \cs_new:Npx \@@_to_str_testii:nnw #1#2#3 \q_stop { \exp_not:N \tl_if_empty:nTF {#3} { \exp_not:N \tl_to_str:n {#1} } { \exp_not:N \@@_to_str_loopii:nnw { #2 \iow_char:N \{ #1 } #3 \exp_not:N \q_stop } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_show:N} % Display the variable name, then its string representation. % \begin{macrocode} \cs_new_protected:Npn \gtl_show:N #1 { \exp_args:Nx \tl_show:n { \token_to_str:N #1 = \gtl_to_str:N #1 } } % \end{macrocode} % \end{macro} % % \subsection{Extended token list conditionals} % % \begin{macro}[EXP, pTF]{\gtl_if_eq:NN} % Two extended token lists are equal if their contents agree. % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_eq:NN #1#2 { p , T , F , TF } { \tl_if_eq:NNTF #1 #2 { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_empty:N} % An extended token list is empty if it is equal to the empty one. % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_empty:N #1 { p , T , F , TF } { \tl_if_eq:NNTF #1 \c_empty_gtl { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_tl:N} % \begin{macro}[EXP,aux]{\@@_if_tl_return:w} % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_tl:N #1 { p , T , F , TF } { \exp_after:wN \@@_if_tl_return:w #1 } \cs_new:Npn \@@_if_tl_return:w \s_@@ #1#2#3 \s__stop { \tl_if_empty:nTF { #1 #3 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_single_token:N} % \begin{macro}[EXP,aux]{\@@_if_single_token_return:w} % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_single_token:N #1 { p , T , F , TF } { \exp_after:wN \@@_if_single_token_return:w #1 } \cs_new:Npn \@@_if_single_token_return:w \s_@@ #1#2#3 \s__stop { \bool_if:nTF { \tl_if_empty_p:n {#2} && \tl_if_single_p:n { #1 #3 } && \tl_if_empty_p:o { \use:n #1 #3 } || \tl_if_single_token_p:n {#2} && \tl_if_empty_p:n { #1 #3 } } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP,pTF]{\gtl_if_blank:N} % \begin{macro}[EXP,aux]{\@@_if_blank_return:w} % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_blank:N #1 { p , T , F , TF } { \exp_after:wN \@@_if_blank_return:w #1 } \cs_new:Npn \@@_if_blank_return:w \s_@@ #1#2#3 \s__stop { \tl_if_blank:nTF { #1 #2 #3 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP,pTF] % { % \gtl_if_head_is_group_begin:N, % \gtl_if_head_is_group_end:N, % \gtl_if_head_is_space:N, % \gtl_if_head_is_N_type:N, % } % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_head_is_group_begin:N #1 { p , T , F , TF } { \exp_after:wN \@@_head:wnnnnn #1 { \prg_return_false: } { \prg_return_true: } { \prg_return_false: } { \prg_return_false: } { \prg_return_false: \use_none:n } } \prg_new_conditional:Npnn \gtl_if_head_is_group_end:N #1 { p , T , F , TF } { \exp_after:wN \@@_head:wnnnnn #1 { \prg_return_false: } { \prg_return_false: } { \prg_return_true: } { \prg_return_false: } { \prg_return_false: \use_none:n } } \prg_new_conditional:Npnn \gtl_if_head_is_space:N #1 { p , T , F , TF } { \exp_after:wN \@@_head:wnnnnn #1 { \prg_return_false: } { \prg_return_false: } { \prg_return_false: } { \prg_return_true: } { \prg_return_false: \use_none:n } } \prg_new_conditional:Npnn \gtl_if_head_is_N_type:N #1 { p , T , F , TF } { \exp_after:wN \@@_head:wnnnnn #1 { \prg_return_false: } { \prg_return_false: } { \prg_return_false: } { \prg_return_false: } { \prg_return_true: \use_none:n } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_head_eq_catcode:NN} % \begin{macro}[EXP, pTF]{\gtl_if_head_eq_charcode:NN} % \begin{macro}[EXP, aux]{\@@_if_head_eq_code_return:NNN} % In the empty case, |?| can match with |#2|, but then % \cs{use_none:nn} gets rid of \cs{prg_return_true:} and \cs{else:}, % to correctly leave \cs{prg_return_false:}. We could not simplify % this by placing the \cs{exp_not:N} |#2| after the construction % involving~|#1|, because |#2|~must be taken into the \TeX{} primitive % test, in case |#2|~itself is a primitive \TeX{} conditional, which % would mess up conditional nesting. % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_head_eq_catcode:NN #1#2 { p , T , F , TF } { \@@_if_head_eq_code_return:NNN \if_catcode:w #1#2 } \prg_new_conditional:Npnn \gtl_if_head_eq_charcode:NN #1#2 { p , T , F , TF } { \@@_if_head_eq_code_return:NNN \if_charcode:w #1#2 } \cs_new:Npn \@@_if_head_eq_code_return:NNN #1#2#3 { #1 \exp_not:N #3 \exp_after:wN \@@_head:wnnnnn #2 { ? \use_none:nn } { \c_group_begin_token } { \c_group_end_token } { \c_space_token } { \exp_not:N } \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP, pTF]{\gtl_if_head_eq_meaning:NN} % \begin{macro}[EXP, aux]{\@@_if_head_eq_meaning_return:NN} % \begin{macrocode} \prg_new_conditional:Npnn \gtl_if_head_eq_meaning:NN #1#2 { p , T , F , TF } { \@@_if_head_eq_meaning_return:NN #1#2 } \cs_new:Npn \@@_if_head_eq_meaning_return:NN #1#2 { \exp_after:wN \@@_head:wnnnnn #1 { \if_false: } { \if_meaning:w #2 \c_group_begin_token } { \if_meaning:w #2 \c_group_end_token } { \if_meaning:w #2 \c_space_token } { \if_meaning:w #2 } \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{First token of an extended token list} % % \begin{macro}[EXP, int]{\@@_head:wnnnnn} % \begin{macro}[EXP, aux]{\@@_head_aux:nwnnnn} % \begin{macro}[EXP, aux]{\@@_head_auxii:N, \@@_head_auxiii:Nnn} % This function performs |#4|~if the \texttt{gtl} is empty, |#5|~if it % starts with a begin-group character, |#6|~if it starts with an % end-group character, |#7|~if it starts with a space, and in other % cases (when the first token is |N|-type), it performs |#8|~followed % by the first token. % \begin{macrocode} \cs_new:Npn \@@_head:wnnnnn \s_@@ #1#2#3 \s__stop #4#5#6#7#8 { \tl_if_empty:nTF {#1} { \tl_if_empty:nTF {#2} { \tl_if_empty:nTF {#3} {#4} {#5} } { \@@_head_aux:nwnnnn {#2} \q_stop {#5} {#6} {#7} {#8} } } { \@@_head_aux:nwnnnn #1 \q_stop {#5} {#6} {#7} {#8} } } \cs_new:Npn \@@_head_aux:nwnnnn #1#2 \q_stop #3#4#5#6 { \tl_if_head_is_group:nTF {#1} {#3} { \tl_if_empty:nTF {#1} {#4} { \tl_if_head_is_space:nTF {#1} {#5} { \if_false: { \fi: \@@_head_auxii:N #1 } {#6} } } } } \cs_new:Npn \@@_head_auxii:N #1 { \exp_after:wN \@@_head_auxiii:Nnn \exp_after:wN #1 \exp_after:wN { \if_false: } \fi: } \cs_new:Npn \@@_head_auxiii:Nnn #1#2#3 { #3 #1 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\gtl_head:N} % If |#1| is empty, do nothing. If it starts with a begin-group % character or an end-group character leave the appropriate brace % (thanks to \cs{if_false:} tricks). If it starts with a space, leave % that, and finally if it starts with a normal token, leave it, within % \cs{exp_not:n}. % \begin{macrocode} \cs_new:Npn \gtl_head:N #1 { \exp_after:wN \@@_head:wnnnnn #1 { } { \exp_after:wN { \if_false: } \fi: } { \if_false: { \fi: } } { ~ } { \@@_exp_not_n:N } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\gtl_head_do:NN} % Similar to \cs{gtl_head:N}, but inserting |#2| before the resulting token. % \begin{macrocode} \cs_new:Npn \gtl_head_do:NN #1#2 { \exp_after:wN \@@_head:wnnnnn #1 { #2 \q_no_value } { \exp_after:wN #2 \exp_after:wN { \if_false: } \fi: } { \if_false: { \fi: #2 } } { #2 ~ } { #2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\gtl_get_left:NN} % \begin{macrocode} \cs_new_protected:Npn \gtl_get_left:NN #1#2 { \exp_after:wN \@@_head:wnnnnn #1 { \gtl_set:Nn #2 { \q_no_value } } { \gtl_set_eq:NN #2 \c_group_begin_gtl } { \gtl_set_eq:NN #2 \c_group_end_gtl } { \gtl_set:Nn #2 { ~ } } { \gtl_set:Nn #2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\gtl_pop_left:N, \gtl_gpop_left:N} % \begin{macro}[aux, rEXP] % { % \@@_pop_left:w, % \@@_pop_left_auxi:n, % \@@_pop_left_auxii:nnnw, % \@@_pop_left_auxiii:nnnw, % \@@_pop_left_auxiv:nn, % \@@_pop_left_auxv:nnn, % \@@_pop_left_auxvi:n % } % \begin{macrocode} \cs_new_protected:Npn \gtl_pop_left:N #1 { \tl_set:Nx #1 { \exp_after:wN \@@_pop_left:w #1 } } \cs_new_protected:Npn \gtl_gpop_left:N #1 { \tl_gset:Nx #1 { \exp_after:wN \@@_pop_left:w #1 } } \cs_new:Npn \@@_pop_left:w \s_@@ #1#2#3 \s__stop { \tl_if_empty:nTF {#1} { \tl_if_empty:nTF {#2} { \@@_pop_left_auxi:n {#3} } { \@@_pop_left_auxiv:nn {#2} {#3} } } { \@@_pop_left_auxv:nnn {#1} {#2} {#3} } } \cs_new:Npn \@@_pop_left_auxi:n #1 { \s_@@ { } \@@_pop_left_auxii:nnnw { } { } #1 \q_nil \q_stop \s__stop } \cs_new:Npn \@@_pop_left_auxii:nnnw #1#2#3 { \quark_if_nil:nTF {#3} { \@@_pop_left_auxiii:nnnw {#1} {#2} {#3} } { \@@_pop_left_auxii:nnnw { #1 #2 } { {#3} } } } \cs_new:Npn \@@_pop_left_auxiii:nnnw #1#2#3#4 \q_stop { \tl_if_empty:nTF {#4} { \exp_not:n { #2 {#1} } } { \@@_pop_left_auxii:nnnw { #1 #2 } { {#3} } } } \cs_new:Npn \@@_pop_left_auxiv:nn #1#2 { \s_@@ { \tl_if_head_is_group:nT {#1} { { \tl_head:n {#1} } } } { \tl_if_head_is_space:nTF {#1} { \exp_not:f } { \tl_tail:n } {#1} } { \exp_not:n {#2} } \s__stop } \cs_new:Npn \@@_pop_left_auxv:nnn #1#2#3 { \s_@@ { \if_false: { \fi: \@@_pop_left_auxvi:n #1 } } { \exp_not:n {#2} } { \exp_not:n {#3} } \s__stop } \cs_new:Npn \@@_pop_left_auxvi:n #1 { \tl_if_empty:nF {#1} { \tl_if_head_is_group:nT {#1} { { \tl_head:n {#1} } } { \tl_if_head_is_space:nTF {#1} { \exp_not:f } { \tl_tail:n } {#1} } } \exp_after:wN \exp_not:n \exp_after:wN { \if_false: } \fi: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_pop_left:NN, \gtl_gpop_left:NN} % Getting the first token and removing it from the extended token list % is done in two steps. % \begin{macrocode} \cs_new_protected:Npn \gtl_pop_left:NN #1#2 { \gtl_get_left:NN #1 #2 \gtl_pop_left:N #1 } \cs_new_protected:Npn \gtl_gpop_left:NN #1#2 { \gtl_get_left:NN #1 #2 \gtl_gpop_left:N #1 } % \end{macrocode} % \end{macro} % % \subsection{Longest token list starting an extended token list} % % \begin{macro}[EXP]{\gtl_left_tl:N} % \begin{macro}[EXP,aux]{\@@_left_tl:w} % \begin{macrocode} \cs_new:Npn \gtl_left_tl:N #1 { \exp_after:wN \@@_left_tl:w #1 } \cs_new:Npn \@@_left_tl:w \s_@@ #1#2#3 \s__stop { \tl_if_empty:nTF {#1} { \exp_not:n {#2} } { \tl_head:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_pop_left_tl:N, \gtl_gpop_left_tl:N} % \begin{macrocode} \cs_new_protected:Npn \gtl_pop_left_tl:N #1 { \tl_set:Nx #1 { \exp_after:wN \@@_pop_left_tl:w #1 } } \cs_new_protected:Npn \gtl_gpop_left_tl:N #1 { \tl_gset:Nx #1 { \exp_after:wN \@@_pop_left_tl:w #1 } } \cs_new:Npn \@@_pop_left_tl:w \s_@@ #1#2#3 \s__stop { \s_@@ \tl_if_empty:nTF {#1} { { } { } } { { { } \tl_tail:n {#1} } { \exp_not:n {#2} } } { \exp_not:n {#3} } \s__stop } % \end{macrocode} % \end{macro} % % \subsection{First item of an extended token list} % % \begin{macro}[EXP]{\gtl_left_item:NF} % \begin{macro}[aux, EXP]{\@@_left_item:wF, \@@_left_item_auxi:nwF} % The left-most item of an extended token list is the head of its left % token list. The code thus starts like \cs{gtl_left_tl:N}. It ends % with a check to test if we should use the head, or issue the false % code. % \begin{macrocode} \cs_new:Npn \gtl_left_item:NF #1 { \exp_after:wN \@@_left_item:wF #1 } \cs_new:Npn \@@_left_item:wF \s_@@ #1#2#3 \s__stop { \@@_left_item_auxi:nwF #1 {#2} \q_stop } \cs_new:Npn \@@_left_item_auxi:nwF #1#2 \q_stop #3 { \tl_if_blank:nTF {#1} {#3} { \tl_head:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\gtl_pop_left_item:NN, \gtl_gpop_left_item:NN} % \begin{macro}[aux] % {\@@_pop_left_item:wNNN, \@@_pop_left_item_aux:nwnnNNN} % If there is no extra end-group characters, and if the balanced part % is blank, we cannot extract an item: return \texttt{false}. If the % balanced part is not blank, store its first item into |#4|, and % store the altered generalized token list into~|#6|, locally or % globally. Otherwise, pick out the part before the first extra % end-group character as |#1| of the second auxiliary, and do % essentially the same: if it is blank, there is no item, and if it is % not blank, pop its first item. % \begin{macrocode} \prg_new_protected_conditional:Npnn \gtl_pop_left_item:NN #1#2 { TF , T , F } { \exp_after:wN \@@_pop_left_item:wNNN #1#2 \tl_set:Nx #1 } \prg_new_protected_conditional:Npnn \gtl_gpop_left_item:NN #1#2 { TF , T , F } { \exp_after:wN \@@_pop_left_item:wNNN #1#2 \tl_gset:Nx #1 } \cs_new_protected:Npn \@@_pop_left_item:wNNN \s_@@ #1#2#3 \s__stop #4#5#6 { \tl_if_empty:nTF {#1} { \tl_if_blank:nTF {#2} { \prg_return_false: } { \tl_set:Nx #4 { \tl_head:n {#2} } #5 #6 { \s_@@ { } { \tl_tail:n {#2} } { \exp_not:n {#3} } \s__stop } \prg_return_true: } } { \@@_pop_left_item_aux:nwnnNNN #1 \q_nil \q_stop {#2} {#3} #4 #5 #6 } } \cs_new_protected:Npn \@@_pop_left_item_aux:nwnnNNN #1#2 \q_stop #3#4#5#6#7 { \tl_if_blank:nTF {#1} { \prg_return_false: } { \tl_set:Nx #5 { \tl_head:n {#1} } #6 #7 { \s_@@ { { \tl_tail:n {#1} } \@@_strip_nil_mark:w #2 \q_mark } { \exp_not:n {#3} } { \exp_not:n {#4} } \s__stop } \prg_return_true: } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{First group in an extended token list} % % The functions of this section extract from an extended token list the % tokens that would be absorbed after |\def\foo|, namely tokens with no % begin-group nor end-group characters, followed by one group. Those % tokens are either left in the input stream or stored in a token list % variable, and the |pop| functions also remove those tokens from the % extended token list variable. % % \begin{macro}[EXP]{\gtl_left_text:NF} % \begin{macro}[aux, EXP] % { % \@@_left_text:wF, % \@@_left_text_auxi:nwF, % \@@_left_text_auxii:wnwF, % \@@_left_text_auxiii:nnwF, % } % \begin{macrocode} \cs_new:Npn \gtl_left_text:NF #1 { \exp_after:wN \@@_left_text:wF #1 } \cs_new:Npn \@@_left_text:wF \s_@@ #1#2#3 \s__stop { \tl_if_empty:nTF {#1} { \@@_left_text_auxi:nwF {#2} \q_stop } { \@@_left_text_auxi:nwF #1 \q_stop } } \cs_new:Npn \@@_left_text_auxi:nwF #1#2 \q_stop { \@@_left_text_auxii:wnwF #1 \q_mark { } \q_mark \q_stop } \cs_new:Npn \@@_left_text_auxii:wnwF #1 # { \@@_left_text_auxiii:nnwF {#1} } \cs_new:Npn \@@_left_text_auxiii:nnwF #1#2 #3 \q_mark #4 \q_stop #5 { \tl_if_empty:nTF {#4} {#5} { \exp_not:n { #1 {#2} } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\gtl_pop_left_text:N, \gtl_gpop_left_text:N} % \begin{macro}[aux, rEXP] % { % \@@_pop_left_text:w, % \@@_pop_left_text_auxi:n, % \@@_pop_left_text_auxii:wnw, % \@@_pop_left_text_auxiii:nnw, % \@@_pop_left_text_auxiv:nw, % } % \begin{macrocode} \cs_new_protected:Npn \gtl_pop_left_text:N #1 { \tl_set:Nx #1 { \exp_after:wN \@@_pop_left_text:w #1 } } \cs_new_protected:Npn \gtl_gpop_left_text:N #1 { \tl_gset:Nx #1 { \exp_after:wN \@@_pop_left_text:w #1 } } \cs_new:Npn \@@_pop_left_text:w \s_@@ #1#2#3 \s__stop { \s_@@ \tl_if_empty:nTF {#1} { { } { \@@_pop_left_text_auxi:n {#2} } } { { \@@_pop_left_text_auxiv:nw #1 \q_nil \q_mark } { \exp_not:n {#2} } } { \exp_not:n {#3} } \s__stop } \cs_new:Npn \@@_pop_left_text_auxi:n #1 { \@@_pop_left_text_auxii:wnw #1 \q_nil \q_mark { } \q_mark \q_stop } \cs_new:Npn \@@_pop_left_text_auxii:wnw #1 # { \@@_pop_left_text_auxiii:nnw {#1} } \cs_new:Npn \@@_pop_left_text_auxiii:nnw #1#2#3 \q_mark #4 \q_stop { \tl_if_empty:nTF {#4} { \@@_strip_nil_mark:w #1 } { \@@_strip_nil_mark:w #3 \q_mark } } \cs_new:Npn \@@_pop_left_text_auxiv:nw #1 { { \@@_pop_left_text_auxi:n {#1} } \@@_strip_nil_mark:w } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Counting tokens} % % \begin{macro}[EXP, int]{\@@_tl_count:n} % \begin{macro}[EXP, aux]{\@@_tl_count_loop:n, \@@_tl_count_test:w} % A more robust version of \cs{tl_count:n}, which will however break % if the token list contains \cs{q_stop} at the outer brace level. % This cannot happen when \cs{@@_tl_count:n} is called with lists of % braced items. The technique is to loop, and when seeing % \cs{q_mark}, make sure that this is really the end of the list. % \begin{macrocode} \cs_new:Npn \@@_tl_count:n #1 { \int_eval:n { \c_zero \@@_tl_count_loop:n #1 \q_nil \q_stop } } \cs_new:Npn \@@_tl_count_loop:n #1 { \quark_if_nil:nTF {#1} { \@@_tl_count_test:w } { + \c_one \@@_tl_count_loop:n } } \cs_new:Npn \@@_tl_count_test:w #1 \q_stop { \tl_if_empty:nF {#1} { + \c_one \@@_tl_count_loop:n #1 \q_stop } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\gtl_extra_begin:N, \gtl_extra_end:N} % \begin{macro}[EXP, aux]{\@@_extra_begin:w, \@@_extra_end:w} % Count the number of extra end-group or of extra begin-group % characters in an extended token list. This is the number of items % in the first or third brace groups. We cannot use \cs{tl_count:n}, % as \pkg{gtl} is meant to be robust against inclusion of quarks. % \begin{macrocode} \cs_new:Npn \gtl_extra_end:N #1 { \exp_after:wN \@@_extra_end:w #1 } \cs_new:Npn \@@_extra_end:w \s_@@ #1#2#3 \s__stop { \@@_tl_count:n {#1} } \cs_new:Npn \gtl_extra_begin:N #1 { \exp_after:wN \@@_extra_begin:w #1 } \cs_new:Npn \@@_extra_begin:w \s_@@ #1#2#3 \s__stop { \@@_tl_count:n {#3} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\gtl_count_tokens:N} % \begin{macro}[aux, rEXP] % { % \@@_count_tokens:w, % \@@_count_auxi:nw, % \@@_count_auxii:w, % \@@_count_auxiii:n % } % \begin{macrocode} \cs_new:Npn \gtl_count_tokens:N #1 { \exp_after:wN \@@_count_tokens:w #1 } \cs_new:Npn \@@_count_tokens:w \s_@@ #1#2#3 \s__stop { \int_eval:n { \c_minus_one \@@_count_auxi:nw #1 {#2} #3 \q_nil \q_stop } } \cs_new:Npn \@@_count_auxi:nw #1 { \quark_if_nil:nTF {#1} { \@@_count_auxii:w } { + \c_one \@@_count_auxiii:n {#1} \@@_count_auxi:nw } } \cs_new:Npn \@@_count_auxii:w #1 \q_stop { \tl_if_empty:nF {#1} { + \c_two \@@_count_auxi:nw #1 \q_stop } } \cs_new:Npn \@@_count_auxiii:n #1 { \tl_if_empty:nF {#1} { \tl_if_head_is_group:nTF {#1} { + \c_two \exp_args:No \@@_count_auxiii:n { \use:n #1 } } { + \c_one \tl_if_head_is_N_type:nTF {#1} { \exp_args:No \@@_count_auxiii:n { \use_none:n #1 } } { \exp_args:Nf \@@_count_auxiii:n {#1} } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Messages} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \endinput