% \iffalse meta-comment % %% File: l3seq.dtx Copyright (C) 1990-2017 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 development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full]{l3doc} % %<*driver|package> \GetIdInfo$Id: l3seq.dtx 6842 2017-02-06 21:51:22Z joseph $ {L3 Sequences and stacks} % %<*driver> \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3seq} package\\ Sequences and stacks^^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} % % \LaTeX3 implements a \enquote{sequence} data type, which contain % an ordered list of entries which may contain any \meta{balanced text}. % It is possible to map functions to sequences such that the function % is applied to every item in the sequence. % % Sequences are also used to implement stack functions in \LaTeX3. This % is achieved using a number of dedicated stack functions. % % \section{Creating and initialising sequences} % % \begin{function}{\seq_new:N, \seq_new:c} % \begin{syntax} % \cs{seq_new:N} \meta{sequence} % \end{syntax} % Creates a new \meta{sequence} or raises an error if the name is % already taken. The declaration is global. The \meta{sequence} will % initially contain no items. % \end{function} % % \begin{function}{\seq_clear:N, \seq_clear:c, \seq_gclear:N, \seq_gclear:c} % \begin{syntax} % \cs{seq_clear:N} \meta{sequence} % \end{syntax} % Clears all items from the \meta{sequence}. % \end{function} % % \begin{function} % {\seq_clear_new:N, \seq_clear_new:c, \seq_gclear_new:N, \seq_gclear_new:c} % \begin{syntax} % \cs{seq_clear_new:N} \meta{sequence} % \end{syntax} % Ensures that the \meta{sequence} exists globally by applying % \cs{seq_new:N} if necessary, then applies % \cs[index=seq_clear:N]{seq_(g)clear:N} to leave % the \meta{sequence} empty. % \end{function} % % \begin{function} % { % \seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc, % \seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc % } % \begin{syntax} % \cs{seq_set_eq:NN} \meta{sequence_1} \meta{sequence_2} % \end{syntax} % Sets the content of \meta{sequence_1} equal to that of % \meta{sequence_2}. % \end{function} % % \begin{function}[added = 2014-07-17] % { % \seq_set_from_clist:NN, \seq_set_from_clist:cN, % \seq_set_from_clist:Nc, \seq_set_from_clist:cc, % \seq_set_from_clist:Nn, \seq_set_from_clist:cn, % \seq_gset_from_clist:NN, \seq_gset_from_clist:cN, % \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc, % \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn % } % \begin{syntax} % \cs{seq_set_from_clist:NN} \meta{sequence} \meta{comma-list} % \end{syntax} % Converts the data in the \meta{comma list} into a \meta{sequence}: % the original \meta{comma list} is unchanged. % \end{function} % % \begin{function}[added = 2011-08-15, updated = 2012-07-02] % { % \seq_set_split:Nnn , \seq_set_split:NnV , % \seq_gset_split:Nnn, \seq_gset_split:NnV % } % \begin{syntax} % \cs{seq_set_split:Nnn} \meta{sequence} \Arg{delimiter} \Arg{token list} % \end{syntax} % Splits the \meta{token list} into \meta{items} separated % by \meta{delimiter}, and assigns the result to the \meta{sequence}. % Spaces on both sides of each \meta{item} are ignored, % then one set of outer braces is removed (if any); % this space trimming behaviour is identical to that of % \pkg{l3clist} functions. Empty \meta{items} are preserved by % \cs{seq_set_split:Nnn}, and can be removed afterwards using % \cs{seq_remove_all:Nn} \meta{sequence} \Arg{}. % The \meta{delimiter} may not contain |{|, |}| or |#| % (assuming \TeX{}'s normal category code r\'egime). % If the \meta{delimiter} is empty, the \meta{token list} is split % into \meta{items} as a \meta{token list}. % \end{function} % % \begin{function} % {\seq_concat:NNN, \seq_concat:ccc, \seq_gconcat:NNN, \seq_gconcat:ccc} % \begin{syntax} % \cs{seq_concat:NNN} \meta{sequence_1} \meta{sequence_2} \meta{sequence_3} % \end{syntax} % Concatenates the content of \meta{sequence_2} and \meta{sequence_3} % together and saves the result in \meta{sequence_1}. The items in % \meta{sequence_2} will be placed at the left side of the new sequence. % \end{function} % % \begin{function}[EXP, pTF, added=2012-03-03] % {\seq_if_exist:N, \seq_if_exist:c} % \begin{syntax} % \cs{seq_if_exist_p:N} \meta{sequence} % \cs{seq_if_exist:NTF} \meta{sequence} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{sequence} is currently defined. This does not % check that the \meta{sequence} really is a sequence variable. % \end{function} % % \section{Appending data to sequences} % % \begin{function}{ % \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, % \seq_put_left:No, \seq_put_left:Nx, % \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv, % \seq_put_left:co, \seq_put_left:cx, % \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, % \seq_gput_left:No, \seq_gput_left:Nx, % \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, % \seq_gput_left:co, \seq_gput_left:cx % } % \begin{syntax} % \cs{seq_put_left:Nn} \meta{sequence} \Arg{item} % \end{syntax} % Appends the \meta{item} to the left of the \meta{sequence}. % \end{function} % % \begin{function}{ % \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, % \seq_put_right:No, \seq_put_right:Nx, % \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, % \seq_put_right:co, \seq_put_right:cx, % \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, % \seq_gput_right:No, \seq_gput_right:Nx, % \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, % \seq_gput_right:co, \seq_gput_right:cx % } % \begin{syntax} % \cs{seq_put_right:Nn} \meta{sequence} \Arg{item} % \end{syntax} % Appends the \meta{item} to the right of the \meta{sequence}. % \end{function} % % \section{Recovering items from sequences} % % Items can be recovered from either the left or the right of sequences. % For implementation reasons, the actions at the left of the sequence are % faster than those acting on the right. These functions all assign the % recovered material locally, \emph{i.e.}~setting the % \meta{token list variable} used with \cs{tl_set:Nn} and \emph{never} % \cs{tl_gset:Nn}. % % \begin{function}[updated = 2012-05-14]{\seq_get_left:NN, \seq_get_left:cN} % \begin{syntax} % \cs{seq_get_left:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Stores the left-most item from a \meta{sequence} in the % \meta{token list variable} without removing it from the % \meta{sequence}. The \meta{token list variable} is assigned locally. % If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-19]{\seq_get_right:NN, \seq_get_right:cN} % \begin{syntax} % \cs{seq_get_right:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Stores the right-most item from a \meta{sequence} in the % \meta{token list variable} without removing it from the % \meta{sequence}. The \meta{token list variable} is assigned locally. % If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-14]{\seq_pop_left:NN, \seq_pop_left:cN} % \begin{syntax} % \cs{seq_pop_left:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the left-most item from a \meta{sequence} into the % \meta{token list variable}, \emph{i.e.}~removes the item from the % sequence and stores it in the \meta{token list variable}. % Both of the variables are assigned locally. If \meta{sequence} is % empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-14]{\seq_gpop_left:NN, \seq_gpop_left:cN} % \begin{syntax} % \cs{seq_gpop_left:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the left-most item from a \meta{sequence} into the % \meta{token list variable}, \emph{i.e.}~removes the item from the % sequence and stores it in the \meta{token list variable}. % The \meta{sequence} is modified globally, while the assignment of % the \meta{token list variable} is local. % If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-19]{\seq_pop_right:NN, \seq_pop_right:cN} % \begin{syntax} % \cs{seq_pop_right:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the right-most item from a \meta{sequence} into the % \meta{token list variable}, \emph{i.e.}~removes the item from the % sequence and stores it in the \meta{token list variable}. % Both of the variables are assigned locally. If \meta{sequence} is % empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-19]{\seq_gpop_right:NN, \seq_gpop_right:cN} % \begin{syntax} % \cs{seq_gpop_right:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the right-most item from a \meta{sequence} into the % \meta{token list variable}, \emph{i.e.}~removes the item from the % sequence and stores it in the \meta{token list variable}. % The \meta{sequence} is modified globally, while the assignment of % the \meta{token list variable} is local. % If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[added = 2014-07-17, EXP]{\seq_item:Nn, \seq_item:cn} % \begin{syntax} % \cs{seq_item:Nn} \meta{sequence} \Arg{integer expression} % \end{syntax} % Indexing items in the \meta{sequence} from~$1$ at the top (left), this % function will evaluate the \meta{integer expression} and leave the % appropriate item from the sequence in the input stream. If the % \meta{integer expression} is negative, indexing occurs from the % bottom (right) of the sequence. When the \meta{integer expression} % is larger than the number of items in the \meta{sequence} (as % calculated by \cs{seq_count:N}) then the function will expand 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{Recovering values from sequences with branching} % % The functions in this section combine tests for non-empty sequences % with recovery of an item from the sequence. They offer increased readability % and performance over separate testing and recovery phases. % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] % {\seq_get_left:NN, \seq_get_left:cN} % \begin{syntax} % \cs{seq_get_left:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, stores the left-most item from a % \meta{sequence} % in the \meta{token list variable} without removing it from a % \meta{sequence}. % The \meta{token list variable} is assigned locally. % \end{function} % % \begin{function}[TF, added = 2012-05-19] % {\seq_get_right:NN, \seq_get_right:cN} % \begin{syntax} % \cs{seq_get_right:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, stores the right-most item from a % \meta{sequence} % in the \meta{token list variable} without removing it from a % \meta{sequence}. % The \meta{token list variable} is assigned locally. % \end{function} % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] % {\seq_pop_left:NN, \seq_pop_left:cN} % \begin{syntax} % \cs{seq_pop_left:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the left-most item from a % \meta{sequence} % in the \meta{token list variable}, \emph{i.e.}~removes the item from a % \meta{sequence}. % Both the \meta{sequence} and the \meta{token list variable} are assigned % locally. % \end{function} % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] % {\seq_gpop_left:NN, \seq_gpop_left:cN} % \begin{syntax} % \cs{seq_gpop_left:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the left-most item from a \meta{sequence} % in the \meta{token list variable}, \emph{i.e.}~removes the item from a % \meta{sequence}. % The \meta{sequence} is modified globally, while the \meta{token list variable} % is assigned locally. % \end{function} % % \begin{function}[TF, added = 2012-05-19] % {\seq_pop_right:NN, \seq_pop_right:cN} % \begin{syntax} % \cs{seq_pop_right:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the right-most item from a \meta{sequence} % in the \meta{token list variable}, \emph{i.e.}~removes the item from a % \meta{sequence}. % Both the \meta{sequence} and the \meta{token list variable} are assigned % locally. % \end{function} % % \begin{function}[TF, added = 2012-05-19] % {\seq_gpop_right:NN, \seq_gpop_right:cN} % \begin{syntax} % \cs{seq_gpop_right:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the right-most item from a \meta{sequence} % in the \meta{token list variable}, \emph{i.e.}~removes the item from a % \meta{sequence}. The \meta{sequence} is modified globally, while the % \meta{token list variable} is assigned locally. % \end{function} % % \section{Modifying sequences} % % While sequences are normally used as ordered lists, it may be % necessary to modify the content. The functions here may be used % to update sequences, while retaining the order of the unaffected % entries. % % \begin{function} % { % \seq_remove_duplicates:N, \seq_remove_duplicates:c, % \seq_gremove_duplicates:N, \seq_gremove_duplicates:c % } % \begin{syntax} % \cs{seq_remove_duplicates:N} \meta{sequence} % \end{syntax} % Removes duplicate items from the \meta{sequence}, leaving the % left most copy of each item in the \meta{sequence}. The \meta{item} % comparison takes place on a token basis, as for \cs{tl_if_eq:nnTF}. % \begin{texnote} % This function iterates through every item in the \meta{sequence} and % does a comparison with the \meta{items} already checked. It is therefore % relatively slow with large sequences. % \end{texnote} % \end{function} % % \begin{function} % { % \seq_remove_all:Nn , \seq_remove_all:cn, % \seq_gremove_all:Nn, \seq_gremove_all:cn % } % \begin{syntax} % \cs{seq_remove_all:Nn} \meta{sequence} \Arg{item} % \end{syntax} % Removes every occurrence of \meta{item} from the \meta{sequence}. % The \meta{item} comparison takes place on a token basis, as for % \cs{tl_if_eq:nnTF}. % \end{function} % % \begin{function}[added = 2014-07-18] % { % \seq_reverse:N, \seq_reverse:c, % \seq_greverse:N, \seq_greverse:c % } % \begin{syntax} % \cs{seq_reverse:N} \meta{sequence} % \end{syntax} % Reverses the order of the items stored in the \meta{sequence}. % \end{function} % % \begin{function}[added = 2017-02-06] % {\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn} % \begin{syntax} % \cs{seq_sort:Nn} \meta{sequence} \Arg{comparison code} % \end{syntax} % Sorts the items in the \meta{sequence} according to the % \meta{comparison code}, and assigns the result to % \meta{sequence}. The details of sorting comparison are % described in Section~\ref{sec:l3sort:mech}. % \end{function} % % \section{Sequence conditionals} % % \begin{function}[EXP,pTF]{\seq_if_empty:N, \seq_if_empty:c} % \begin{syntax} % \cs{seq_if_empty_p:N} \meta{sequence} % \cs{seq_if_empty:NTF} \meta{sequence} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{sequence} is empty (containing no items). % \end{function} % % \begin{function}[TF] % { % \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:No, \seq_if_in:Nx, % \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \seq_if_in:co, \seq_if_in:cx % } % \begin{syntax} % \cs{seq_if_in:NnTF} \meta{sequence} \Arg{item} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{item} is present in the \meta{sequence}. % \end{function} % % \section{Mapping to sequences} % % \begin{function}[rEXP, updated = 2012-06-29] % {\seq_map_function:NN, \seq_map_function:cN} % \begin{syntax} % \cs{seq_map_function:NN} \meta{sequence} \meta{function} % \end{syntax} % Applies \meta{function} to every \meta{item} stored in the % \meta{sequence}. The \meta{function} will receive one argument for % each iteration. The \meta{items} are returned from left to right. % The function \cs{seq_map_inline:Nn} is faster than % \cs{seq_map_function:NN} for sequences with more than about~$10$ % items. % One mapping may be nested inside another. % \end{function} % % \begin{function}[updated = 2012-06-29] % {\seq_map_inline:Nn, \seq_map_inline:cn} % \begin{syntax} % \cs{seq_map_inline:Nn} \meta{sequence} \Arg{inline function} % \end{syntax} % Applies \meta{inline function} to every \meta{item} stored % within the \meta{sequence}. 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. The \meta{items} % are returned from left to right. % \end{function} % % \begin{function}[updated = 2012-06-29] % { % \seq_map_variable:NNn, \seq_map_variable:Ncn, % \seq_map_variable:cNn, \seq_map_variable:ccn % } % \begin{syntax} % \cs{seq_map_variable:NNn} \meta{sequence} \meta{tl~var.} \Arg{function using tl~var.} % \end{syntax} % Stores each entry in the \meta{sequence} in turn in the % \meta{tl~var.}\ and applies the \meta{function using tl~var.} % The \meta{function} will usually consist of code making use of % the \meta{tl~var.}, but this is not enforced. One variable % mapping can be nested inside another. The \meta{items} % are returned from left to right. % \end{function} % % \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:} % \begin{syntax} % \cs{seq_map_break:} % \end{syntax} % Used to terminate a \cs[no-index]{seq_map_\ldots} function before all % entries in the \meta{sequence} have been processed. This will % normally take place within a conditional statement, for example % \begin{verbatim} % \seq_map_inline:Nn \l_my_seq % { % \str_if_eq:nnTF { #1 } { bingo } % { \seq_map_break: } % { % % Do something useful % } % } % \end{verbatim} % Use outside of a \cs[no-index]{seq_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 further items are taken % from the input stream. This will depend on the design of the mapping % function. % \end{texnote} % \end{function} % % \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:n} % \begin{syntax} % \cs{seq_map_break:n} \Arg{tokens} % \end{syntax} % Used to terminate a \cs[no-index]{seq_map_\ldots} function before all % entries in the \meta{sequence} 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} % \seq_map_inline:Nn \l_my_seq % { % \str_if_eq:nnTF { #1 } { bingo } % { \seq_map_break:n { } } % { % % Do something useful % } % } % \end{verbatim} % Use outside of a \cs[no-index]{seq_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}[EXP, added = 2012-07-13]{\seq_count:N, \seq_count:c} % \begin{syntax} % \cs{seq_count:N} \meta{sequence} % \end{syntax} % Leaves the number of items in the \meta{sequence} in the input % stream as an \meta{integer denotation}. The total number of items % in a \meta{sequence} will include those which are empty and duplicates, % \emph{i.e.}~every item in a \meta{sequence} is unique. % \end{function} % % \section{Using the content of sequences directly} % % \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nnnn, \seq_use:cnnn} % \begin{syntax} % \cs{seq_use:Nnnn} \meta{seq~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two} % \end{syntax} % Places the contents of the \meta{seq~var} in the input stream, with % the appropriate \meta{separator} between the items. Namely, if the % sequence has more than two items, the \meta{separator between more % than two} is placed between each pair of items except the last, % for which the \meta{separator between final two} is used. If the % sequence has exactly two items, then they are placed in the input stream % separated by the \meta{separator between two}. If the sequence has % a single item, it is placed in the input stream, and an empty sequence % produces no output. An error will be raised if the variable does % not exist or if it is invalid. % % For example, % \begin{verbatim} % \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f } % \seq_use:Nnnn \l_tmpa_seq { ~and~ } { ,~ } { ,~and~ } % \end{verbatim} % will insert \enquote{\texttt{a, b, c, de, and f}} in the input % stream. The first separator argument is not used in this case % because the sequence has more than $2$ items. % \begin{texnote} % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{items} % will not expand further when appearing in an \texttt{x}-type % argument expansion. % \end{texnote} % \end{function} % % \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nn, \seq_use:cn} % \begin{syntax} % \cs{seq_use:Nn} \meta{seq~var} \Arg{separator} % \end{syntax} % Places the contents of the \meta{seq~var} in the input stream, with % the \meta{separator} between the items. If the sequence has % a single item, it is placed in the input stream with no \meta{separator}, % and an empty sequence produces no output. An error will be raised if % the variable does not exist or if it is invalid. % % For example, % \begin{verbatim} % \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f } % \seq_use:Nn \l_tmpa_seq { ~and~ } % \end{verbatim} % will insert \enquote{\texttt{a and b and c and de and f}} in the input % stream. % \begin{texnote} % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{items} % will not expand further when appearing in an \texttt{x}-type % argument expansion. % \end{texnote} % \end{function} % % \section{Sequences as stacks} % % Sequences can be used as stacks, where data is pushed to and popped % from the top of the sequence. (The left of a sequence is the top, for % performance reasons.) The stack functions for sequences are not % intended to be mixed with the general ordered data functions detailed % in the previous section: a sequence should either be used as an % ordered data type or as a stack, but not in both ways. % % \begin{function}[updated = 2012-05-14]{\seq_get:NN, \seq_get:cN} % \begin{syntax} % \cs{seq_get:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Reads the top item from a \meta{sequence} into the % \meta{token list variable} without removing it from the % \meta{sequence}. The \meta{token list variable} is assigned locally. % If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-14]{\seq_pop:NN, \seq_pop:cN} % \begin{syntax} % \cs{seq_pop:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the top item from a \meta{sequence} into the % \meta{token list variable}. Both of the variables are assigned % locally. If \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[updated = 2012-05-14]{\seq_gpop:NN, \seq_gpop:cN} % \begin{syntax} % \cs{seq_gpop:NN} \meta{sequence} \meta{token list variable} % \end{syntax} % Pops the top item from a \meta{sequence} into the % \meta{token list variable}. The \meta{sequence} is modified globally, % while the \meta{token list variable} is assigned locally. If % \meta{sequence} is empty the \meta{token list variable} will % contain the special marker \cs{q_no_value}. % \end{function} % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_get:NN, \seq_get:cN} % \begin{syntax} % \cs{seq_get:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, stores the top item from a % \meta{sequence} in the \meta{token list variable} without removing it from % the \meta{sequence}. The \meta{token list variable} is assigned locally. % \end{function} % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_pop:NN, \seq_pop:cN} % \begin{syntax} % \cs{seq_pop:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the top item from the % \meta{sequence} in the \meta{token list variable}, \emph{i.e.}~removes the % item from the \meta{sequence}. Both the \meta{sequence} and the % \meta{token list variable} are assigned locally. % \end{function} % % \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_gpop:NN, \seq_gpop:cN} % \begin{syntax} % \cs{seq_gpop:NNTF} \meta{sequence} \meta{token list variable} \Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{sequence} is empty, leaves the \meta{false code} in the % input stream. The value of the \meta{token list variable} is % not defined in this case and should not be relied upon. If the % \meta{sequence} is non-empty, pops the top item from the \meta{sequence} % in the \meta{token list variable}, \emph{i.e.}~removes the item from the % \meta{sequence}. The \meta{sequence} is modified globally, while the % \meta{token list variable} is assigned locally. % \end{function} % % \begin{function} % { % \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:No, \seq_push:Nx, % \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:co, \seq_push:cx, % \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, % \seq_gpush:No, \seq_gpush:Nx, % \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, % \seq_gpush:co, \seq_gpush:cx % } % \begin{syntax} % \cs{seq_push:Nn} \meta{sequence} \Arg{item} % \end{syntax} % Adds the \Arg{item} to the top of the \meta{sequence}. % \end{function} % % \section{Sequences as sets} % % Sequences can also be used as sets, such that all of their items are % distinct. Usage of sequences as sets is not currently widespread, % hence no specific set function is provided. Instead, it is explained % here how common set operations can be performed by combining several % functions described in earlier sections. When using sequences to % implement sets, one should be careful not to rely on the order of % items in the sequence representing the set. % % Sets should not contain several occurences of a given item. To make % sure that a \meta{sequence variable} only has distinct items, use % \cs{seq_remove_duplicates:N} \meta{sequence variable}. This function % is relatively slow, and to avoid performance issues one should only % use it when necessary. % % Some operations on a set \meta{seq~var} are straightforward. For % instance, \cs{seq_count:N} \meta{seq~var} expands to the number of % items, while \cs{seq_if_in:NnTF} \meta{seq~var} \Arg{item} tests if % the \meta{item} is in the set. % % Adding an \meta{item} to a set \meta{seq~var} can be done by appending % it to the \meta{seq~var} if it is not already in the \meta{seq~var}: % \begin{quote}\ttfamily % \cs{seq_if_in:NnF} \meta{seq~var} \Arg{item} \\ % \ \ \{ \cs{seq_put_right:Nn} \meta{seq~var} \Arg{item} \} % \end{quote} % Removing an \meta{item} from a set \meta{seq~var} can be done using % \cs{seq_remove_all:Nn}, % \begin{quote}\ttfamily % \cs{seq_remove_all:Nn} \meta{seq~var} \Arg{item} % \end{quote} % % The intersection of two sets \meta{seq~var_1} and \meta{seq~var_2} can % be stored into \meta{seq~var_3} by collecting items of % \meta{seq~var_1} which are in \meta{seq~var_2}. % \begin{quote}\ttfamily % \cs{seq_clear:N} \meta{seq~var_3} \\ % \cs{seq_map_inline:Nn} \meta{seq~var_1} \\ % \ \ \{ \\ % \ \ \ \ \cs{seq_if_in:NnT} \meta{seq~var_2} \{\#1\} \\ % \ \ \ \ \ \ \{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \} \\ % \ \ \} % \end{quote} % The code as written here only works if \meta{seq~var_3} is different % from the other two sequence variables. To cover all cases, items % should first be collected in a sequence % |\l__|\meta{pkg}|_internal_seq|, then \meta{seq~var_3} should be set % equal to this internal sequence. The same remark applies to other set % functions. % % The union of two sets \meta{seq~var_1} and \meta{seq~var_2} can be % stored into \meta{seq~var_3} through % \begin{quote}\ttfamily % \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_1} \meta{seq~var_2} \\ % \cs{seq_remove_duplicates:N} \meta{seq~var_3} % \end{quote} % or by adding items to (a copy of) \meta{seq~var_1} one by one % \begin{quote}\ttfamily % \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1} \\ % \cs{seq_map_inline:Nn} \meta{seq~var_2} \\ % \ \ \{ \\ % \ \ \ \ \cs{seq_if_in:NnF} \meta{seq~var_3} \{\#1\} \\ % \ \ \ \ \ \ \{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \} \\ % \ \ \} % \end{quote} % The second approach is faster than the first when the \meta{seq~var_2} % is short compared to \meta{seq~var_1}. % % The difference of two sets \meta{seq~var_1} and \meta{seq~var_2} can % be stored into \meta{seq~var_3} by removing items of the % \meta{seq~var_2} from (a copy of) the \meta{seq~var_1} one by one. % \begin{quote}\ttfamily % \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1} \\ % \cs{seq_map_inline:Nn} \meta{seq~var_2} \\ % \ \ \{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \} % \end{quote} % % The symmetric difference of two sets \meta{seq~var_1} and % \meta{seq~var_2} can be stored into \meta{seq~var_3} by computing the % difference between \meta{seq~var_1} and \meta{seq~var_2} and storing % the result as |\l__|\meta{pkg}|_internal_seq|, then the difference % between \meta{seq~var_2} and \meta{seq~var_1}, and finally % concatenating the two differences to get the symmetric differences. % \begin{quote}\ttfamily % \cs{seq_set_eq:NN} |\l__|\meta{pkg}|_internal_seq| \meta{seq~var_1} \\ % \cs{seq_map_inline:Nn} \meta{seq~var_2} \\ % \ \ \{ \cs{seq_remove_all:Nn} |\l__|\meta{pkg}|_internal_seq| \{\#1\} \} \\ % \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_2} \\ % \cs{seq_map_inline:Nn} \meta{seq~var_1} \\ % \ \ \{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \} \\ % \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_3} |\l__|\meta{pkg}|_internal_seq| % \end{quote} % % \section{Constant and scratch sequences} % % \begin{variable}[added = 2012-07-02]{\c_empty_seq} % Constant that is always empty. % \end{variable} % % \begin{variable}[added = 2012-04-26]{\l_tmpa_seq, \l_tmpb_seq} % Scratch sequences 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}[added = 2012-04-26]{\g_tmpa_seq, \g_tmpb_seq} % Scratch sequences 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{Viewing sequences} % % \begin{function}[updated = 2015-08-01]{\seq_show:N, \seq_show:c} % \begin{syntax} % \cs{seq_show:N} \meta{sequence} % \end{syntax} % Displays the entries in the \meta{sequence} in the terminal. % \end{function} % % \section{Internal sequence functions} % % \begin{variable}{\s__seq} % This scan mark (equal to \cs{scan_stop:}) marks the beginning of a % sequence variable. % \end{variable} % % \begin{function}[EXP]{\__seq_item:n} % \begin{syntax} % \cs{__seq_item:n} \Arg{item} % \end{syntax} % The internal token used to begin each sequence entry. If expanded % outside of a mapping or manipulation function, an error will be % raised. The definition should always be set globally. % \end{function} % % \begin{function}{\__seq_push_item_def:n, \__seq_push_item_def:x} % \begin{syntax} % \cs{__seq_push_item_def:n} \Arg{code} % \end{syntax} % Saves the definition of \cs{__seq_item:n} and redefines it to % accept one parameter and expand to \meta{code}. This function % should always be balanced by use of \cs{__seq_pop_item_def:}. % \end{function} % % \begin{function}{\__seq_pop_item_def:} % \begin{syntax} % \cs{__seq_pop_item_def:} % \end{syntax} % Restores the definition of \cs{__seq_item:n} most recently saved by % \cs{__seq_push_item_def:n}. This function should always be used in % a balanced pair with \cs{__seq_push_item_def:n}. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3seq} implementation} % % \TestFiles{m3seq002,m3seq003} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=seq> % \end{macrocode} % % A sequence is a control sequence whose top-level expansion is of the % form \enquote{\cs{s_@@} \cs{@@_item:n} \marg{item_1} \ldots % \cs{@@_item:n} \marg{item_n}}, with a leading scan % mark followed by $n$~items of the same form. An % earlier implementation used the structure \enquote{\cs{seq_elt:w} % \meta{item_1} \cs{seq_elt_end:} \ldots \cs{seq_elt:w} \meta{item_n} % \cs{seq_elt_end:}}. This allowed rapid searching using a delimited % function, but was not suitable for items containing |{|, |}| and |#| % tokens, and also lead to the loss of surrounding braces around items. % % \begin{variable}{\s_@@} % The variable is defined in the \pkg{l3quark} module, loaded later. % \end{variable} % % \begin{macro}[int]{\@@_item:n} % The delimiter is always defined, but when used incorrectly simply % removes its argument and hits an undefined control sequence to % raise an error. % \begin{macrocode} \cs_new:Npn \@@_item:n { \__msg_kernel_expandable_error:nn { kernel } { misused-sequence } \use_none:n } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl} % Scratch space for various internal uses. % \begin{macrocode} \tl_new:N \l_@@_internal_a_tl \tl_new:N \l_@@_internal_b_tl % \end{macrocode} % \end{variable} % % \begin{macro}[aux]{\@@_tmp:w} % Scratch function for internal use. % \begin{macrocode} \cs_new_eq:NN \@@_tmp:w ? % \end{macrocode} % \end{macro} % % \begin{variable}{\c_empty_seq} % A sequence with no item, following the structure mentioned above. % \begin{macrocode} \tl_const:Nn \c_empty_seq { \s_@@ } % \end{macrocode} % \end{variable} % % \subsection{Allocation and initialisation} % % \begin{macro}{\seq_new:N, \seq_new:c} % \UnitTested % Sequences are initialized to \cs{c_empty_seq}. % \begin{macrocode} \cs_new_protected:Npn \seq_new:N #1 { \__chk_if_free_cs:N #1 \cs_gset_eq:NN #1 \c_empty_seq } \cs_generate_variant:Nn \seq_new:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\seq_clear:N, \seq_clear:c} % \UnitTested % \begin{macro}{\seq_gclear:N, \seq_gclear:c} % \UnitTested % Clearing a sequence is similar to setting it equal to the empty one. % \begin{macrocode} \cs_new_protected:Npn \seq_clear:N #1 { \seq_set_eq:NN #1 \c_empty_seq } \cs_generate_variant:Nn \seq_clear:N { c } \cs_new_protected:Npn \seq_gclear:N #1 { \seq_gset_eq:NN #1 \c_empty_seq } \cs_generate_variant:Nn \seq_gclear:N { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_clear_new:N, \seq_clear_new:c} % \UnitTested % \begin{macro}{\seq_gclear_new:N, \seq_gclear_new:c} % \UnitTested % Once again we copy code from the token list functions. % \begin{macrocode} \cs_new_protected:Npn \seq_clear_new:N #1 { \seq_if_exist:NTF #1 { \seq_clear:N #1 } { \seq_new:N #1 } } \cs_generate_variant:Nn \seq_clear_new:N { c } \cs_new_protected:Npn \seq_gclear_new:N #1 { \seq_if_exist:NTF #1 { \seq_gclear:N #1 } { \seq_new:N #1 } } \cs_generate_variant:Nn \seq_gclear_new:N { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc} % \UnitTested % \begin{macro} % {\seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc} % \UnitTested % Copying a sequence is the same as copying the underlying token list. % \begin{macrocode} \cs_new_eq:NN \seq_set_eq:NN \tl_set_eq:NN \cs_new_eq:NN \seq_set_eq:Nc \tl_set_eq:Nc \cs_new_eq:NN \seq_set_eq:cN \tl_set_eq:cN \cs_new_eq:NN \seq_set_eq:cc \tl_set_eq:cc \cs_new_eq:NN \seq_gset_eq:NN \tl_gset_eq:NN \cs_new_eq:NN \seq_gset_eq:Nc \tl_gset_eq:Nc \cs_new_eq:NN \seq_gset_eq:cN \tl_gset_eq:cN \cs_new_eq:NN \seq_gset_eq:cc \tl_gset_eq:cc % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \seq_set_from_clist:NN, \seq_set_from_clist:cN, % \seq_set_from_clist:Nc, \seq_set_from_clist:cc, % \seq_set_from_clist:Nn, \seq_set_from_clist:cn % } % \begin{macro} % { % \seq_gset_from_clist:NN, \seq_gset_from_clist:cN, % \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc, % \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn % } % Setting a sequence from a comma-separated list is done using a simple % mapping. % \begin{macrocode} \cs_new_protected:Npn \seq_set_from_clist:NN #1#2 { \tl_set:Nx #1 { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n } } \cs_new_protected:Npn \seq_set_from_clist:Nn #1#2 { \tl_set:Nx #1 { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n } } \cs_new_protected:Npn \seq_gset_from_clist:NN #1#2 { \tl_gset:Nx #1 { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n } } \cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2 { \tl_gset:Nx #1 { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n } } \cs_generate_variant:Nn \seq_set_from_clist:NN { Nc } \cs_generate_variant:Nn \seq_set_from_clist:NN { c , cc } \cs_generate_variant:Nn \seq_set_from_clist:Nn { c } \cs_generate_variant:Nn \seq_gset_from_clist:NN { Nc } \cs_generate_variant:Nn \seq_gset_from_clist:NN { c , cc } \cs_generate_variant:Nn \seq_gset_from_clist:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \seq_set_split:Nnn , \seq_set_split:NnV , % \seq_gset_split:Nnn, \seq_gset_split:NnV % } % \begin{macro}[aux]{\@@_set_split:NNnn} % \begin{macro}[aux] % { % \@@_set_split_auxi:w, \@@_set_split_auxii:w, % \@@_set_split_end: % } % When the separator is empty, everything is very simple, just map % \cs{@@_wrap_item:n} through the items of the last argument. % For non-trivial separators, the goal is to split a given token list % at the marker, strip spaces from each item, and remove one set of % outer braces if after removing leading and trailing % spaces the item is enclosed within braces. After % \cs{tl_replace_all:Nnn}, the token list \cs{l_@@_internal_a_tl} % is a repetition of the pattern % \cs{@@_set_split_auxi:w} \cs{prg_do_nothing:} % \meta{item with spaces} \cs{@@_set_split_end:}. % Then, \texttt{x}-expansion causes \cs{@@_set_split_auxi:w} % to trim spaces, and leaves its result as % \cs{@@_set_split_auxii:w} \meta{trimmed item} % \cs{@@_set_split_end:}. This is then converted % to the \pkg{l3seq} internal structure by another % \texttt{x}-expansion. In the first step, we insert % \cs{prg_do_nothing:} to avoid losing braces too early: % that would cause space trimming to act within those % lost braces. The second step is solely there to strip % braces which are outermost after space trimming. % \begin{macrocode} \cs_new_protected:Npn \seq_set_split:Nnn { \@@_set_split:NNnn \tl_set:Nx } \cs_new_protected:Npn \seq_gset_split:Nnn { \@@_set_split:NNnn \tl_gset:Nx } \cs_new_protected:Npn \@@_set_split:NNnn #1#2#3#4 { \tl_if_empty:nTF {#3} { \tl_set:Nn \l_@@_internal_a_tl { \tl_map_function:nN {#4} \@@_wrap_item:n } } { \tl_set:Nn \l_@@_internal_a_tl { \@@_set_split_auxi:w \prg_do_nothing: #4 \@@_set_split_end: } \tl_replace_all:Nnn \l_@@_internal_a_tl { #3 } { \@@_set_split_end: \@@_set_split_auxi:w \prg_do_nothing: } \tl_set:Nx \l_@@_internal_a_tl { \l_@@_internal_a_tl } } #1 #2 { \s_@@ \l_@@_internal_a_tl } } \cs_new:Npn \@@_set_split_auxi:w #1 \@@_set_split_end: { \exp_not:N \@@_set_split_auxii:w \exp_args:No \tl_trim_spaces:n {#1} \exp_not:N \@@_set_split_end: } \cs_new:Npn \@@_set_split_auxii:w #1 \@@_set_split_end: { \@@_wrap_item:n {#1} } \cs_generate_variant:Nn \seq_set_split:Nnn { NnV } \cs_generate_variant:Nn \seq_gset_split:Nnn { NnV } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_concat:NNN, \seq_concat:ccc} % \UnitTested % \begin{macro}{\seq_gconcat:NNN, \seq_gconcat:ccc} % \UnitTested % When concatenating sequences, one must remove the leading \cs{s_@@} % of the second sequence. The result starts with \cs{s_@@} (of the % first sequence), which stops \texttt{f}-expansion. % \begin{macrocode} \cs_new_protected:Npn \seq_concat:NNN #1#2#3 { \tl_set:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } } \cs_new_protected:Npn \seq_gconcat:NNN #1#2#3 { \tl_gset:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } } \cs_generate_variant:Nn \seq_concat:NNN { ccc } \cs_generate_variant:Nn \seq_gconcat:NNN { ccc } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[pTF]{\seq_if_exist:N, \seq_if_exist:c} % Copies of the \texttt{cs} functions defined in \pkg{l3basics}. % \begin{macrocode} \prg_new_eq_conditional:NNn \seq_if_exist:N \cs_if_exist:N { TF , T , F , p } \prg_new_eq_conditional:NNn \seq_if_exist:c \cs_if_exist:c { TF , T , F , p } % \end{macrocode} % \end{macro} % % \subsection{Appending data to either end} % % \begin{macro}{ % \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, % \seq_put_left:No, \seq_put_left:Nx, % \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv, % \seq_put_left:co, \seq_put_left:cx % } % \UnitTested % \begin{macro}{ % \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, % \seq_gput_left:No, \seq_gput_left:Nx, % \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, % \seq_gput_left:co, \seq_gput_left:cx % } % \begin{macro}[aux,EXP]{\@@_put_left_aux:w} % When adding to the left of a sequence, remove \cs{s_@@}. This is % done by \cs{@@_put_left_aux:w}, which also stops % \texttt{f}-expansion. % \begin{macrocode} \cs_new_protected:Npn \seq_put_left:Nn #1#2 { \tl_set:Nx #1 { \exp_not:n { \s_@@ \@@_item:n {#2} } \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 } } } \cs_new_protected:Npn \seq_gput_left:Nn #1#2 { \tl_gset:Nx #1 { \exp_not:n { \s_@@ \@@_item:n {#2} } \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 } } } \cs_new:Npn \@@_put_left_aux:w \s_@@ { \exp_stop_f: } \cs_generate_variant:Nn \seq_put_left:Nn { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_put_left:Nn { c , cV , cv , co , cx } \cs_generate_variant:Nn \seq_gput_left:Nn { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_gput_left:Nn { c , cV , cv , co , cx } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro} % { % \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, % \seq_put_right:No, \seq_put_right:Nx, % \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, % \seq_put_right:co, \seq_put_right:cx % } % \UnitTested % \begin{macro} % { % \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, % \seq_gput_right:No, \seq_gput_right:Nx, % \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, % \seq_gput_right:co, \seq_gput_right:cx % } % Since there is no trailing marker, adding an item to the right of a % sequence simply means wrapping it in \cs{@@_item:n}. % \begin{macrocode} \cs_new_protected:Npn \seq_put_right:Nn #1#2 { \tl_put_right:Nn #1 { \@@_item:n {#2} } } \cs_new_protected:Npn \seq_gput_right:Nn #1#2 { \tl_gput_right:Nn #1 { \@@_item:n {#2} } } \cs_generate_variant:Nn \seq_gput_right:Nn { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_gput_right:Nn { c , cV , cv , co , cx } \cs_generate_variant:Nn \seq_put_right:Nn { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_put_right:Nn { c , cV , cv , co , cx } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Modifying sequences} % % \begin{macro}[aux]{\@@_wrap_item:n} % This function converts its argument to a proper sequence item % in an \texttt{x}-expansion context. % \begin{macrocode} \cs_new:Npn \@@_wrap_item:n #1 { \exp_not:n { \@@_item:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_remove_seq} % An internal sequence for the removal routines. % \begin{macrocode} \seq_new:N \l_@@_remove_seq % \end{macrocode} % \end{variable} % % \begin{macro}{\seq_remove_duplicates:N, \seq_remove_duplicates:c} % \UnitTested % \begin{macro}{\seq_gremove_duplicates:N, \seq_gremove_duplicates:c} % \UnitTested % \begin{macro}[aux]{\@@_remove_duplicates:NN} % Removing duplicates means making a new list then copying it. % \begin{macrocode} \cs_new_protected:Npn \seq_remove_duplicates:N { \@@_remove_duplicates:NN \seq_set_eq:NN } \cs_new_protected:Npn \seq_gremove_duplicates:N { \@@_remove_duplicates:NN \seq_gset_eq:NN } \cs_new_protected:Npn \@@_remove_duplicates:NN #1#2 { \seq_clear:N \l_@@_remove_seq \seq_map_inline:Nn #2 { \seq_if_in:NnF \l_@@_remove_seq {##1} { \seq_put_right:Nn \l_@@_remove_seq {##1} } } #1 #2 \l_@@_remove_seq } \cs_generate_variant:Nn \seq_remove_duplicates:N { c } \cs_generate_variant:Nn \seq_gremove_duplicates:N { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_remove_all:Nn, \seq_remove_all:cn} % \UnitTested % \begin{macro}{\seq_gremove_all:Nn, \seq_gremove_all:cn} % \UnitTested % \begin{macro}[aux]{\@@_remove_all_aux:NNn} % The idea of the code here is to avoid a relatively expensive addition of % items one at a time to an intermediate sequence. % The approach taken is therefore similar to % that in \cs{@@_pop_right:NNN}, using a \enquote{flexible} % \texttt{x}-type expansion to do most of the work. As \cs{tl_if_eq:nnT} % is not expandable, a two-part strategy is needed. First, the % \texttt{x}-type expansion uses \cs{str_if_eq:nnT} to find potential % matches. If one is found, the expansion is halted and the necessary % set up takes place to use the \cs{tl_if_eq:NNT} test. The \texttt{x}-type % is started again, including all of the items copied already. This will % happen repeatedly until the entire sequence has been scanned. The code % is set up to avoid needing and intermediate scratch list: the lead-off % \texttt{x}-type expansion (|#1 #2 {#2}|) will ensure that nothing is % lost. % \begin{macrocode} \cs_new_protected:Npn \seq_remove_all:Nn { \@@_remove_all_aux:NNn \tl_set:Nx } \cs_new_protected:Npn \seq_gremove_all:Nn { \@@_remove_all_aux:NNn \tl_gset:Nx } \cs_new_protected:Npn \@@_remove_all_aux:NNn #1#2#3 { \@@_push_item_def:n { \str_if_eq:nnT {##1} {#3} { \if_false: { \fi: } \tl_set:Nn \l_@@_internal_b_tl {##1} #1 #2 { \if_false: } \fi: \exp_not:o {#2} \tl_if_eq:NNT \l_@@_internal_a_tl \l_@@_internal_b_tl { \use_none:nn } } \@@_wrap_item:n {##1} } \tl_set:Nn \l_@@_internal_a_tl {#3} #1 #2 {#2} \@@_pop_item_def: } \cs_generate_variant:Nn \seq_remove_all:Nn { c } \cs_generate_variant:Nn \seq_gremove_all:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro} % {\seq_reverse:N, \seq_reverse:c, \seq_greverse:N, \seq_greverse:c} % \begin{macro}[aux]{\@@_reverse:NN} % \begin{macro}[aux, EXP]{\@@_reverse_item:nwn} % Previously, \cs{seq_reverse:N} was coded by collecting the items % in reverse order after an \cs{exp_stop_f:} marker. % \begin{verbatim} % \cs_new_protected:Npn \seq_reverse:N #1 % { % \cs_set_eq:NN \@@_item:n \@@_reverse_item:nw % \tl_set:Nf #2 { #2 \exp_stop_f: } % } % \cs_new:Npn \@@_reverse_item:nw #1 #2 \exp_stop_f: % { % #2 \exp_stop_f: % \@@_item:n {#1} % } % \end{verbatim} % At first, this seems optimal, since we can forget about each item % as soon as it is placed after \cs{exp_stop_f:}. Unfortunately, % \TeX{}'s usual tail recursion does not take place in this case: % since the following \cs{@@_reverse_item:nw} only reads % tokens until \cs{exp_stop_f:}, and never reads the % |\@@_item:n {#1}| left by the previous call, \TeX{} cannot % remove that previous call from the stack, and in particular % must retain the various macro parameters in memory, until the % end of the replacement text is reached. The stack is thus % only flushed after all the \cs{@@_reverse_item:nw} are % expanded. Keeping track of the arguments of all those calls % uses up a memory quadratic in the length of the sequence. % \TeX{} can then not cope with more than a few thousand items. % % Instead, we collect the items in the argument % of \cs{exp_not:n}. The previous calls are cleanly removed % from the stack, and the memory consumption becomes linear. % \begin{macrocode} \cs_new_protected:Npn \seq_reverse:N { \@@_reverse:NN \tl_set:Nx } \cs_new_protected:Npn \seq_greverse:N { \@@_reverse:NN \tl_gset:Nx } \cs_new_protected:Npn \@@_reverse:NN #1 #2 { \cs_set_eq:NN \@@_tmp:w \@@_item:n \cs_set_eq:NN \@@_item:n \@@_reverse_item:nwn #1 #2 { #2 \exp_not:n { } } \cs_set_eq:NN \@@_item:n \@@_tmp:w } \cs_new:Npn \@@_reverse_item:nwn #1 #2 \exp_not:n #3 { #2 \exp_not:n { \@@_item:n {#1} #3 } } \cs_generate_variant:Nn \seq_reverse:N { c } \cs_generate_variant:Nn \seq_greverse:N { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn} % Implemented in \pkg{l3sort}. % \end{macro} % % \subsection{Sequence conditionals} % % \begin{macro}[pTF]{\seq_if_empty:N, \seq_if_empty:c} % \UnitTested % Similar to token lists, we compare with the empty sequence. % \begin{macrocode} \prg_new_conditional:Npnn \seq_if_empty:N #1 { p , T , F , TF } { \if_meaning:w #1 \c_empty_seq \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \seq_if_empty_p:N { c } \cs_generate_variant:Nn \seq_if_empty:NT { c } \cs_generate_variant:Nn \seq_if_empty:NF { c } \cs_generate_variant:Nn \seq_if_empty:NTF { c } % \end{macrocode} % \end{macro} % % \begin{macro}[TF] % { % \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:No, \seq_if_in:Nx, % \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \seq_if_in:co, \seq_if_in:cx % } % \UnitTested % \begin{macro}[aux]{\@@_if_in:} % The approach here is to define \cs{@@_item:n} to compare its % argument with the test sequence. If the two items are equal, the % mapping is terminated and \cs{group_end:} \cs{prg_return_true:} % is inserted after skipping over the rest of the recursion. On the % other hand, if there is no match then the loop will break returning % \cs{prg_return_false:}. % Everything is inside a group so that \cs{@@_item:n} is preserved % in nested situations. % \begin{macrocode} \prg_new_protected_conditional:Npnn \seq_if_in:Nn #1#2 { T , F , TF } { \group_begin: \tl_set:Nn \l_@@_internal_a_tl {#2} \cs_set_protected:Npn \@@_item:n ##1 { \tl_set:Nn \l_@@_internal_b_tl {##1} \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl \exp_after:wN \@@_if_in: \fi: } #1 \group_end: \prg_return_false: \__prg_break_point: } \cs_new:Npn \@@_if_in: { \__prg_break:n { \group_end: \prg_return_true: } } \cs_generate_variant:Nn \seq_if_in:NnT { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_if_in:NnT { c , cV , cv , co , cx } \cs_generate_variant:Nn \seq_if_in:NnF { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_if_in:NnF { c , cV , cv , co , cx } \cs_generate_variant:Nn \seq_if_in:NnTF { NV , Nv , No , Nx } \cs_generate_variant:Nn \seq_if_in:NnTF { c , cV , cv , co , cx } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Recovering data from sequences} % % \begin{macro}[int]{\@@_pop:NNNN, \@@_pop_TF:NNNN} % The two \texttt{pop} functions share their emptiness tests. We also % use a common emptiness test for all branching \texttt{get} and % \texttt{pop} functions. % \begin{macrocode} \cs_new_protected:Npn \@@_pop:NNNN #1#2#3#4 { \if_meaning:w #3 \c_empty_seq \tl_set:Nn #4 { \q_no_value } \else: #1#2#3#4 \fi: } \cs_new_protected:Npn \@@_pop_TF:NNNN #1#2#3#4 { \if_meaning:w #3 \c_empty_seq % \tl_set:Nn #4 { \q_no_value } \prg_return_false: \else: #1#2#3#4 \prg_return_true: \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}{\seq_get_left:NN, \seq_get_left:cN} % \UnitTested % \begin{macro}[aux]{\@@_get_left:wnw} % Getting an item from the left of a sequence is pretty easy: just % trim off the first item after \cs{@@_item:n} at the start. We % append a \cs{q_no_value} item to cover the case of an empty sequence % \begin{macrocode} \cs_new_protected:Npn \seq_get_left:NN #1#2 { \tl_set:Nx #2 { \exp_after:wN \@@_get_left:wnw #1 \@@_item:n { \q_no_value } \q_stop } } \cs_new:Npn \@@_get_left:wnw #1 \@@_item:n #2#3 \q_stop { \exp_not:n {#2} } \cs_generate_variant:Nn \seq_get_left:NN { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_pop_left:NN, \seq_pop_left:cN} % \UnitTested % \begin{macro}{\seq_gpop_left:NN, \seq_gpop_left:cN} % \UnitTested % \begin{macro}[aux]{\@@_pop_left:NNN, \@@_pop_left:wnwNNN} % The approach to popping an item is pretty similar to that to get % an item, with the only difference being that the sequence itself has % to be redefined. This makes it more sensible to use an auxiliary % function for the local and global cases. % \begin{macrocode} \cs_new_protected:Npn \seq_pop_left:NN { \@@_pop:NNNN \@@_pop_left:NNN \tl_set:Nn } \cs_new_protected:Npn \seq_gpop_left:NN { \@@_pop:NNNN \@@_pop_left:NNN \tl_gset:Nn } \cs_new_protected:Npn \@@_pop_left:NNN #1#2#3 { \exp_after:wN \@@_pop_left:wnwNNN #2 \q_stop #1#2#3 } \cs_new_protected:Npn \@@_pop_left:wnwNNN #1 \@@_item:n #2#3 \q_stop #4#5#6 { #4 #5 { #1 #3 } \tl_set:Nn #6 {#2} } \cs_generate_variant:Nn \seq_pop_left:NN { c } \cs_generate_variant:Nn \seq_gpop_left:NN { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_get_right:NN, \seq_get_right:cN} % \UnitTested % \begin{macro}[aux]{\@@_get_right_loop:nn} % First remove \cs{s_@@} and prepend \cs{q_no_value}, then take two % arguments at a time. Before the right-hand end of the sequence, % this is a brace group followed by \cs{@@_item:n}, both removed by % \cs{use_none:nn}. At the end of the sequence, the two question % marks are taken by \cs{use_none:nn}, and the assignment is placed % before the right-most item. In the next iteration, % \cs{@@_get_right_loop:nn} receives two empty arguments, and % \cs{use_none:nn} stops the loop. % \begin{macrocode} \cs_new_protected:Npn \seq_get_right:NN #1#2 { \exp_after:wN \use_i_ii:nnn \exp_after:wN \@@_get_right_loop:nn \exp_after:wN \q_no_value #1 { ?? \tl_set:Nn #2 } { } { } } \cs_new_protected:Npn \@@_get_right_loop:nn #1#2 { \use_none:nn #2 {#1} \@@_get_right_loop:nn } \cs_generate_variant:Nn \seq_get_right:NN { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_pop_right:NN, \seq_pop_right:cN} % \UnitTested % \begin{macro}{\seq_gpop_right:NN, \seq_gpop_right:cN} % \UnitTested % \begin{macro}[aux]{\@@_pop_right:NNN, \@@_pop_right_loop:nn} % The approach to popping from the right is a bit more involved, but does % use some of the same ideas as getting from the right. What is needed is a % \enquote{flexible length} way to set a token list variable. This is % supplied by the |{ \if_false: } \fi:| \ldots % |\if_false: { \fi: }| construct. Using an \texttt{x}-type % expansion and a \enquote{non-expanding} definition for \cs{@@_item:n}, % the left-most $n - 1$ entries in a sequence of $n$ items will be stored % back in the sequence. That needs a loop of unknown length, hence using the % strange \cs{if_false:} way of including braces. When the last item % of the sequence is reached, the closing brace for the assignment is % inserted, and |\tl_set:Nn #3| is inserted in front of the final % entry. This therefore does the pop assignment. One more iteration % is performed, with an empty argument and \cs{use_none:nn}, which % finally stops the loop. % \begin{macrocode} \cs_new_protected:Npn \seq_pop_right:NN { \@@_pop:NNNN \@@_pop_right:NNN \tl_set:Nx } \cs_new_protected:Npn \seq_gpop_right:NN { \@@_pop:NNNN \@@_pop_right:NNN \tl_gset:Nx } \cs_new_protected:Npn \@@_pop_right:NNN #1#2#3 { \cs_set_eq:NN \@@_tmp:w \@@_item:n \cs_set_eq:NN \@@_item:n \scan_stop: #1 #2 { \if_false: } \fi: \s_@@ \exp_after:wN \use_i:nnn \exp_after:wN \@@_pop_right_loop:nn #2 { \if_false: { \fi: } \tl_set:Nx #3 } { } \use_none:nn \cs_set_eq:NN \@@_item:n \@@_tmp:w } \cs_new:Npn \@@_pop_right_loop:nn #1#2 { #2 { \exp_not:n {#1} } \@@_pop_right_loop:nn } \cs_generate_variant:Nn \seq_pop_right:NN { c } \cs_generate_variant:Nn \seq_gpop_right:NN { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\seq_get_left:NN, \seq_get_left:cN} % \begin{macro}[TF]{\seq_get_right:NN, \seq_get_right:cN} % Getting from the left or right with a check on the results. The % first argument to \cs{@@_pop_TF:NNNN} is left unused. % \begin{macrocode} \prg_new_protected_conditional:Npnn \seq_get_left:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_left:NN #1#2 } \prg_new_protected_conditional:Npnn \seq_get_right:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_right:NN #1#2 } \cs_generate_variant:Nn \seq_get_left:NNT { c } \cs_generate_variant:Nn \seq_get_left:NNF { c } \cs_generate_variant:Nn \seq_get_left:NNTF { c } \cs_generate_variant:Nn \seq_get_right:NNT { c } \cs_generate_variant:Nn \seq_get_right:NNF { c } \cs_generate_variant:Nn \seq_get_right:NNTF { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\seq_pop_left:NN, \seq_pop_left:cN} % \begin{macro}[TF]{\seq_gpop_left:NN, \seq_gpop_left:cN} % \begin{macro}[TF]{\seq_pop_right:NN, \seq_pop_right:cN} % \begin{macro}[TF]{\seq_gpop_right:NN, \seq_gpop_right:cN} % More or less the same for popping. % \begin{macrocode} \prg_new_protected_conditional:Npnn \seq_pop_left:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_set:Nn #1 #2 } \prg_new_protected_conditional:Npnn \seq_gpop_left:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_gset:Nn #1 #2 } \prg_new_protected_conditional:Npnn \seq_pop_right:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \@@_pop_right:NNN \tl_set:Nx #1 #2 } \prg_new_protected_conditional:Npnn \seq_gpop_right:NN #1#2 { T , F , TF } { \@@_pop_TF:NNNN \@@_pop_right:NNN \tl_gset:Nx #1 #2 } \cs_generate_variant:Nn \seq_pop_left:NNT { c } \cs_generate_variant:Nn \seq_pop_left:NNF { c } \cs_generate_variant:Nn \seq_pop_left:NNTF { c } \cs_generate_variant:Nn \seq_gpop_left:NNT { c } \cs_generate_variant:Nn \seq_gpop_left:NNF { c } \cs_generate_variant:Nn \seq_gpop_left:NNTF { c } \cs_generate_variant:Nn \seq_pop_right:NNT { c } \cs_generate_variant:Nn \seq_pop_right:NNF { c } \cs_generate_variant:Nn \seq_pop_right:NNTF { c } \cs_generate_variant:Nn \seq_gpop_right:NNT { c } \cs_generate_variant:Nn \seq_gpop_right:NNF { c } \cs_generate_variant:Nn \seq_gpop_right:NNTF { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_item:Nn, \seq_item:cn} % \begin{macro}[aux]{\@@_item:wNn, \@@_item:nN, \@@_item:nnn} % 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 the stop code |{ ? \__prg_break: } { }| will be used by the auxiliary, % terminating the loop and returning nothing at all. % \begin{macrocode} \cs_new:Npn \seq_item:Nn #1 { \exp_after:wN \@@_item:wNn #1 \q_stop #1 } \cs_new:Npn \@@_item:wNn \s_@@ #1 \q_stop #2#3 { \exp_args:Nf \@@_item:nnn { \exp_args:Nf \@@_item:nN { \int_eval:n {#3} } #2 } #1 { ? \__prg_break: } { } \__prg_break_point: } \cs_new:Npn \@@_item:nN #1#2 { \int_compare:nNnTF {#1} < \c_zero { \int_eval:n { \seq_count:N #2 + \c_one + #1 } } {#1} } \cs_new:Npn \@@_item:nnn #1#2#3 { \use_none:n #2 \int_compare:nNnTF {#1} = \c_one { \__prg_break:n { \exp_not:n {#3} } } { \exp_args:Nf \@@_item:nnn { \int_eval:n { #1 - 1 } } } } \cs_generate_variant:Nn \seq_item:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Mapping to sequences} % % \begin{macro}{\seq_map_break:} % \UnitTested % \begin{macro}{\seq_map_break:n} % \UnitTested % To break a function, the special token \cs{__prg_break_point:Nn} is % used to find the end of the code. Any ending code is then inserted % before the return value of \cs{seq_map_break:n} is inserted. % \begin{macrocode} \cs_new:Npn \seq_map_break: { \__prg_map_break:Nn \seq_map_break: { } } \cs_new:Npn \seq_map_break:n { \__prg_map_break:Nn \seq_map_break: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_map_function:NN, \seq_map_function:cN} % \UnitTested % \begin{macro}[aux,rEXP]{\@@_map_function:NNn} % The idea here is to apply the code of |#2| to each item in the % sequence without altering the definition of \cs{@@_item:n}. This % is done as by noting that every odd token in the sequence must be % \cs{@@_item:n}, which can be gobbled by \cs{use_none:n}. At the end of % the loop, |#2| is instead |? \seq_map_break:|, which therefore breaks the % loop without needing to do a (relatively-expensive) quark test. % \begin{macrocode} \cs_new:Npn \seq_map_function:NN #1#2 { \exp_after:wN \use_i_ii:nnn \exp_after:wN \@@_map_function:NNn \exp_after:wN #2 #1 { ? \seq_map_break: } { } \__prg_break_point:Nn \seq_map_break: { } } \cs_new:Npn \@@_map_function:NNn #1#2#3 { \use_none:n #2 #1 {#3} \@@_map_function:NNn #1 } \cs_generate_variant:Nn \seq_map_function:NN { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_push_item_def:n, \@@_push_item_def:x} % \begin{macro}[aux]{\@@_push_item_def:} % \begin{macro}[int]{\@@_pop_item_def:} % The definition of \cs{@@_item:n} needs to be saved and restored at % various points within the mapping and manipulation code. That is handled % here: as always, this approach uses global assignments. % \begin{macrocode} \cs_new_protected:Npn \@@_push_item_def:n { \@@_push_item_def: \cs_gset:Npn \@@_item:n ##1 } \cs_new_protected:Npn \@@_push_item_def:x { \@@_push_item_def: \cs_gset:Npx \@@_item:n ##1 } \cs_new_protected:Npn \@@_push_item_def: { \int_gincr:N \g__prg_map_int \cs_gset_eq:cN { __prg_map_ \int_use:N \g__prg_map_int :w } \@@_item:n } \cs_new_protected:Npn \@@_pop_item_def: { \cs_gset_eq:Nc \@@_item:n { __prg_map_ \int_use:N \g__prg_map_int :w } \int_gdecr:N \g__prg_map_int } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\seq_map_inline:Nn, \seq_map_inline:cn} % \UnitTested % The idea here is that \cs{@@_item:n} is already \enquote{applied} to % each item in a sequence, and so an in-line mapping is just a case of % redefining \cs{@@_item:n}. % \begin{macrocode} \cs_new_protected:Npn \seq_map_inline:Nn #1#2 { \@@_push_item_def:n {#2} #1 \__prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: } } \cs_generate_variant:Nn \seq_map_inline:Nn { c } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \seq_map_variable:NNn, \seq_map_variable:Ncn, % \seq_map_variable:cNn, \seq_map_variable:ccn % } % \UnitTested % This is just a specialised version of the in-line mapping function, % using an \texttt{x}-type expansion for the code set up so that the % number of |#| tokens required is as expected. % \begin{macrocode} \cs_new_protected:Npn \seq_map_variable:NNn #1#2#3 { \@@_push_item_def:x { \tl_set:Nn \exp_not:N #2 {##1} \exp_not:n {#3} } #1 \__prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: } } \cs_generate_variant:Nn \seq_map_variable:NNn { Nc } \cs_generate_variant:Nn \seq_map_variable:NNn { c , cc } % \end{macrocode} % \end{macro} % % \begin{macro}{\seq_count:N, \seq_count:c} % \begin{macro}[aux]{\@@_count:n} % Counting the items in a sequence is done using the same approach as for % other count functions: turn each entry into a \texttt{+1} then use % integer evaluation to actually do the mathematics. % \begin{macrocode} \cs_new:Npn \seq_count:N #1 { \int_eval:n { 0 \seq_map_function:NN #1 \@@_count:n } } \cs_new:Npn \@@_count:n #1 { + \c_one } \cs_generate_variant:Nn \seq_count:N { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Using sequences} % % \begin{macro}[EXP]{\seq_use:Nnnn, \seq_use:cnnn} % \begin{macro}[EXP, aux] % {\@@_use:NNnNnn, \@@_use_setup:w, \@@_use:nwwwwnwn, \@@_use:nwwn} % \begin{macro}[EXP]{\seq_use:Nn, \seq_use:cn} % See \cs{clist_use:Nnnn} for a general explanation. The main % difference is that we use \cs{@@_item:n} as a delimiter rather than % commas. We also need to add \cs{@@_item:n} at various places, and % \cs{s_@@}. % \begin{macrocode} \cs_new:Npn \seq_use:Nnnn #1#2#3#4 { \seq_if_exist:NTF #1 { \int_case:nnF { \seq_count:N #1 } { { 0 } { } { 1 } { \exp_after:wN \@@_use:NNnNnn #1 ? { } { } } { 2 } { \exp_after:wN \@@_use:NNnNnn #1 {#2} } } { \exp_after:wN \@@_use_setup:w #1 \@@_item:n \q_mark { \@@_use:nwwwwnwn {#3} } \q_mark { \@@_use:nwwn {#4} } \q_stop { } } } { \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#1} } } \cs_generate_variant:Nn \seq_use:Nnnn { c } \cs_new:Npn \@@_use:NNnNnn #1#2#3#4#5#6 { \exp_not:n { #3 #6 #5 } } \cs_new:Npn \@@_use_setup:w \s_@@ { \@@_use:nwwwwnwn { } } \cs_new:Npn \@@_use:nwwwwnwn #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4#5 \q_mark #6#7 \q_stop #8 { #6 \@@_item:n {#3} \@@_item:n {#4} #5 \q_mark {#6} #7 \q_stop { #8 #1 #2 } } \cs_new:Npn \@@_use:nwwn #1 \@@_item:n #2 #3 \q_stop #4 { \exp_not:n { #4 #1 #2 } } \cs_new:Npn \seq_use:Nn #1#2 { \seq_use:Nnnn #1 {#2} {#2} {#2} } \cs_generate_variant:Nn \seq_use:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Sequence stacks} % % The same functions as for sequences, but with the correct naming. % % \begin{macro}{ % \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:No, \seq_push:Nx, % \seq_push:cn, \seq_push:cV, \seq_push:cV, \seq_push:co, \seq_push:cx % } % \UnitTested % \begin{macro}{ % \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:No, \seq_gpush:Nx, % \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \seq_gpush:co, \seq_gpush:cx % } % \UnitTested % Pushing to a sequence is the same as adding on the left. % \begin{macrocode} \cs_new_eq:NN \seq_push:Nn \seq_put_left:Nn \cs_new_eq:NN \seq_push:NV \seq_put_left:NV \cs_new_eq:NN \seq_push:Nv \seq_put_left:Nv \cs_new_eq:NN \seq_push:No \seq_put_left:No \cs_new_eq:NN \seq_push:Nx \seq_put_left:Nx \cs_new_eq:NN \seq_push:cn \seq_put_left:cn \cs_new_eq:NN \seq_push:cV \seq_put_left:cV \cs_new_eq:NN \seq_push:cv \seq_put_left:cv \cs_new_eq:NN \seq_push:co \seq_put_left:co \cs_new_eq:NN \seq_push:cx \seq_put_left:cx \cs_new_eq:NN \seq_gpush:Nn \seq_gput_left:Nn \cs_new_eq:NN \seq_gpush:NV \seq_gput_left:NV \cs_new_eq:NN \seq_gpush:Nv \seq_gput_left:Nv \cs_new_eq:NN \seq_gpush:No \seq_gput_left:No \cs_new_eq:NN \seq_gpush:Nx \seq_gput_left:Nx \cs_new_eq:NN \seq_gpush:cn \seq_gput_left:cn \cs_new_eq:NN \seq_gpush:cV \seq_gput_left:cV \cs_new_eq:NN \seq_gpush:cv \seq_gput_left:cv \cs_new_eq:NN \seq_gpush:co \seq_gput_left:co \cs_new_eq:NN \seq_gpush:cx \seq_gput_left:cx % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\seq_get:NN, \seq_get:cN} % \UnitTested % \begin{macro}{\seq_pop:NN, \seq_pop:cN} % \UnitTested % \begin{macro}{\seq_gpop:NN, \seq_gpop:cN} % \UnitTested % In most cases, getting items from the stack does not need to specify % that this is from the left. So alias are provided. % \begin{macrocode} \cs_new_eq:NN \seq_get:NN \seq_get_left:NN \cs_new_eq:NN \seq_get:cN \seq_get_left:cN \cs_new_eq:NN \seq_pop:NN \seq_pop_left:NN \cs_new_eq:NN \seq_pop:cN \seq_pop_left:cN \cs_new_eq:NN \seq_gpop:NN \seq_gpop_left:NN \cs_new_eq:NN \seq_gpop:cN \seq_gpop_left:cN % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\seq_get:NN, \seq_get:cN} % \begin{macro}[TF]{\seq_pop:NN, \seq_pop:cN} % \begin{macro}[TF]{\seq_gpop:NN, \seq_gpop:cN} % More copies. % \begin{macrocode} \prg_new_eq_conditional:NNn \seq_get:NN \seq_get_left:NN { T , F , TF } \prg_new_eq_conditional:NNn \seq_get:cN \seq_get_left:cN { T , F , TF } \prg_new_eq_conditional:NNn \seq_pop:NN \seq_pop_left:NN { T , F , TF } \prg_new_eq_conditional:NNn \seq_pop:cN \seq_pop_left:cN { T , F , TF } \prg_new_eq_conditional:NNn \seq_gpop:NN \seq_gpop_left:NN { T , F , TF } \prg_new_eq_conditional:NNn \seq_gpop:cN \seq_gpop_left:cN { T , F , TF } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Viewing sequences} % % \begin{macro}{\seq_show:N, \seq_show:c} % \UnitTested % Apply the general \cs{__msg_show_variable:NNNnn}. % \begin{macrocode} \cs_new_protected:Npn \seq_show:N #1 { \__msg_show_variable:NNNnn #1 \seq_if_exist:NTF \seq_if_empty:NTF { seq } { \seq_map_function:NN #1 \__msg_show_item:n } } \cs_generate_variant:Nn \seq_show:N { c } % \end{macrocode} % \end{macro} % % \subsection{Scratch sequences} % % \begin{variable}{\l_tmpa_seq, \l_tmpb_seq, \g_tmpa_seq, \g_tmpb_seq} % Temporary comma list variables. % \begin{macrocode} \seq_new:N \l_tmpa_seq \seq_new:N \l_tmpb_seq \seq_new:N \g_tmpa_seq \seq_new:N \g_tmpb_seq % \end{macrocode} % \end{variable} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex