diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3kernel/l3seq.dtx |
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3seq.dtx')
-rw-r--r-- | macros/latex/contrib/l3kernel/l3seq.dtx | 2070 |
1 files changed, 2070 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/l3seq.dtx b/macros/latex/contrib/l3kernel/l3seq.dtx new file mode 100644 index 0000000000..2a0083bd6a --- /dev/null +++ b/macros/latex/contrib/l3kernel/l3seq.dtx @@ -0,0 +1,2070 @@ +% \iffalse meta-comment +% +%% File: l3seq.dtx +% +% Copyright (C) 1990-2019 The LaTeX3 Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3kernel bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3seq} package\\ Sequences and stacks^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2019-08-25} +% +% \maketitle +% +% \begin{documentation} +% +% \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} +% initially contains 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} are 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} +% is set to 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} +% is set to 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} is set to +% 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} is set to +% 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} is set to +% 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} is set to +% 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 evaluates the \meta{integer expression} and leaves 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. If 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 expands to +% nothing. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2016-12-06]{\seq_rand_item:N, \seq_rand_item:c} +% \begin{syntax} +% \cs{seq_rand_item:N} \meta{seq~var} +% \end{syntax} +% Selects a pseudo-random item of the \meta{sequence}. If the +% \meta{sequence} is empty the result is empty. +% This is not available in older versions of \XeTeX{}. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{x}-type +% argument expansion. +% \end{texnote} +% \end{function} +% +% \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 the +% \meta{sequence} +% in the \meta{token list variable} without removing it from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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 the +% \meta{sequence} +% in the \meta{token list variable} without removing it from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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 the +% \meta{sequence} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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 the \meta{sequence} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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 the \meta{sequence} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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 the \meta{sequence} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{sequence}, then leaves the \meta{true code} in the input stream. +% 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} +% +% All mappings are done at the current group level, \emph{i.e.}~any +% local assignments made by the \meta{function} or \meta{code} discussed +% below remain in effect after the loop. +% +% \begin{function}[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. +% \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|. +% 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{variable} \Arg{code} +% \end{syntax} +% Stores each \meta{item} of the \meta{sequence} in turn in the (token +% list) \meta{variable} and applies the \meta{code}. The \meta{code} +% will usually make use of the \meta{variable}, but this is not +% enforced. The assignments to the \meta{variable} are local. Its +% value after the loop is the last \meta{item} in the \meta{sequence}, +% or its original value if the \meta{sequence} is empty. 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 +% normally takes 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 leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before further items are taken +% from the input stream. This depends 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{code} +% \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{code} after the mapping has ended. This +% normally takes 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 { <code> } } +% { +% % Do something useful +% } +% } +% \end{verbatim} +% Use outside of a \cs[no-index]{seq_map_\ldots} scenario leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before the \meta{code} is +% inserted into the input stream. +% This depends on the design of the mapping function. +% \end{texnote} +% \end{function} +% +% \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} includes 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 is 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} +% inserts \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} +% do 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 is 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} +% inserts \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} +% do 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} is set to +% 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} +% is set to 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} is set to +% 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 occurrences 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} +% +% \begin{function}[added = 2014-08-12, updated = 2015-08-01]{\seq_log:N, \seq_log:c} +% \begin{syntax} +% \cs{seq_log:N} \meta{sequence} +% \end{syntax} +% Writes the entries in the \meta{sequence} in the log file. +% \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{function}[EXP]{\@@_item:n} +% \begin{syntax} +% \cs{@@_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 is +% raised. The definition should always be set globally. +% \end{function} +% +% \begin{function}{\@@_push_item_def:n, \@@_push_item_def:x} +% \begin{syntax} +% \cs{@@_push_item_def:n} \Arg{code} +% \end{syntax} +% Saves the definition of \cs{@@_item:n} and redefines it to +% accept one parameter and expand to \meta{code}. This function +% should always be balanced by use of \cs{@@_pop_item_def:}. +% \end{function} +% +% \begin{function}{\@@_pop_item_def:} +% \begin{syntax} +% \cs{@@_pop_item_def:} +% \end{syntax} +% Restores the definition of \cs{@@_item:n} most recently saved by +% \cs{@@_push_item_def:n}. This function should always be used in +% a balanced pair with \cs{@@_push_item_def:n}. +% \end{function} +% +% \begin{variable}{\s_@@} +% This private scan mark. +% \begin{macrocode} +\scan_new:N \s_@@ +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_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 + { + \__kernel_msg_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}{\@@_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 + { + \__kernel_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}{\@@_set_split:NNnn} +% \begin{macro} +% { +% \@@_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}[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}{\@@_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}{\@@_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}{\@@_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 +% happens 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}|) ensures 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}{\@@_reverse:NN} +% \begin{macro}[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: + } +\prg_generate_conditional_variant:Nnn \seq_if_empty:N + { c } { p , T , F , TF } +% \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}{\@@_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 breaks, 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: } } +\prg_generate_conditional_variant:Nnn \seq_if_in:Nn + { NV , Nv , No , Nx , c , cV , cv , co , cx } { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Recovering data from sequences} +% +% \begin{macro}{\@@_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}{\@@_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}{\@@_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}[EXP]{\@@_get_right_loop:nw, \@@_get_right_end:NnN} +% First remove \cs{s_@@} and prepend \cs{q_no_value}. The first +% argument of \cs{@@_get_right_loop:nw} is the last item found, and +% the second argument is empty until the end of the loop, where it is +% code that applies \cs{exp_not:n} to the last item and ends the loop. +% \begin{macrocode} +\cs_new_protected:Npn \seq_get_right:NN #1#2 + { + \tl_set:Nx #2 + { + \exp_after:wN \use_i_ii:nnn + \exp_after:wN \@@_get_right_loop:nw + \exp_after:wN \q_no_value + #1 + \@@_get_right_end:NnN \@@_item:n + } + } +\cs_new:Npn \@@_get_right_loop:nw #1#2 \@@_item:n + { + #2 \use_none:n {#1} + \@@_get_right_loop:nw + } +\cs_new:Npn \@@_get_right_end:NnN #1#2#3 { \exp_not:n {#2} } +\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}{\@@_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 are 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 } +\prg_generate_conditional_variant:Nnn \seq_get_left:NN + { c } { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_get_right:NN + { c } { T , F , TF } +% \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 } +\prg_generate_conditional_variant:Nnn \seq_pop_left:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_gpop_left:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_pop_right:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_gpop_right:NN { c } + { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_item:Nn, \seq_item:cn} +% \begin{macro}{\@@_item:wNn, \@@_item:nN, \@@_item:nwn} +% 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 argument delimited by \cs{@@_item:n} is \cs{prg_break:} instead +% of being empty, 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:nwn + { \exp_args:Nf \@@_item:nN { \int_eval:n {#3} } #2 } + #1 + \prg_break: \@@_item:n { } + \prg_break_point: + } +\cs_new:Npn \@@_item:nN #1#2 + { + \int_compare:nNnTF {#1} < 0 + { \int_eval:n { \seq_count:N #2 + 1 + #1 } } + {#1} + } +\cs_new:Npn \@@_item:nwn #1#2 \@@_item:n #3 + { + #2 + \int_compare:nNnTF {#1} = 1 + { \prg_break:n { \exp_not:n {#3} } } + { \exp_args:Nf \@@_item:nwn { \int_eval:n { #1 - 1 } } } + } +\cs_generate_variant:Nn \seq_item:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_rand_item:N, \seq_rand_item:c} +% Importantly, \cs{seq_item:Nn} only evaluates its argument once. +% \begin{macrocode} +\cs_new:Npn \seq_rand_item:N #1 + { + \seq_if_empty:NF #1 + { \seq_item:Nn #1 { \int_rand:nn { 1 } { \seq_count:N #1 } } } + } +\cs_generate_variant:Nn \seq_rand_item:N { c } +% \end{macrocode} +% \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}[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}. The +% argument delimited by \cs{@@_item:n} is almost always empty, except +% at the end of the loop where it is \cs{prg_break:}. This allows to +% break 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:Nw + \exp_after:wN #2 + #1 + \prg_break: \@@_item:n { } \prg_break_point: + \prg_break_point:Nn \seq_map_break: { } + } +\cs_new:Npn \@@_map_function:Nw #1#2 \@@_item:n #3 + { + #2 + #1 {#3} + \@@_map_function:Nw #1 + } +\cs_generate_variant:Nn \seq_map_function:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_push_item_def:n, \@@_push_item_def:x} +% \begin{macro}{\@@_push_item_def:} +% \begin{macro}{\@@_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__kernel_prg_map_int + \cs_gset_eq:cN { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + \@@_item:n + } +\cs_new_protected:Npn \@@_pop_item_def: + { + \cs_gset_eq:Nc \@@_item:n + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + \int_gdecr:N \g__kernel_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}{\@@_count:w, \@@_count_end:w} +% Since counting the items in a sequence is quite common, we optimize +% it by grabbing $8$~items at a time and correspondingly adding $8$ to +% an integer expression. At the end of the loop, |#9| is +% \cs{@@_count_end:w} instead of being empty. It removes |8+| and +% instead places the number of \cs{@@_item:n} that \cs{@@_count:w} +% grabbed before reaching the end of the sequence. +% \begin{macrocode} +\cs_new:Npn \seq_count:N #1 + { + \int_eval:n + { + \exp_after:wN \use_i:nn + \exp_after:wN \@@_count:w + #1 + \@@_count_end:w \@@_item:n 7 + \@@_count_end:w \@@_item:n 6 + \@@_count_end:w \@@_item:n 5 + \@@_count_end:w \@@_item:n 4 + \@@_count_end:w \@@_item:n 3 + \@@_count_end:w \@@_item:n 2 + \@@_count_end:w \@@_item:n 1 + \@@_count_end:w \@@_item:n 0 + \prg_break_point: + } + } +\cs_new:Npn \@@_count:w + #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4 \@@_item:n + #5 \@@_item:n #6 \@@_item:n #7 \@@_item:n #8 #9 \@@_item:n + { #9 8 + \@@_count:w } +\cs_new:Npn \@@_count_end:w 8 + \@@_count:w #1#2 \prg_break_point: {#1} +\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] +% {\@@_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 { } + } + } + { + \__kernel_msg_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, \seq_log:N, \seq_log:c, \@@_show:NN} +% \UnitTested +% Apply the general \cs{msg_show:nnnnnn}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_show:N { \@@_show:NN \msg_show:nnxxxx } +\cs_generate_variant:Nn \seq_show:N { c } +\cs_new_protected:Npn \seq_log:N { \@@_show:NN \msg_log:nnxxxx } +\cs_generate_variant:Nn \seq_log:N { c } +\cs_new_protected:Npn \@@_show:NN #1#2 + { + \__kernel_chk_defined:NT #2 + { + #1 { LaTeX/kernel } { show-seq } + { \token_to_str:N #2 } + { \seq_map_function:NN #2 \msg_show_item:n } + { } { } + } + } +% \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} +%</initex|package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |