summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3clist.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3clist.dtx1437
1 files changed, 1437 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx b/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
new file mode 100644
index 00000000000..d031b15f552
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
@@ -0,0 +1,1437 @@
+% \iffalse meta-comment
+%
+%% File: l3clist.dtx Copyright (C) 2004-2011 Frank Mittelbach,
+%% 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 "expl3 bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX3 Project.
+%%
+%% -----------------------------------------------------------------------
+%
+%<*driver|package>
+\RequirePackage{l3names}
+\GetIdInfo$Id: l3clist.dtx 2498 2011-07-09 17:51:47Z joseph $
+ {L3 Experimental comma separated lists}
+%</driver|package>
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3clist} package\\ Comma separated lists^^A
+% \thanks{This file describes v\ExplFileVersion,
+% last revised \ExplFileDate.}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released \ExplFileDate}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Comma lists contain ordered data where items can be added to the left
+% or right end of the sequence. This gives an ordered list which can
+% then be utilised with the \cs{clist_map_function:NN} function. Comma
+% Comma lists cannot contain empty items, thus
+% \begin{verbatim}
+% \clist_new:N \l_my_clist
+% \clist_put_right:Nn \l_my_clist { }
+% \clist_if_empty:NTF \l_my_clist { true } { false }
+% \end{verbatim}
+% will leave \texttt{true} in the input stream.
+%
+% \section{Creating and initialising comma lists}
+%
+% \begin{function}{\clist_new:N, \clist_new:c}
+% \begin{syntax}
+% \cs{clist_new:N} \meta{comma list}
+% \end{syntax}
+% Creates a new \meta{comma list} or raises an error if the name is
+% already taken. The declaration is global. The \meta{comma list} will
+% initially contain no items.
+% \end{function}
+%
+% \begin{function}{\clist_clear:N, \clist_clear:c}
+% \begin{syntax}
+% \cs{clist_clear:N} \meta{comma list}
+% \end{syntax}
+% Clears all items from the \meta{comma list} within the scope of
+% the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}{\clist_gclear:N, \clist_gclear:c}
+% \begin{syntax}
+% \cs{clist_gclear:N} \meta{comma list}
+% \end{syntax}
+% Clears all entries from the \meta{comma list} globally.
+% \end{function}
+%
+% \begin{function}{\clist_clear_new:N, \clist_clear_new:c}
+% \begin{syntax}
+% \cs{clist_clear_new:N} \meta{comma list}
+% \end{syntax}
+% If the \meta{comma list} already exists, clears it within the scope
+% of the current \TeX{} group. If the \meta{comma list} is not defined,
+% it will be created (using \cs{clist_new:N}). Thus the comma list is
+% guaranteed to be available and clear within the current \TeX{}
+% group. The \meta{comma list} will exist globally, but the content
+% outside of the current \TeX{} group is not specified.
+% \end{function}
+%
+% \begin{function}{\clist_gclear_new:N, \clist_gclear_new:c}
+% \begin{syntax}
+% \cs{clist_gclear_new:N} \meta{comma list}
+% \end{syntax}
+% If the \meta{comma list} already exists, clears it globally. If the
+% \meta{comma list} is not defined, it will be created (using
+% \cs{clist_new:N}). Thus the comma list is guaranteed to be available
+% and globally clear.
+% \end{function}
+%
+% \begin{function}
+% {\clist_set_eq:NN, \clist_set_eq:cN, \clist_set_eq:Nc, \clist_set_eq:cc}
+% \begin{syntax}
+% \cs{clist_set_eq:NN} \meta{comma list1} \meta{comma list2}
+% \end{syntax}
+% Sets the content of \meta{comma list1} equal to that of
+% \meta{comma list2}. This assignment is restricted to the current
+% \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_gset_eq:NN, \clist_gset_eq:cN,
+% \clist_gset_eq:Nc, \clist_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{clist_gset_eq:NN} \meta{comma list1} \meta{comma list2}
+% \end{syntax}
+% Sets the content of \meta{comma list1} equal to that of
+% \meta{comma list2}. This assignment is global and so is not
+% limited by the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}{\clist_concat:NNN, \clist_concat:ccc}
+% \begin{syntax}
+% \cs{clist_concat:NNN} \meta{comma list1} \meta{comma list2}
+% ~~\meta{comma list3}
+% \end{syntax}
+% Concatenates the content of \meta{comma list2} and \meta{comma list3}
+% together and saves the result in \meta{comma list1}. The items in
+% \meta{comma list2} will be placed at the left side of the new comma list.
+% This operation is local to the current \TeX{} group and will
+% remove any existing content in \meta{comma list1}.
+% \end{function}
+%
+% \begin{function}{\clist_gconcat:NNN, \clist_gconcat:ccc}
+% \begin{syntax}
+% \cs{clist_gconcat:NNN} \meta{comma list1} \meta{comma list2}
+% ~~\meta{comma list3}
+% \end{syntax}
+% Concatenates the content of \meta{comma list2} and \meta{comma list3}
+% together and saves the result in \meta{comma list1}. The items in
+% \meta{comma list2} will be placed at the left side of the new comma list.
+% This operation is global and will remove any existing content in
+% \meta{comma list1}.
+% \end{function}
+%
+% \section{Appending items to comma lists}
+%
+% \begin{function}{
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:No, \clist_put_left:Nx,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:co, \clist_put_left:cx
+% }
+% \begin{syntax}
+% \cs{clist_put_left:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the left of the \meta{comma list}.
+% The assignment is restricted to the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}{
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:No, \clist_gput_left:Nx,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \clist_gput_left:co, \clist_gput_left:cx
+% }
+% \begin{syntax}
+% \cs{clist_gput_left:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the left of the \meta{comma list}.
+% The assignment is global.
+% \end{function}
+%
+% \begin{function}{
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:No, \clist_put_right:Nx,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:co, \clist_put_right:cx
+% }
+% \begin{syntax}
+% \cs{clist_put_right:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the right of the \meta{comma list}.
+% The assignment is restricted to the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}{
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:No, \clist_gput_right:Nx,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:co, \clist_gput_right:cx
+% }
+% \begin{syntax}
+% \cs{clist_gput_right:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the right of the \meta{comma list}.
+% The assignment is global.
+% \end{function}
+%
+% \section{Comma lists as stacks}
+%
+% \begin{function}{\clist_get:NN, \clist_get:cN}
+% \begin{syntax}
+% \cs{clist_get:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Stores the left-most item from a \meta{comma list} in the
+% \meta{token list variable} without removing it from the
+% \meta{comma list}. The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}{\clist_get:NN, \clist_get:cN}
+% \begin{syntax}
+% \cs{clist_get:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Stores the right-most item from a \meta{comma list} in the
+% \meta{token list variable} without removing it from the
+% \meta{comma list}. The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}{\clist_pop:NN, \clist_pop:cN}
+% \begin{syntax}
+% \cs{clist_pop:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{comma list} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% comma list and stores it in the \meta{token list variable}.
+% Both of the variables are assigned locally.
+% \end{function}
+%
+% \begin{function}{\clist_gpop:NN, \clist_gpop:cN}
+% \begin{syntax}
+% \cs{clist_gpop:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{comma list} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% comma list and stores it in the \meta{token list variable}.
+% The \meta{comma list} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_push:Nn, \clist_push:NV, \clist_push:No, \clist_push:Nx,
+% \clist_push:cn, \clist_push:cV, \clist_push:co, \clist_push:cx
+% }
+% \begin{syntax}
+% \cs{clist_push:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Adds the \Arg{item} to the top of the \meta{comma list}.
+% The assignment is restricted to the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No, \clist_gpush:Nx,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co, \clist_gpush:cx
+% }
+% \begin{syntax}
+% \cs{clist_gpush:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Pushes the \meta{item} onto the end of the top of the
+% \meta{comma list}. The assignment is global.
+% \end{function}
+%
+% \section{Using comma lists}
+%
+% \begin{function}[EXP]{\clist_use:N, \clist_use:c}
+% \begin{syntax}
+% \cs{clist_use:N} \meta{comma list}
+% \end{syntax}
+% Places the \meta{comma list} directly into the input stream, thus
+% treating it as a \meta{token list}.
+% \end{function}
+%
+% \section{Modifying comma lists}
+%
+% While comma lists are normally used as ordered lists, it may be
+% necessary to modify the content. The functions here may be used
+% to update comma lists, while retaining the order of the unaffected
+% entries.
+%
+% \begin{function}{\clist_remove_duplicates:N, \clist_remove_duplicates:c}
+% \begin{syntax}
+% \cs{clist_remove_duplicates:N} \meta{comma list}
+% \end{syntax}
+% Removes duplicate items from the \meta{comma list}, leaving the
+% left most copy of each item in the \meta{comma list}. The \meta{item}
+% comparison takes place on a token basis, as for \cs{tl_if_eq:nn(TF)}.
+% The removal is local to the current \TeX{} group.
+% \begin{texnote}
+% This function iterates through every item in the \meta{comma list} and
+% does a comparison with the \meta{items} already checked. It is therefore
+% relatively slow with large comma lists.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\clist_gremove_duplicates:N, \clist_gremove_duplicates:c}
+% \begin{syntax}
+% \cs{clist_gremove_duplicates:N} \meta{comma list}
+% \end{syntax}
+% Removes duplicate items from the \meta{comma list}, leaving the
+% left most copy of each item in the \meta{comma list}. The \meta{item}
+% comparison takes place on a token basis, as for \cs{tl_if_eq:nn(TF)}.
+% The removal is applied globally.
+% \begin{texnote}
+% This function iterates through every item in the \meta{comma list} and
+% does a comparison with the \meta{items} already checked. It is therefore
+% relatively slow with large comma lists.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\clist_remove_all:Nn, \clist_remove_all:cn}
+% \begin{syntax}
+% \cs{clist_remove_all:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Removes every occurrence of \meta{item} from the \meta{comma list}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nn(TF)}. The removal is local to the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}{\clist_gremove_all:Nn, \clist_gremove_all:cn}
+% \begin{syntax}
+% \cs{clist_gremove_all:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Removes each occurrence of \meta{item} from the \meta{comma list}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nn(TF)}. The removal is applied globally.
+% \end{function}
+%
+% \begin{function}{\clist_trim_spaces:N, \clist_trim_spaces:c}
+% \begin{syntax}
+% \cs{clist_trim_spaces:N} \meta{comma list}
+% \end{syntax}
+% Removes leading and trailing spaces from each \meta{item} in the
+% \meta{comma list} within the current \TeX{} group. This space-removal
+% process takes place as described for \cs{tl_trim_spaces:n}.
+% \end{function}
+%
+% \begin{function}{\clist_gtrim_spaces:N, \clist_gtrim_spaces:c}
+% \begin{syntax}
+% \cs{clist_gtrim_spaces:N} \meta{comma list}
+% \end{syntax}
+% Removes leading and trailing spaces from each \meta{item} in the
+% \meta{comma list} globally. This space-removal
+% process takes place as described for \cs{tl_trim_spaces:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\clist_trim_spaces:n}
+% \begin{syntax}
+% \cs{clist_trim_spaces:N} \meta{comma list}
+% \end{syntax}
+% Removes leading and trailing spaces from each \meta{item} in the
+% \meta{comma list}, leaving the resulting modified list in the
+% input stream.
+% \end{function}
+%
+% \section{Comma list conditionals}
+%
+% \begin{function}[EXP,pTF]{\clist_if_empty:N, \clist_if_empty:c}
+% \begin{syntax}
+% \cs{clist_if_empty_p:N} \meta{comma list}
+% \cs{clist_if_empty:NTF} \meta{comma list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{comma list} is empty (containing no items). The
+% branching versions then leave either \meta{true code} or
+% \meta{false code} in the input stream, as appropriate to the truth of
+% the test and the variant of the function chosen. The logical truth of
+% the test is left in the input stream by the predicate version.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]
+% {\clist_if_eq:NN, \clist_if_eq:Nc, \clist_if_eq:cN, \clist_if_eq:cc}
+% \begin{syntax}
+% \cs{clist_if_eq_p:NN} \Arg{clist1} \Arg{clist2}
+% \cs{clist_if_eq:NNTF} \Arg{clist1} \Arg{clist2} \Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% Compares the content of two \meta{comma lists} and
+% is logically \texttt{true} if the two contain the same list of
+% entries in the same order. The branching versions then leave either
+% \meta{true code} or \meta{false code} in the input stream, as
+% appropriate to the truth of the test and the variant of the function
+% chosen. The logical truth of the test is left in the input stream by
+% the predicate version.
+% \end{function}
+%
+% \begin{function}[TF]{
+% \clist_if_in:Nn, \clist_if_in:NV, \clist_if_in:No,
+% \clist_if_in:cn, \clist_if_in:cV, \clist_if_in:co,
+% \clist_if_in:nn, \clist_if_in:nV, \clist_if_in:no
+% }
+% \begin{syntax}
+% \cs{clist_if_in:NnTF} \meta{comma list} \Arg{item}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{item} is present in the \meta{comma list}.
+% Either the \meta{true code} or \meta{false code} is left in the input
+% stream, as appropriate to the truth of the test and the variant of the
+% function
+% chosen.
+% \end{function}
+%
+% \section{Mapping to comma lists}
+%
+% \begin{function}[EXP]
+% {\clist_map_function:NN, \clist_map_function:cN, \clist_map_function:nN}
+% \begin{syntax}
+% \cs{clist_map_function:NN} \meta{comma list} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} stored in the
+% \meta{comma list}. The \meta{function} will receive one argument for
+% each iteration. The \meta{items} are returned from left to right.
+% The function \cs{clist_map_inline:Nn} is in general more efficient
+% than \cs{clist_map_function:NN}.
+% One mapping may be nested inside another.
+% \end{function}
+%
+% \begin{function}
+% {\clist_map_inline:Nn, \clist_map_inline:cn, \clist_map_inline:nn}
+% \begin{syntax}
+% \cs{clist_map_inline:Nn} \meta{comma list} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{comma list}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% One in line mapping can be nested inside another. The \meta{items}
+% are returned from left to right.
+% \end{function}
+%
+% \begin{function}
+% {\clist_map_variable:NNn, \clist_map_variable:cNn, \clist_map_variable:nNn}
+% \begin{syntax}
+% \cs{clist_map_variable:NNn} \meta{comma list}
+% ~~\meta{tl~var.} \Arg{function using tl~var.}
+% \end{syntax}
+% Stores each entry in the \meta{comma list} 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}[EXP]{\clist_map_break:}
+% \begin{syntax}
+% \cs{clist_map_break:}
+% \end{syntax}
+% Used to terminate a \cs{clist_map_\ldots} function before all
+% entries in the \meta{comma list} have been processed. This will
+% normally take place within a conditional statement, for example
+% \begin{verbatim}
+% \clist_map_inline:Nn \l_my_clist
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \clist_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs{clist_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{clist_break_point:n} 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}[EXP]{\clist_map_break:n}
+% \begin{syntax}
+% \cs{clist_map_break:n} \Arg{tokens}
+% \end{syntax}
+% Used to terminate a \cs{clist_map_\ldots} function before all
+% entries in the \meta{comma list} 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}
+% \clist_map_inline:Nn \l_my_clist
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \clist_map_break:n { <tokens> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs{clist_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{clist_break_point:n} before the \meta{tokens} are
+% inserted into the input stream.
+% This will depend on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \section{Comma lists as stacks}
+%
+% Comma lists can be used as stacks, where data is pushed to and popped
+% from the top of the comma list. (The left of a comma list is the top, for
+% performance reasons.) The stack functions for comma lists are not
+% intended to be mixed with the general ordered data functions detailed
+% in the previous section: a comma list should either be used as an
+% ordered data type or as a stack, but not in both ways.
+%
+% \begin{function}{\clist_get:NN, \clist_get:cN}
+% \begin{syntax}
+% \cs{clist_get:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Reads the top item from a \meta{comma list} into the
+% \meta{token list variable} without removing it from the
+% \meta{comma list}. The \meta{token list variable} is assigned locally.
+% If \meta{comma list} is empty an error will be raised.
+% \end{function}
+%
+% \begin{function}{\clist_pop:NN, \clist_pop:cN}
+% \begin{syntax}
+% \cs{clist_pop:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{comma list} into the
+% \meta{token list variable}. Both of the variables are assigned
+% locally. If \meta{comma list} is empty an error will be raised.
+% \end{function}
+%
+% \begin{function}{\clist_gpop:NN, \clist_gpop:cN}
+% \begin{syntax}
+% \cs{clist_gpop:NN} \meta{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{comma list} into the
+% \meta{token list variable}. The \meta{comma list} is modified globally,
+% while the \meta{token list variable} is assigned locally. If
+% \meta{comma list} is empty an error will be raised.
+% \end{function}
+%
+% \begin{function}{
+% \clist_push:Nn, \clist_push:NV, \clist_push:No, \clist_push:Nx,
+% \clist_push:cn, \clist_push:cV, \clist_push:co, \clist_push:cx
+% }
+% \begin{syntax}
+% \cs{clist_push:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Adds the \Arg{item} to the top of the \meta{comma list}.
+% The assignment is restricted to the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}{
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No, \clist_gpush:Nx,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co, \clist_gpush:cx
+% }
+% \begin{syntax}
+% \cs{clist_gpush:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Pushes the \meta{item} onto the end of the top of the
+% \meta{comma list}. The assignment is global.
+% \end{function}
+%
+% \section{Viewing comma lists}
+%
+% \begin{function}{\clist_show:N, \clist_show:c}
+% \begin{syntax}
+% \cs{clist_show:N} \meta{comma list}
+% \end{syntax}
+% Displays the entries in the \meta{comma list} in the terminal.
+% \end{function}
+%
+% \section{Experimental comma list functions}
+%
+% This section contains functions which may or may not be retained, depending
+% on how useful they are found to be.
+%
+% \begin{function}[EXP]{\clist_length:N, \clist_length:c,\clist_length:n}
+% \begin{syntax}
+% \cs{clist_length:N} \meta{comma list}
+% \end{syntax}
+% Leaves the number of items in the \meta{comma list} in the input
+% stream as an \meta{integer denotation}. The total number of items
+% in a \meta{comma list} will include those which are empty and duplicates,
+% \emph{i.e.}~every item in a \meta{comma list} is unique.
+% \end{function}
+%
+% \begin{function}[EXP]{\clist_item:Nn, \clist_item:cn, \clist_item:nn}
+% \begin{syntax}
+% \cs{clist_item:Nn} \meta{comma list} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{comma list} from $0$ at the top (left), this
+% function will evaluate the \meta{integer expression} and leave the
+% appropriate item from the comma list in the input stream. If the
+% \meta{integer expression} is negative, indexing occurs from the
+% bottom (right) of the comma list. When the \meta{integer expression}
+% is larger than the number of items in the \meta{comma list} (as
+% calculated by \cs{clist_length:N}) then the function will expand to
+% nothing.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc
+% }
+% \begin{syntax}
+% \cs{clist_set_from_seq:NN} \meta{comma list} \meta{sequence}
+% \end{syntax}
+% Sets the \meta{comma list} within the current \TeX{} group to be equal
+% to the content of the \meta{sequence}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% \begin{syntax}
+% \cs{clist_gset_from_seq:NN} \meta{comma list} \meta{sequence}
+% \end{syntax}
+% Sets the \meta{comma list} globally to equal to the content of the
+% \meta{sequence}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3clist} implementation}
+%
+% \TestFiles{m3clist002}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*package>
+\ProvidesExplPackage
+ {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+\package_check_loaded_expl:
+%</package>
+% \end{macrocode}
+%
+% \begin{variable}{\l_clist_tmpa_tl, \l_clist_tmpb_tl}
+% Scratch space for various internal uses.
+% \begin{macrocode}
+\tl_new:N \l_clist_tmpa_tl
+\tl_new:N \l_clist_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Allocation and initialisation}
+%
+% \begin{macro}{\clist_new:N,\clist_new:c}
+% \UnitTested
+% Internally, comma lists are just token lists.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_new:N \tl_new:N
+\cs_new_eq:NN \clist_new:c \tl_new:c
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_clear:N, \clist_clear:c}
+% \UnitTested
+% \begin{macro}{\clist_gclear:N, \clist_gclear:c}
+% \UnitTested
+% Clearing comma lists is just the same as clearing token lists.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_clear:N \tl_clear:N
+\cs_new_eq:NN \clist_clear:c \tl_clear:c
+\cs_new_eq:NN \clist_gclear:N \tl_gclear:N
+\cs_new_eq:NN \clist_gclear:c \tl_gclear:c
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_clear_new:N, \clist_clear_new:c}
+% \UnitTested
+% \begin{macro}{\clist_gclear_new:N, \clist_gclear_new:c}
+% \UnitTested
+% Once again a copy from the token list functions.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_clear_new:N \tl_clear_new:N
+\cs_new_eq:NN \clist_clear_new:c \tl_clear_new:c
+\cs_new_eq:NN \clist_gclear_new:N \tl_gclear_new:N
+\cs_new_eq:NN \clist_gclear_new:c \tl_gclear_new:c
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\clist_set_eq:NN, \clist_set_eq:cN, \clist_set_eq:Nc, \clist_set_eq:cc}
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gset_eq:NN, \clist_gset_eq:cN,
+% \clist_gset_eq:Nc, \clist_gset_eq:cc
+% }
+% \UnitTested
+% Once again, these are simple copies from the token list functions.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \clist_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \clist_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \clist_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \clist_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \clist_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \clist_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \clist_gset_eq:cc \tl_gset_eq:cc
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_concat:NNN, \clist_concat:ccc}
+% \UnitTested
+% \begin{macro}{\clist_gconcat:NNN, \clist_gconcat:ccc}
+% \UnitTested
+% \begin{macro}[aux]{\clist_concat_aux:NNNN}
+% Concatenating sequences is not quite as easy as it seems, as there is
+% the danger that |#1| may be the same as either |#2| or |#3|. Also,
+% there needs to be the correct addition of a comma to the output. So
+% a little work to do.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \clist_concat:NNN
+ { \clist_concat_aux:NNNN \tl_set:Nx }
+\cs_new_protected_nopar:Npn \clist_gconcat:NNN
+ { \clist_concat_aux:NNNN \tl_gset:Nx }
+\cs_new_protected_nopar:Npn \clist_concat_aux:NNNN #1#2#3#4
+ {
+ #1 #2
+ {
+ \clist_if_empty:NTF #3
+ { \exp_not:o #4 }
+ {
+ \exp_not:o #3
+ \clist_if_empty:NF #4
+ {
+ ,
+ \exp_not:o #4
+ }
+ }
+ }
+ }
+\cs_generate_variant:Nn \clist_concat:NNN { ccc }
+\cs_generate_variant:Nn \clist_gconcat:NNN { ccc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Appending items to comma lists}
+%
+% \begin{macro}
+% {
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:No, \clist_put_left:Nx,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:co, \clist_put_left:cx
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:No, \clist_gput_left:Nx,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \clist_gput_left:co, \clist_gput_left:cx
+% }
+% \UnitTested
+% Comma lists cannot hold empty values: there are therefore a couple
+% of sanity checks to avoid accumulating commas.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \clist_put_left:Nn
+ { \clist_put_aux:NNnnNn \tl_set:Nn \tl_put_left:Nn { } , }
+\cs_new_protected_nopar:Npn \clist_gput_left:Nn
+ { \clist_put_aux:NNnnNn \tl_gset:Nn \tl_gput_left:Nn { } , }
+\cs_new_protected:Npn \clist_put_aux:NNnnNn #1#2#3#4#5#6
+ {
+ \clist_if_empty:NTF #5
+ { #1 #5 {#6} }
+ { \tl_if_empty:nF {#6} { #2 #5 { #3 #6 #4 } } }
+ }
+\cs_generate_variant:Nn \clist_put_left:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_put_left:Nn { c , cV , co , cx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { c , cV , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:No, \clist_put_right:Nx,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:co, \clist_put_right:cx
+% }
+% \begin{macro}
+% {
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:No, \clist_gput_right:Nx,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:co, \clist_gput_right:cx
+% }
+% The same for the right side.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_put_right:Nn
+ { \clist_put_aux:NNnnNn \tl_set:Nn \tl_put_right:Nn , { } }
+\cs_new_protected_nopar:Npn \clist_gput_right:Nn
+ { \clist_put_aux:NNnnNn \tl_gset:Nn \tl_gput_right:Nn , { } }
+\cs_generate_variant:Nn \clist_put_right:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_put_right:Nn { c , cV , co , cx }
+\cs_generate_variant:Nn \clist_gput_right:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_gput_right:Nn { c , cV , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Comma lists as stacks}
+%
+% \begin{macro}{\clist_get:NN, \clist_get:cN}
+% \UnitTested
+% \begin{macro}[aux]{\clist_get_aux:wN}
+% Getting an item from the left of a comma list is pretty easy: just
+% trim off the first item using the comma.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \clist_get:NN #1#2
+ { \exp_after:wN \clist_get_aux:wN #1 , \q_stop #2 }
+\cs_new_protected:Npn \clist_get_aux:wN #1 , #2 \q_stop #3
+ { \tl_set:Nn #3 {#1} }
+\cs_generate_variant:Nn \clist_get:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_pop:NN, \clist_pop:cN}
+% \UnitTested
+% \begin{macro}{\clist_gpop:NN, \clist_gpop:cN}
+% \UnitTested
+% \begin{macro}[aux]{\clist_pop_aux:NNN}
+% \begin{macro}[aux]{\clist_pop_aux:wNNN}
+% The aim here is to get the popped item as |#1| in the auxiliary, with
+% |#2| containing either the remainder of the list \emph{or} \cs{q_nil}
+% if there were insufficient items. That keeps the number of auxiliary
+% functions down.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \clist_pop:NN
+ { \clist_pop_aux:NNN \tl_set:Nn }
+\cs_new_protected_nopar:Npn \clist_gpop:NN
+ { \clist_pop_aux:NNN \tl_gset:Nn }
+\cs_new_protected_nopar:Npn \clist_pop_aux:NNN #1#2#3
+ { \exp_after:wN \clist_pop_aux:wNNN #2 , \q_nil , \q_nil , \q_stop #1#2#3 }
+\cs_new_protected:Npn \clist_pop_aux:wNNN #1 , #2 , \q_nil , #3 \q_stop #4#5#6
+ {
+ \quark_if_nil:nTF {#2}
+ { #4 #5 { } }
+ { #4 #5 {#2} }
+ \tl_set:Nn #6 {#1}
+ }
+\cs_generate_variant:Nn \clist_pop:NN { c }
+\cs_generate_variant:Nn \clist_gpop:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \clist_push:Nn, \clist_push:NV, \clist_push:No, \clist_push:Nx,
+% \clist_push:cn, \clist_push:cV, \clist_push:co, \clist_push:cx
+% }
+% \UnitTested
+% \begin{macro}{
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No, \clist_gpush:Nx,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co, \clist_gpush:cx
+% }
+% \UnitTested
+% Pushing to a sequence is the same as adding on the left.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_push:Nn \clist_put_left:Nn
+\cs_new_eq:NN \clist_push:NV \clist_put_left:NV
+\cs_new_eq:NN \clist_push:No \clist_put_left:No
+\cs_new_eq:NN \clist_push:Nx \clist_put_left:Nx
+\cs_new_eq:NN \clist_push:cn \clist_put_left:cn
+\cs_new_eq:NN \clist_push:cV \clist_put_left:cV
+\cs_new_eq:NN \clist_push:co \clist_put_left:co
+\cs_new_eq:NN \clist_push:cx \clist_put_left:cx
+\cs_new_eq:NN \clist_gpush:Nn \clist_gput_left:Nn
+\cs_new_eq:NN \clist_gpush:NV \clist_gput_left:NV
+\cs_new_eq:NN \clist_gpush:No \clist_gput_left:No
+\cs_new_eq:NN \clist_gpush:Nx \clist_gput_left:Nx
+\cs_new_eq:NN \clist_gpush:cn \clist_gput_left:cn
+\cs_new_eq:NN \clist_gpush:cV \clist_gput_left:cV
+\cs_new_eq:NN \clist_gpush:co \clist_gput_left:co
+\cs_new_eq:NN \clist_gpush:cx \clist_gput_left:cx
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using comma lists}
+%
+% \begin{macro}{\clist_use:N, \clist_use:c}
+% \UnitTested
+% The approach is the same as for \cs{tl_use:N}.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_use:N \tl_use:N
+\cs_new_eq:NN \clist_use:c \tl_use:c
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Modifying comma lists}
+%
+% \begin{variable}{\l_clist_remove_clist}
+% An internal comma list for the removal routines.
+% \begin{macrocode}
+\clist_new:N \l_clist_remove_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\clist_remove_duplicates:N, \clist_remove_duplicates:c}
+% \UnitTested
+% \begin{macro}{\clist_gremove_duplicates:N, \clist_gremove_duplicates:c}
+% \UnitTested
+% \begin{macro}[aux]{\clist_remove_duplicates_aux:NN}
+% Removing duplicates means making a new list then copying it.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_remove_duplicates:N
+ { \clist_remove_duplicates_aux:NN \clist_set_eq:NN }
+\cs_new_protected:Npn \clist_gremove_duplicates:N
+ { \clist_remove_duplicates_aux:NN \clist_gset_eq:NN }
+\cs_new_protected:Npn \clist_remove_duplicates_aux:NN #1#2
+ {
+ \clist_clear:N \l_clist_remove_clist
+ \clist_map_inline:Nn #2
+ {
+ \clist_if_in:NnF \l_clist_remove_clist {##1}
+ { \clist_put_right:Nn \l_clist_remove_clist {##1} }
+ }
+ #1 #2 \l_clist_remove_clist
+ }
+\cs_generate_variant:Nn \clist_remove_duplicates:N { c }
+\cs_generate_variant:Nn \clist_gremove_duplicates:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_remove_all:Nn, \clist_remove_all:cn}
+% \UnitTested
+% \begin{macro}{\clist_gremove_all:Nn, \clist_gremove_all:cn}
+% \UnitTested
+% \begin{macro}[aux]{\clist_remove_all_aux:NNn}
+% \begin{macro}[aux]{\clist_remove_all_aux:w}
+% Removing an item from a comma list is done without looping over
+% the entire list, as the performance of that approach is very bad for
+% long lists. Instead, a delimited function is needed. For this to work
+% correctly, there is a need to add an additional comma at the start of
+% the list, and to remove it again once the removal is complete. Of
+% course, the list can end up empty, which is the reason for the test
+% before copying back to the parent.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_remove_all:Nn
+ { \clist_remove_all_aux:NNn \clist_set_eq:NN }
+\cs_new_protected:Npn \clist_gremove_all:Nn
+ { \clist_remove_all_aux:NNn \clist_gset_eq:NN }
+\cs_new_protected:Npn \clist_remove_all_aux:NNn #1#2#3
+ {
+ \clist_if_empty:NF #2
+ {
+ \clist_clear:N \l_clist_remove_clist
+ \cs_set_protected:Npn \clist_remove_all_aux:w
+ ##1 , #3 , ##2 \q_stop
+ {
+ \tl_put_right:Nn \l_clist_remove_clist {##1}
+ \quark_if_no_value:nF {##2}
+ { \clist_remove_all_aux:w , ##2 \q_stop }
+ }
+ \exp_after:wN \clist_remove_all_aux:w
+ \exp_after:wN , #2 , #3 , \q_no_value \q_stop
+ \tl_if_empty:NF \l_clist_remove_clist
+ {
+ \exp_after:wN \tl_set:No \exp_after:wN
+ \l_clist_remove_clist \exp_after:wN
+ { \exp_after:wN \use_none:n \l_clist_remove_clist }
+ }
+ #1 #2 \l_clist_remove_clist
+ }
+ }
+\cs_new_protected:Npn \clist_remove_all_aux:w { }
+\cs_generate_variant:Nn \clist_remove_all:Nn { c }
+\cs_generate_variant:Nn \clist_gremove_all:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_trim_spaces:n}
+% \begin{macro}
+% {
+% \clist_trim_spaces:N, \clist_trim_spaces:c,
+% \clist_gtrim_spaces:N, \clist_gtrim_spaces:c
+% }
+% \begin{macro}{\clist_trim_spaces_aux_i:n}
+% \begin{macro}{\clist_trim_spaces_aux_ii:n}
+% Here, the basic plan is to use \cs{tl_trim_spaces:n} to do the work:
+% the only issue is to make sure that the number of commas at the end of
+% the process is correct.
+% \begin{macrocode}
+\cs_new:Npn \clist_trim_spaces:n #1
+ {
+ \exp_args:Nf \clist_trim_spaces_aux_i:n
+ { \clist_map_function:nN {#1} \clist_trim_spaces_aux_ii:n }
+ }
+\cs_new:Npn \clist_trim_spaces_aux_i:n #1 { \use_ii:nn #1 }
+\cs_new:Npn \clist_trim_spaces_aux_ii:n #1
+ { , \tl_trim_spaces:n {#1} }
+\cs_new_protected:Npn \clist_trim_spaces:N #1
+ { \tl_set:Nf #1 { \exp_args:No \clist_trim_spaces:n #1 } }
+\cs_new_protected:Npn \clist_gtrim_spaces:N #1
+ { \tl_gset:Nf #1 { \exp_args:No \clist_trim_spaces:n #1 } }
+\cs_generate_variant:Nn \clist_trim_spaces:N { c }
+\cs_generate_variant:Nn \clist_gtrim_spaces:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Comma list conditionals}
+%
+% \begin{macro}[aux]{\clist_tmp:w}
+% A temporary function for comparison.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_tmp:w { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\clist_if_empty:N, \clist_if_empty:c}
+% \UnitTested
+% Simple copies from the token list variable material.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \clist_if_empty:N \tl_if_empty:N { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_empty:c \tl_if_empty:c { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]
+% {\clist_if_eq:NN, \clist_if_eq:Nc, \clist_if_eq:cN, \clist_if_eq:cc}
+% \UnitTested
+% Simple copies from the token list variable material.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \clist_if_eq:NN \tl_if_eq:NN { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_eq:Nc \tl_if_eq:Nc { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_eq:cN \tl_if_eq:cN { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_eq:cc \tl_if_eq:cc { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \clist_if_in:Nn, \clist_if_in:NV, \clist_if_in:No,
+% \clist_if_in:cn, \clist_if_in:cV, \clist_if_in:co
+% }
+% \UnitTested
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \clist_if_in:Nn #1#2
+ { T , F , TF }
+ {
+ \cs_set_protected:Npn \clist_tmp:w ##1 , #2 , ##2##3 \q_stop
+ {
+ \if_meaning:w \q_no_value ##2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+ \exp_last_unbraced:NNo \clist_tmp:w , #1 , #2 , \q_no_value \q_stop
+ }
+\prg_new_protected_conditional:Npnn \clist_if_in:nn #1#2
+ { T , F , TF }
+ {
+ \cs_set_protected:Npn \clist_tmp:w ##1 , #2 , ##2##3 \q_stop
+ {
+ \if_meaning:w \q_no_value ##2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+ \clist_tmp:w , #1 , #2 , \q_no_value \q_stop
+ }
+\cs_generate_variant:Nn \clist_if_in:NnT { NV , No }
+\cs_generate_variant:Nn \clist_if_in:NnT { c , cV , co }
+\cs_generate_variant:Nn \clist_if_in:NnF { NV , No }
+\cs_generate_variant:Nn \clist_if_in:NnF { c , cV , co }
+\cs_generate_variant:Nn \clist_if_in:NnTF { NV , No }
+\cs_generate_variant:Nn \clist_if_in:NnTF { c , cV , co }
+\cs_generate_variant:Nn \clist_if_in:nnT { nV , no }
+\cs_generate_variant:Nn \clist_if_in:nnF { nV , no }
+\cs_generate_variant:Nn \clist_if_in:nnTF { nV , no }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Mapping to comma lists}
+%
+% \begin{macro}{\clist_map_function:NN, \clist_map_function:cN}
+% \UnitTested
+% \begin{macro}{\clist_map_function:nN}
+% \UnitTested
+% \begin{macro}[aux]{\clist_map_function_aux:Nw}
+% Mapping to comma lists is pretty simple, if not massively efficient.
+% \begin{macrocode}
+\cs_new_nopar:Npn \clist_map_function:NN #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_last_unbraced:NNo \clist_map_function_aux:Nw #2 #1
+ , \q_recursion_tail , \q_recursion_stop
+ }
+ }
+\cs_new:Npn \clist_map_function:nN #1#2
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \clist_map_function_aux:Nw #2 #1
+ , \q_recursion_tail , \q_recursion_stop
+ }
+ }
+\cs_new:Npn \clist_map_function_aux:Nw #1#2 ,
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ #1 {#2}
+ \clist_map_function_aux:Nw #1
+ }
+\cs_generate_variant:Nn \clist_map_function:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\g_clist_map_inline_int}
+% For the nesting of mappings.
+% \begin{macrocode}
+\int_new:N \g_clist_map_inline_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\clist_map_inline:Nn, \clist_map_inline:cn}
+% \UnitTested
+% \begin{macro}{\clist_map_inline:nn}
+% \UnitTested
+% Inline mapping is done by creating a suitable function
+% \enquote{on the fly}:
+% this is done globally to avoid any issues with \TeX{}'s groups.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_map_inline:Nn #1#2
+ {
+ \int_gincr:N \g_clist_map_inline_int
+ \cs_gset:cpn { clist_map_inline_ \int_use:N \g_clist_map_inline_int :n }
+ ##1
+ {#2}
+ \exp_args:NNc \clist_map_function:NN #1
+ { clist_map_inline_ \int_use:N \g_clist_map_inline_int :n }
+ \int_gdecr:N \g_clist_map_inline_int
+ }
+\cs_new_protected:Npn \clist_map_inline:nn #1#2
+ {
+ \int_gincr:N \g_clist_map_inline_int
+ \cs_gset:cpn { clist_map_inline_ \int_use:N \g_clist_map_inline_int :n }
+ ##1
+ {#2}
+ \exp_args:Nnc \clist_map_function:nN {#1}
+ { clist_map_inline_ \int_use:N \g_clist_map_inline_int :n }
+ \int_gdecr:N \g_clist_map_inline_int
+ }
+\cs_generate_variant:Nn \clist_map_inline:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_variable:NNn, \clist_map_variable:cNn}
+% \UnitTested
+% This is just a dedicated version of the inline mapping.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_map_variable:NNn #1#2#3
+ {
+ \clist_map_inline:Nn #1
+ {
+ \tl_set:Nn #2 {##1}
+ #3
+ }
+ }
+\cs_new_protected:Npn \clist_map_variable:nNn #1#2#3
+ {
+ \clist_map_inline:nn {#1}
+ {
+ \tl_set:Nn #2 {##1}
+ #3
+ }
+ }
+\cs_generate_variant:Nn \clist_map_variable:NNn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_break:}
+% \UnitTested
+% \begin{macro}{\clist_map_break:n}
+% Both are simple renaming.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_map_break: \use_none_delimit_by_q_recursion_stop:w
+\cs_new_eq:NN \clist_map_break:n \use_i_delimit_by_q_recursion_stop:nw
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \section{Viewing comma lists}
+%
+% \begin{macro}{\clist_show:N, \clist_show:c}
+% \UnitTested
+% \begin{macro}[aux]{\clist_show_aux:n}
+% \begin{macro}[aux]{\clist_show_aux:w}
+% The aim of the mapping here is to create a token list containing the
+% formatted comma list. The very first item needs the new line and \verb*|> |
+% removing, which is achieved using a \texttt{w}-type auxiliary. To avoid
+% a low-level \TeX{} error if there is an empty comma list, a simple test is
+% used to keep the output \enquote{clean}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \clist_show:N #1
+ {
+ \clist_if_empty:NTF #1
+ {
+ \iow_term:x { Comma~list~\token_to_str:N #1 \c_space_tl is~empty }
+ \tl_show:n { }
+ }
+ {
+ \iow_term:x
+ {
+ Comma~list~\token_to_str:N #1 \c_space_tl
+ contains~the~items~(without~outer~braces):
+ }
+ \tl_set:Nx \l_clist_show_tl
+ { \clist_map_function:NN #1 \clist_show_aux:n }
+ \etex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \clist_show_aux:w \l_clist_show_tl }
+ }
+ }
+\cs_new:Npn \clist_show_aux:n #1
+ {
+ \iow_newline: > \c_space_tl \c_space_tl
+ \iow_char:N \{ \exp_not:n {#1} \iow_char:N \}
+ }
+\cs_new:Npn \clist_show_aux:w #1 > ~ { }
+\cs_generate_variant:Nn \clist_show:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Experimental functions}
+%
+% \begin{macro}{\clist_length:N, \clist_length:c}
+% \begin{macro}{\clist_length:n}
+% \begin{macro}[aux]{\clist_length_aux:n}
+% Counting the items in a comma list is done using the same approach as for
+% other length functions: turn each entry into a \texttt{+1} then use
+% integer evaluation to actually do the mathematics.
+% \begin{macrocode}
+\cs_new:Npn \clist_length:N #1
+ {
+ \int_eval:n
+ {
+ 0
+ \clist_map_function:NN #1 \clist_length_aux:n
+ }
+ }
+\cs_new:Npn \clist_length:n #1
+ {
+ \int_eval:n
+ {
+ 0
+ \clist_map_function:nN {#1} \clist_length_aux:n
+ }
+ }
+\cs_new:Npn \clist_length_aux:n #1 { +1 }
+\cs_generate_variant:Nn \clist_length:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_item:Nn, \clist_item:cn}
+% \begin{macro}{\clist_item:nn}
+% \begin{macro}[aux]{\clist_item_aux: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 \cs{quark_if_recursion_stop:n} will be true,
+% terminating the loop and returning nothing at all.
+% \begin{macrocode}
+\cs_set_nopar:Npn \clist_item:Nn #1#2
+ { \exp_args:No \clist_item:nn #1 {#2} }
+\cs_set:Npn \clist_item:nn #1#2
+ {
+ \int_compare:nNnTF {#2} < \c_zero
+ {
+ \exp_args:Nf \clist_item_aux:nw
+ { \int_eval:n { \clist_length:n {#1} + #2 } }
+ #1 , \q_recursion_tail \q_recursion_stop
+ }
+ { \clist_item_aux:nw {#2} #1 , \q_recursion_tail \q_recursion_stop }
+ }
+\cs_set:Npn \clist_item_aux:nw #1#2 , #3
+ {
+ \int_compare:nNnTF {#1} = \c_zero
+ { \use_i_delimit_by_q_recursion_stop:nw {#2} }
+ {
+ \quark_if_recursion_tail_stop:n {#3}
+ \exp_args:Nf \clist_item_aux:nw
+ { \int_eval:n { #1 - 1 } }
+ #3
+ }
+ }
+\cs_generate_variant:Nn \clist_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc
+% }
+% \begin{macro}
+% {
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% Setting a comma list from a comma-separated list is done using a simple
+% mapping. We wrap each item with braces, \cs{exp_not:n}, and a comma. The
+% first comma must be removed, except in the case of an empty comma-list.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_set_from_seq:NN #1#2
+ {
+ \seq_if_empty:NTF #2
+ { \clist_clear:N #1 }
+ {
+ \seq_push_item_def:n { , \exp_not:n {{##1}} }
+ \tl_set:Nx #1
+ { \exp_after:wN \use_none:n \tex_romannumeral:D -`\0 #2 }
+ \seq_pop_item_def:
+ }
+ }
+\cs_new_protected:Npn \clist_gset_from_seq:NN #1#2
+ {
+ \seq_if_empty:NTF #2
+ { \clist_gclear:N #1 }
+ {
+ \seq_push_item_def:n { , \exp_not:n {##1} }
+ \tl_gset:Nx #1
+ { \exp_after:wN \use_none:n \tex_romannumeral:D -`\0 #2 }
+ \seq_pop_item_def:
+ }
+ }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { c , cc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Deprecated interfaces}
+%
+% Deprecated on 2011-05-27, for removal by 2011-08-31.
+%
+% \begin{macro}{\clist_top:NN, \clist_top:cN}
+% These are old stack functions.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_top:NN \clist_get:NN
+\cs_new_eq:NN \clist_top:cN \clist_get:cN
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_remove_element:Nn}
+% \begin{macro}{\clist_gremove_element:Nn}
+% An older name for \cs{clist_remove_all:Nn}.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_remove_element:Nn \clist_remove_all:Nn
+\cs_new_eq:NN \clist_gremove_element:Nn \clist_gremove_all:Nn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_display:N, \clist_display:c}
+% An older name for \cs{clist_show:N}.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_display:N \clist_show:N
+\cs_new_eq:NN \clist_display:c \clist_show:c
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex \ No newline at end of file