% \iffalse meta-comment % %% File: l3clist.dtx Copyright (C) 2004-2011 Frank Mittelbach, %% The LaTeX3 project %% (C) 2012 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% This file is part of the "l3kernel bundle" (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX3 Project. %% %% ----------------------------------------------------------------------- % %<*driver|package> \RequirePackage{l3names} \GetIdInfo$Id: l3clist.dtx 3460 2012-02-26 12:16:41Z will $ {L3 Experimental comma separated lists} % %<*driver> \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \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 list. The resulting ordered list can then % be mapped over using \cs{clist_map_function:NN}. Several items can % be added at once, and spaces are removed from both sides of each item % on input. Hence, % \begin{verbatim} % \clist_new:N \l_my_clist % \clist_put_left:Nn \l_my_clist { ~ a ~ , ~ {b} ~ } % \clist_put_right:Nn \l_my_clist { ~ { c ~ } , d } % \end{verbatim} % results in \cs{l_my_clist} containing |a,{b},{c~},d|. % Comma lists cannot contain empty items, thus % \begin{verbatim} % \clist_clear_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. To include an item % which contains a comma, or starts or ends with a space, % surround it with braces. % % \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, \clist_gclear:N, \clist_gclear:c} % \begin{syntax} % \cs{clist_clear:N} \meta{comma list} % \end{syntax} % Clears all items from the \meta{comma list}. % \end{function} % % \begin{function} % { % \clist_clear_new:N, \clist_clear_new:c, % \clist_gclear_new:N, \clist_gclear_new:c % } % \begin{syntax} % \cs{clist_clear_new:N} \meta{comma list} % \end{syntax} % Ensures that the \meta{comma list} exists globally by applying % \cs{clsit_new:N} if necessary, then applies \cs{clist_(g)clear:N} to leave % the list empty. % \end{function} % % \begin{function} % { % \clist_set_eq:NN, \clist_set_eq:cN, % \clist_set_eq:Nc, \clist_set_eq:cc, % \clist_gset_eq:NN, \clist_gset_eq:cN, % \clist_gset_eq:Nc, \clist_gset_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}. % \end{function} % % \begin{function} % { % \clist_concat:NNN, \clist_concat:ccc, % \clist_gconcat:NNN, \clist_gconcat: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. % \end{function} % % \section{Adding data to comma lists} % % \begin{function}[added = 2011-09-06] % { % \clist_set:Nn, \clist_set:NV, % \clist_set:No, \clist_set:Nx, % \clist_set:cn, \clist_set:cV, % \clist_set:co, \clist_set:cx, % \clist_gset:Nn, \clist_gset:NV, % \clist_gset:No, \clist_gset:Nx, % \clist_gset:cn, \clist_gset:cV, % \clist_gset:co, \clist_gset:cx % } % \begin{syntax} % \cs{clist_set:Nn} \meta{comma list} |{|\meta{item1},\ldots{},\meta{item$_n$}|}| % \end{syntax} % Sets \meta{comma list} to contain the \meta{items}, % removing any previous content from the variable. % Spaces are removed from both sides of each item. % \end{function} % % \begin{function}[updated = 2011-09-05] % { % \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, % \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_put_left:Nn} \meta{comma list} |{|\meta{item1},\ldots{},\meta{item$_n$}|}| % \end{syntax} % Appends the \meta{items} to the left of the \meta{comma list}. % Spaces are removed from both sides of each item. % \end{function} % % \begin{function}[updated = 2011-09-05] % { % \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, % \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_put_right:Nn} \meta{comma list} |{|\meta{item1},\ldots{},\meta{item$_n$}|}| % \end{syntax} % Appends the \meta{items} to the right of the \meta{comma list}. % Spaces are removed from both sides of each item. % \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, % including the commas, 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, % \clist_gremove_duplicates:N, \clist_gremove_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)}. % \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. % Furthermore, it will not work if any of the items in the % \meta{comma list} contains |{|, |}|, or |#| % (assuming the usual \TeX{} category codes apply). % \end{texnote} % \end{function} % % \begin{function}[updated = 2011-09-06] % { % \clist_remove_all:Nn, \clist_remove_all:cn, % \clist_gremove_all:Nn, \clist_gremove_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)}. % \begin{texnote} % The \meta{item} may not contain |{|, |}|, or |#| % (assuming the usual \TeX{} category codes apply). % \end{texnote} % \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). % \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{clist_1} \Arg{clist_2} % \cs{clist_if_eq:NNTF} \Arg{clist_1} \Arg{clist_2} \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. % \end{function} % % \begin{function}[updated = 2011-09-06, 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}. % In the case of an \texttt{n}-type \meta{comma list}, % spaces are stripped from each item, but braces are not removed. % Hence, % \begin{verbatim} % \clist_if_in:nnTF { a , {b}~ , {b} , c } { b } {true} {false} % \end{verbatim} % yields \texttt{false}. % \begin{texnote} % The \meta{item} may not contain |{|, |}|, or |#| % (assuming the usual \TeX{} category codes apply), % and should not contain |,| nor start or end with a space. % \end{texnote} % \end{function} % % \section{Mapping to comma lists} % % The functions described in this section apply a specified function % to each item of a comma list. % % When the comma list is given explicitly, as an \texttt{n}-type argument, % spaces are trimmed around each item. % If the result of trimming spaces is empty, the item is ignored. % Otherwise, if the item is surrounded by braces, one set is removed, % and the result is passed to the mapped function. Thus, if your % comma list that is being mapped is \verb*|{a , {b }, ,{}, {c},}| % then the arguments passed to the mapped function are % `\verb*|a|', `\verb*|{b }|', an empty argument, and `\verb*|c|'. % % When the comma list is given as an \texttt{N}-type argument, spaces % have already been trimmed on input, and items are simply stripped % of one set of braces if any. This case is more efficient than using % \texttt{n}-type comma lists. % % \begin{function}[rEXP] % {\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}[rEXP]{\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{prg_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}[rEXP]{\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 { } } % { % % 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{prg_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} % 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}[updated = 2011-09-06]{\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, % \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_push:Nn} \meta{comma list} \Arg{items} % \end{syntax} % Adds the \Arg{items} to the top of the \meta{comma list}. % Spaces are removed from both sides of each item. % \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} % % \begin{function}{\clist_show:n} % \begin{syntax} % \cs{clist_show:n} \Arg{tokens} % \end{syntax} % Displays the entries in the comma list in the terminal. % \end{function} % % \section{Scratch comma lists} % % \begin{variable}[added = 2011-09-06]{\l_tmpa_clist, \l_tmpb_clist} % Scratch comma lists for local assignment. These are never used by % the kernel code, and so are safe for use with any \LaTeX3-defined % function. However, they may be overwritten by other non-kernel % code and so should only be used for short-term storage. % \end{variable} % % \begin{variable}[added = 2011-09-06]{\g_tmpa_clist, \g_tmpb_clist} % Scratch comma lists for global assignment. These are never used by % the kernel code, and so are safe for use with any \LaTeX3-defined % function. However, they may be overwritten by other non-kernel % code and so should only be used for short-term storage. % \end{variable} % % \section{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}[added = 2011-06-25, updated = 2011-09-06, 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 duplicates, % \emph{i.e.}~every item in a \meta{comma list} is unique. % \end{function} % % \begin{function}[updated = 2012-01-08, 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. % \begin{texnote} % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{item} % will not expand further when appearing in an x-type % argument expansion. % \end{texnote} % \end{function} % % \begin{function}[updated = 2011-08-31] % { % \clist_set_from_seq:NN, \clist_set_from_seq:cN, % \clist_set_from_seq:Nc, \clist_set_from_seq:cc, % \clist_gset_from_seq:NN, \clist_gset_from_seq:cN, % \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc % } % \begin{syntax} % \cs{clist_set_from_seq:NN} \meta{comma list} \meta{sequence} % \end{syntax} % Sets the \meta{comma list} to be equal to the content of the % \meta{sequence}. % Items which contain either spaces or commas are surrounded by braces. % \end{function} % % \begin{function}[added = 2011-11-26] % { % \clist_const:Nn, \clist_const:Nx, % \clist_const:cn, \clist_const:cx % } % \begin{syntax} % \cs{clist_const:Nn} \meta{clist~var} \Arg{comma list} % \end{syntax} % Creates a new constant \meta{clist~var} or raises an error % if the name is already taken. The value of the % \meta{clist~var} will be set globally to the % \meta{comma list}. % \end{function} % % \begin{function}[EXP, pTF, added = 2011-12-07]{\clist_if_empty:n} % \begin{syntax} % \cs{clist_if_empty_p:n} \Arg{comma list} % \cs{clist_if_empty:nTF} \Arg{comma list} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{comma list} is empty (containing no items). % The rules for space trimming are as for other \texttt{n}-type % comma-list functions, hence the comma list |{~,~,,~}| (without % outer braces) is empty, while |{~,{},}| (without outer braces) % contains one element, which happens to be empty: the comma-list % is not empty. % \end{function} % % \section{Internal comma-list functions} % % \begin{function}[added = 2011-07-09, rEXP]{\clist_trim_spaces:n} % \begin{syntax} % \cs{clist_trim_spaces:n} \Arg{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. This is used by the functions which add data % into a comma list. % \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: % % \end{macrocode} % % \begin{variable}{\l_clist_internal_clist} % Scratch space for various internal uses. This comma list variable % cannot be declared as such because it comes before \cs{clist_new:N} % \begin{macrocode} \tl_new:N \l_clist_internal_clist % \end{macrocode} % \end{variable} % % \begin{macro}[aux]{\clist_tmp:w} % A temporary function for various purposes. % \begin{macrocode} \cs_new_protected:Npn \clist_tmp:w { } % \end{macrocode} % \end{macro} % % \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 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:Npn \clist_concat_aux:NNNN #1#2#3#4 { #1 #2 { \exp_not:o #3 \clist_if_empty:NF #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{Removing spaces around items} % % \begin{macro}[int,EXP]{\clist_trim_spaces_generic:nw} % \begin{macro}[aux]{\clist_trim_spaces_generic_aux:w} % \begin{macro}[aux]{\clist_trim_spaces_generic_aux_ii:nn} % Used as `\cs{clist_trim_spaces_generic:nw} \Arg{code} % \cs{q_mark} \meta{item} |,|' (including the comma). % This expands to the \meta{code}, followed by a brace group % containing the \meta{item}, with leading and trailing spaces removed. % The calling function is responsible for inserting \cs{q_mark} % in front of the \meta{item}, as well as testing for the end of the list. % See \cs{tl_trim_spaces:n} for a partial explanation of what % is happening here. We changed \cs{tl_trim_spaces_aux_iv:w} % into \cs{clist_trim_spaces_generic_aux:w} compared to % \cs{tl_trim_spaces:n}, and dropped a \cs{q_mark}, which is % already included in the argument |##2|. % \begin{macrocode} \cs_set:Npn \clist_tmp:w #1 { \cs_new:Npn \clist_trim_spaces_generic:nw ##1 ##2 , { \tl_trim_spaces_aux_i:w ##2 \q_nil \q_mark #1 { } \q_mark \tl_trim_spaces_aux_ii:w \tl_trim_spaces_aux_iii:w #1 \q_nil \clist_trim_spaces_generic_aux:w \q_stop {##1} } } \clist_tmp:w {~} \cs_new:Npn \clist_trim_spaces_generic_aux:w #1 \q_nil #2 \q_stop { \exp_args:No \clist_trim_spaces_generic_aux_ii:nn { \use_none:n #1 } } \cs_new:Npn \clist_trim_spaces_generic_aux_ii:nn #1 #2 { #2 {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[int,rEXP]{\clist_trim_spaces:n} % \begin{macro}[aux]{\clist_trim_spaces_aux:nn} % The first argument of \cs{clist_trim_spaces_aux:nn} is initially empty, % and later a comma, namely, as soon as we have added an item to the % resulting list. The auxiliary tests for the end of the list, % and also prevents empty arguments from finding their way into the % output. % \begin{macrocode} \cs_new:Npn \clist_trim_spaces:n #1 { \clist_trim_spaces_generic:nw { \clist_trim_spaces_aux:nn { } } \q_mark #1 , \q_recursion_tail, \q_recursion_stop } \cs_new:Npn \clist_trim_spaces_aux:nn #1 #2 { \quark_if_recursion_tail_stop:n {#2} \tl_if_empty:nTF {#2} { \clist_trim_spaces_generic:nw { \clist_trim_spaces_aux:nn {#1} } \q_mark } { #1 \exp_not:n {#2} \clist_trim_spaces_generic:nw { \clist_trim_spaces_aux:nn { , } } \q_mark } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Adding data to comma lists} % % \begin{macro} % { % \clist_set:Nn, \clist_set:NV, % \clist_set:No, \clist_set:Nx, % \clist_set:cn, \clist_set:cV, % \clist_set:co, \clist_set:cx % } % \begin{macro} % { % \clist_gset:Nn, \clist_gset:NV, % \clist_gset:No, \clist_gset:Nx, % \clist_gset:cn, \clist_gset:cV, % \clist_gset:co, \clist_gset:cx % } % \begin{macrocode} \cs_new_protected:Npn \clist_set:Nn #1#2 { \tl_set:Nx #1 { \clist_trim_spaces:n {#2} } } \cs_new_protected:Npn \clist_gset:Nn #1#2 { \tl_gset:Nx #1 { \clist_trim_spaces:n {#2} } } \cs_generate_variant:Nn \clist_set:Nn { NV , No , Nx , c , cV , co , cx } \cs_generate_variant:Nn \clist_gset:Nn { NV , No , Nx , c , cV , co , cx } % \end{macrocode} % \end{macro} % \end{macro} % % \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 % \begin{macro}[aux]{\clist_put_left_aux:NNNn} % 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_left_aux:NNNn \clist_concat:NNN \clist_set:Nn } \cs_new_protected_nopar:Npn \clist_gput_left:Nn { \clist_put_left_aux:NNNn \clist_gconcat:NNN \clist_set:Nn } \cs_new_protected:Npn \clist_put_left_aux:NNNn #1#2#3#4 { #2 \l_clist_internal_clist {#4} #1 #3 \l_clist_internal_clist #3 } \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} % \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 % } % \UnitTested % \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 % } % \UnitTested % \begin{macro}[aux]{\clist_put_right_aux:NNNn} % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_put_right:Nn { \clist_put_right_aux:NNNn \clist_concat:NNN \clist_set:Nn } \cs_new_protected_nopar:Npn \clist_gput_right:Nn { \clist_put_right_aux:NNNn \clist_gconcat:NNN \clist_gset:Nn } \cs_new_protected:Npn \clist_put_right_aux:NNNn #1#2#3#4 { #2 \l_clist_internal_clist {#4} #1 #3 #3 \l_clist_internal_clist } \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} % \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: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, \clist_pop_aux:NwNNN, \clist_pop_aux:wNN} % 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:Nf } \cs_new_protected_nopar:Npn \clist_gpop:NN { \clist_pop_aux:NNN \tl_gset:Nf } \cs_new_protected:Npn \clist_pop_aux:NNN #1#2#3 { \exp_after:wN \clist_pop_aux:wNNN #2 , \q_nil \q_stop #1#2#3 } \cs_new_protected:Npn \clist_pop_aux:wNNN #1 , #2 \q_stop #3#4#5 { \tl_set:Nn #5 {#1} \quark_if_nil:nTF {#2} { #3 #4 { } } { #3 #4 { \clist_pop_aux:w \exp_stop_f: #2 } } } \cs_new_protected:Npn \clist_pop_aux:w #1 , \q_nil {#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} % % \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_internal_remove_clist} % An internal comma list for the removal routines. % \begin{macrocode} \clist_new:N \l_clist_internal_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_internal_remove_clist \clist_map_inline:Nn #2 { \clist_if_in:NnF \l_clist_internal_remove_clist {##1} { \clist_put_right:Nn \l_clist_internal_remove_clist {##1} } } #1 #2 \l_clist_internal_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} % \begin{macro}[aux]{\clist_remove_all_aux:} % The method used here is very similar to \cs{tl_replace_all:Nnn}. % Build a function delimited by the \meta{item} that should be removed, % surrounded with commas, and call that function followed by % the expanded comma list, and another copy of the \meta{item}. % The loop is controlled by the argument grabbed by % \cs{clist_remove_all_aux:w}: when the item was found, % the \cs{q_mark} delimiter used is the one inserted by % \cs{clist_tmp:w}, and \cs{use_none_delimit_by_q_stop:w} % is deleted. At the end, the final \meta{item} is % grabbed, and the argument of \cs{clist_tmp:w} contains % \cs{q_mark}: in that case, \cs{clist_remove_all_aux:w} % removes the second \cs{q_mark} (inserted by \cs{clist_tmp:w}), % and lets \cs{use_none_delimit_by_q_stop:w} act. % % No brace is lost because items are always grabbed with a leading comma. % The result of the first assignment has an extra leading comma, % which we remove in a second assignment. % Two exceptions: if the clist lost all of its elements, the result % is empty, and we shouldn't remove anything; if the clist started up % empty, the first step happens to turn it into a single comma, and % the second step removes it. % \begin{macrocode} \cs_new_protected:Npn \clist_remove_all:Nn { \clist_remove_all_aux:NNn \tl_set:Nx } \cs_new_protected:Npn \clist_gremove_all:Nn { \clist_remove_all_aux:NNn \tl_gset:Nx } \cs_new_protected:Npn \clist_remove_all_aux:NNn #1#2#3 { \cs_set:Npn \clist_tmp:w ##1 , #3 , { ##1 , \q_mark , \use_none_delimit_by_q_stop:w , \clist_remove_all_aux: } #1 #2 { \exp_after:wN \clist_remove_all_aux: #2 , \q_mark , #3 , \q_stop } \clist_if_empty:NF #2 { #1 #2 { \exp_args:No \exp_not:o { \exp_after:wN \use_none:n #2 } } } } \cs_new:Npn \clist_remove_all_aux: { \exp_after:wN \clist_remove_all_aux:w \clist_tmp:w , } \cs_new:Npn \clist_remove_all_aux:w #1 , \q_mark , #2 , { \exp_not:n {#1} } \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} % \end{macro} % % \subsection{Comma list conditionals} % % \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, % \clist_if_in:nn, \clist_if_in:nV, \clist_if_in:no % } % \begin{macro}[aux]{\clist_if_in_return:nn} % \UnitTested % See description of the \cs{tl_if_in:Nn} function for details. % We simply surround the comma list, and the item, with commas. % \begin{macrocode} \prg_new_protected_conditional:Npnn \clist_if_in:Nn #1#2 { T , F , TF } { \exp_args:No \clist_if_in_return:nn #1 {#2} } \prg_new_protected_conditional:Npnn \clist_if_in:nn #1#2 { T , F , TF } { \clist_set:Nn \l_clist_internal_clist {#1} \exp_args:No \clist_if_in_return:nn \l_clist_internal_clist {#2} } \cs_new_protected:Npn \clist_if_in_return:nn #1#2 { \cs_set:Npn \clist_tmp:w ##1 ,#2, { } \tl_if_empty:oTF { \clist_tmp:w ,#1, {} {} ,#2, } { \prg_return_false: } { \prg_return_true: } } \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} % \end{macro} % % \subsection{Mapping to comma lists} % % \begin{macro}{\clist_map_function:NN, \clist_map_function:cN} % \UnitTested % \begin{macro}[aux]{\clist_map_function_aux:Nw} % If the variable is empty, the mapping is skipped (otherwise, % that comma-list would be seen as consisting of one empty item). % Then loop over the comma-list, grabbing one comma-delimited % item at a time. The end is marked by \cs{q_recursion_tail}. % The auxiliary function \cs{clist_map_function_aux:Nw} is used % directly in \cs{clist_map_inline:Nn}. Change with care. % \begin{macrocode} \cs_new: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 , \prg_break_point:n { } } } \cs_new:Npn \clist_map_function_aux:Nw #1#2 , { \quark_if_recursion_tail_break: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} % % \begin{macro}{\clist_map_function:nN} % \UnitTested % \begin{macro}[aux]{\clist_map_function_n_aux:Nn} % \begin{macro}{\clist_map_aux_unbrace:Nw} % The \texttt{n}-type mapping function is a bit more awkward, % since spaces mustbe trimmed from each item. % Space trimming is again based on \cs{clist_trim_spaces_generic:nw}. % The auxiliary \cs{clist_map_function_n_aux:Nn} receives % as arguments the function, and the result of removing leading % and trailing spaces from the item which lies until the next comma. % Empty items are ignored, then one level of braces is removed % by \cs{clist_map_aux_unbrace:Nw}. % \begin{macrocode} \cs_new:Npn \clist_map_function:nN #1#2 { \clist_trim_spaces_generic:nw { \clist_map_function_n_aux:Nn #2 } \q_mark #1, \q_recursion_tail, \prg_break_point:n { } } \cs_new:Npn \clist_map_function_n_aux:Nn #1 #2 { \quark_if_recursion_tail_break:n {#2} \tl_if_empty:nF {#2} { \clist_map_aux_unbrace:Nw #1 #2, } \clist_trim_spaces_generic:nw { \clist_map_function_n_aux:Nn #1 } \q_mark } \cs_new:Npn \clist_map_aux_unbrace:Nw #1 #2, { #1 {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \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. We use a different % function for each level of nesting. % % Since the mapping is non-expandable, we can perform % the space-trimming needed by the \texttt{n} version % simply by storing the comma-list in a variable. We % don't need a different comma-list for each nesting % level: the comma-list is expanded before the mapping % starts. % \begin{macrocode} \cs_new_protected:Npn \clist_map_inline:Nn #1#2 { \clist_if_empty:NF #1 { \int_gincr:N \g_prg_map_int \cs_gset:cpn { clist_map_ \int_use:N \g_prg_map_int :n } ##1 {#2} \exp_last_unbraced:Nco \clist_map_function_aux:Nw { clist_map_ \int_use:N \g_prg_map_int :n } #1 , \q_recursion_tail , \prg_break_point:n { \int_gdecr:N \g_prg_map_int } } } \cs_new_protected:Npn \clist_map_inline:nn #1 { \clist_set:Nn \l_clist_internal_clist {#1} \clist_map_inline:Nn \l_clist_internal_clist } \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 % \begin{macro}{\clist_map_variable:nNn} % \begin{macro}[aux]{\clist_map_variable_aux:Nnw} % As for other comma-list mappings, filter out the case of % an empty list. Same approach as \cs{clist_map_function:Nn}, % additionally we store each item in the given variable. % As for inline mappings, space trimming for the \texttt{n} % variant is done by storing the comma list in a variable. % \begin{macrocode} \cs_new_protected:Npn \clist_map_variable:NNn #1#2#3 { \clist_if_empty:NF #1 { \exp_args:Nno \use:nn { \clist_map_variable_aux:Nnw #2 {#3} } #1 , \q_recursion_tail , \q_recursion_stop \prg_break_point:n { } } } \cs_new_protected:Npn \clist_map_variable:nNn #1 { \clist_set:Nn \l_clist_internal_clist {#1} \clist_map_variable:NNn \l_clist_internal_clist } \cs_new_protected:Npn \clist_map_variable_aux:Nnw #1#2#3, { \tl_set:Nn #1 {#3} \quark_if_recursion_tail_stop:N #1 \use:n {#2} \clist_map_variable_aux:Nnw #1 {#2} } \cs_generate_variant:Nn \clist_map_variable:NNn { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_map_break:} % \begin{macro}{\clist_map_break:n} % The break statements are simply copies. % \begin{macrocode} \cs_new_eq:NN \clist_map_break: \prg_map_break: \cs_new_eq:NN \clist_map_break:n \prg_map_break:n % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Viewing comma lists} % % \begin{macro}{\clist_show:N, \clist_show:c} % \begin{macro}{\clist_show:n} % Apply the general \cs{msg_aux_show:Nnx}. In the case % of an \texttt{n}-type comma-list, first store it % in a scratch variable, then show that variable, % omitting its name from the $4$-th argument. % \begin{macrocode} \cs_new_protected:Npn \clist_show:N #1 { \msg_aux_show:Nnx #1 { clist } { \clist_map_function:NN #1 \msg_aux_show:n } } \cs_new_protected:Npn \clist_show:n #1 { \clist_set:Nn \l_clist_internal_clist {#1} \msg_aux_show:Nnx \l_clist_internal_clist { clist } { \clist_map_function:NN \l_clist_internal_clist \msg_aux_show:n } } \cs_generate_variant:Nn \clist_show:N { c } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Scratch comma lists} % % \begin{variable}{\l_tmpa_clist, \l_tmpb_clist} % \begin{variable}{\g_tmpa_clist, \g_tmpb_clist} % Temporary comma list variables. % \begin{macrocode} \clist_new:N \l_tmpa_clist \clist_new:N \l_tmpb_clist \clist_new:N \g_tmpa_clist \clist_new:N \g_tmpb_clist % \end{macrocode} % \end{variable} % \end{variable} % % \subsection{Experimental functions} % % \begin{macro}{\clist_length:N, \clist_length:c} % \begin{macro}{\clist_length:n} % \begin{macro}[aux]{\clist_length_aux:w} % 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. % In the case of an \texttt{n}-type comma-list, we could of course use % \cs{clist_map_function:nN}, but that is very slow, because it carefully % removes spaces. Instead, we loop manually, and skip blank items % (but not |{}|, hence the extra spaces). % \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_aux:n #1 { +1 } \cs_new:Npx \clist_length:n #1 { \exp_not:N \int_eval:n { 0 \exp_not:N \clist_length_n_aux:w \c_space_tl #1 \exp_not:n { , \q_recursion_tail , \q_recursion_stop } } } \cs_new:Npx \clist_length_n_aux:w #1 , { \exp_not:n { \exp_args:Nf \quark_if_recursion_tail_stop:n } {#1} \exp_not:N \tl_if_blank:nF {#1} { + \c_one } \exp_not:N \clist_length_n_aux:w \c_space_tl } \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}[aux]{\clist_item_aux:nnNn} % \begin{macro}[aux]{\clist_item_N_loop:nw} % To avoid needing to test the end of the list at each step, % we first compute the \meta{length} of the list. If the item number % is less than $-\meta{length}$ or more than $\meta{length}-1$, % the result is empty. If it is negative, but not less than $-\meta{length}$, % add the \meta{length} to the item number before performing the loop. % The loop itself is very simple, return the item if the counter % reached zero, otherwise, decrease the counter and repeat. % \begin{macrocode} \cs_new:Npn \clist_item:Nn #1#2 { \exp_args:Nfo \clist_item_aux:nnNn { \clist_length:N #1 } #1 \clist_item_N_loop:nw {#2} } \cs_new:Npn \clist_item_aux:nnNn #1#2#3#4 { \int_compare:nNnTF {#4} < \c_zero { \int_compare:nNnTF {#4} < { - #1 } { \use_none_delimit_by_q_stop:w } { \exp_args:Nf #3 { \int_eval:n { #4 + #1 } } } } { \int_compare:nNnTF {#4} < {#1} { #3 {#4} } { \use_none_delimit_by_q_stop:w } } #2, \q_stop } \cs_new:Npn \clist_item_N_loop:nw #1 #2, { \int_compare:nNnTF {#1} = \c_zero { \use_i_delimit_by_q_stop:nw { \exp_not:n {#2} } } { \exp_args:Nf \clist_item_N_loop:nw { \int_eval:n { #1 - 1 } } } } \cs_generate_variant:Nn \clist_item:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_item:nn} % \begin{macro}[aux]{ % \clist_item_n_aux:nw, % \clist_item_n_loop:nw, % \clist_item_n_end:n, % \clist_item_n_strip:w} % This starts in the same way as \cs{clist_item:Nn} by checking the length % of the comma list. The final item should be space-trimmed before being % brace-stripped, hence we insert a couple of odd-looking % \cs{prg_do_nothing:} to avoid losing braces. Blank items are ignored. % \begin{macrocode} \cs_new:Npn \clist_item:nn #1#2 { \exp_args:Nf \clist_item_aux:nnNn { \clist_length:n {#1} } {#1} \clist_item_n_aux:nw {#2} } \cs_new:Npn \clist_item_n_aux:nw #1 { \clist_item_n_loop:nw {#1} \prg_do_nothing: } \cs_new:Npn \clist_item_n_loop:nw #1 #2, { \exp_args:No \tl_if_blank:nTF {#2} { \clist_item_n_loop:nw {#1} \prg_do_nothing: } { \int_compare:nNnTF {#1} = \c_zero { \exp_args:No \clist_item_n_end:n {#2} } { \exp_args:Nf \clist_item_n_loop:nw { \int_eval:n { #1 - 1 } } \prg_do_nothing: } } } \cs_new:Npn \clist_item_n_end:n #1 #2 \q_stop { \exp_after:wN \exp_after:wN \exp_after:wN \clist_item_n_strip:w \tl_trim_spaces:n {#1} , } \cs_new:Npn \clist_item_n_strip:w #1 , { \exp_not:n {#1} } % \end{macrocode} % \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 % } % \UnitTested % \begin{macro} % { % \clist_gset_from_seq:NN, \clist_gset_from_seq:cN, % \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc % } % \UnitTested % \begin{macro}[aux]{\clist_set_from_seq_aux:NNNN} % \begin{macro}[aux]{\clist_wrap_item:n} % Setting a comma list from a comma-separated list is done using a simple % mapping. We wrap most items with \cs{exp_not:n}, and a comma. Items which % contain a comma or a space are surrounded by an extra set of braces. 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 { \clist_set_from_seq_aux:NNNN \clist_clear:N \tl_set:Nx } \cs_new_protected:Npn \clist_gset_from_seq:NN { \clist_set_from_seq_aux:NNNN \clist_gclear:N \tl_gset:Nx } \cs_new_protected:Npn \clist_set_from_seq_aux:NNNN #1#2#3#4 { \seq_if_empty:NTF #4 { #1 #3 } { #2 #3 { \exp_last_unbraced:Nf \use_none:n { \seq_map_function:NN #4 \clist_wrap_item:n } } } } \cs_new:Npn \clist_wrap_item:n #1 { , \tl_if_empty:oTF { \clist_set_from_seq_aux:w #1 ~ , #1 ~ } { \exp_not:n {#1} } { \exp_not:n { {#1} } } } \cs_new:Npn \clist_set_from_seq_aux:w #1 , #2 ~ { } \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} % \end{macro} % \end{macro} % % \begin{macro} % { % \clist_const:Nn, \clist_const:cn, % \clist_const:Nx, \clist_const:cx % } % Creating and initializing a constant comma list is done in a way % similar to \cs{clist_set:Nn} and \cs{clist_gset:Nn}, being careful % to strip spaces. % \begin{macrocode} \cs_new_protected:Npn \clist_const:Nn #1#2 { \tl_const:Nx #1 { \clist_trim_spaces:n {#2} } } \cs_generate_variant:Nn \clist_const:Nn { c , Nx , cx } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, pTF]{\clist_if_empty:n} % \begin{macro}[aux, EXP]{\clist_if_empty_n_aux:w} % \begin{macro}[aux, EXP]{\clist_if_empty_n_aux:wNw} % As usual, we insert a token (here |?|) before grabbing % any argument: this avoids losing braces. The argument % of \cs{tl_if_empty:oTF} is empty if |#1| is |?| followed % by blank spaces (besides, this particular variant of % the emptyness test is optimized). If the item of the % comma list is blank, grab the next one. As soon as one % item is non-blank, exit: the second auxiliary will grab % \cs{prg_return_false:} as |#2|, unless every item in % the comma list was blank and the loop actually got broken % by the trailing |\q_mark \prg_return_false:| item. % \begin{macrocode} \prg_new_conditional:Npnn \clist_if_empty:n #1 { p , T , F , TF } { \clist_if_empty_n_aux:w ? #1 , \q_mark \prg_return_false: , \q_mark \prg_return_true: \q_stop } \cs_new:Npn \clist_if_empty_n_aux:w #1 , { \tl_if_empty:oTF { \use_none:nn #1 ? } { \clist_if_empty_n_aux:w ? } { \clist_if_empty_n_aux:wNw } } \cs_new:Npn \clist_if_empty_n_aux:wNw #1 \q_mark #2#3 \q_stop {#2} % \end{macrocode} % \end{macro} % \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} % \UnitTested % These are old stack functions. % \begin{macrocode} %<*deprecated> \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,\clist_gremove_element:Nn} % \UnitTested % An older name for \cs{clist_remove_all:Nn}. % \begin{macrocode} %<*deprecated> \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} % % \begin{macro}{\clist_display:N, \clist_display:c} % An older name for \cs{clist_show:N}. % \begin{macrocode} %<*deprecated> \cs_new_eq:NN \clist_display:N \clist_show:N \cs_new_eq:NN \clist_display:c \clist_show:c % % \end{macrocode} % \end{macro} % % Deprecated on 2011-09-05, for removal by 2011-12-31. % % \begin{macro} % { % \clist_trim_spaces:N, \clist_trim_spaces:c, % \clist_gtrim_spaces:N, \clist_gtrim_spaces:c % } % Since clist items are now always stripped from their surrounding % spaces, it is redundant to provide these functions. % The \cs{clist_trim_spaces:n} function is now internal, % deprecated for use outside the kernel. % \begin{macrocode} %<*deprecated> \cs_new_protected:Npn \clist_trim_spaces:N #1 { \clist_set:No #1 {#1} } \cs_new_protected:Npn \clist_gtrim_spaces:N #1 { \clist_gset:No #1 {#1} } \cs_generate_variant:Nn \clist_trim_spaces:N { c } \cs_generate_variant:Nn \clist_gtrim_spaces:N { c } % % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex