% \iffalse %% File: l3clist.dtx Copyright (C) 2005-2010 Frank Mittelbach, 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 LaTeX Project Team. %% %% ----------------------------------------------------------------------- % %<*driver|package> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: l3clist.dtx 2071 2010-10-09 09:48:48Z joseph $ {L3 Experimental comma separated lists} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{\filename.\filenameext} \end{document} % % \fi % % % % \title{The \textsf{l3clist} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Comma separated lists} % \author{Frank Mittelbach} % \date{\filedate} % \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{Functions for creating/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 entries. %\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_clear:N | % \clist_clear:c | %} % \begin{syntax} % \cs{clist_clear:N} \meta{comma list} % \end{syntax} % Clears all entries 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 | % \clist_gclear_new:N | % \clist_gclear_new:c } % \begin{syntax} % "\clist_clear_new:N" % \end{syntax} % These functions locally or globally clear if it exists or % otherwise allocates it. % \end{function} % % \section{Putting data in} % %\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 | %} % \begin{syntax} % \cs{clist_put_left:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Adds \meta{entry} onto 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 | %} % \begin{syntax} % \cs{clist_gput_left:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Adds \meta{entry} onto 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 | %} % \begin{syntax} % \cs{clist_put_right:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Adds \meta{entry} onto 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 | %} % \begin{syntax} % \cs{clist_gput_right:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Adds \meta{entry} onto the right of the \meta{comma list}. The % assignment is global. %\end{function} % % \section{Getting data out} % %\begin{function}{ % \clist_use:N / (EXP) | % \clist_use:c / (EXP) | %} % \begin{syntax} % \cs{clist_use:N} \meta{comma list} % \end{syntax} % Recovers the content of a \meta{comma list} and places it % directly in the input stream. An error will be raised if the variable % does not exist or if it is invalid. This function is intended mainly % for use when a \meta{comma list} is being saved to an auxiliary file. %\end{function} % % \begin{function}{ \clist_show:N | % \clist_show:c } % \begin{syntax} % "\clist_show:N" % \end{syntax} % Function that pauses the compilation and displays in the terminal % output and in the log file. (Usually used for diagnostic purposes.) % \end{function} % %\begin{function}{ % \clist_display:N | % \clist_display:c | %} % \begin{syntax} % \cs{clist_display:N} \meta{comma list} % \end{syntax} % Displays the value of the \meta{comma list} on the terminal. %\end{function} % % \begin{function}{ \clist_get:NN | % \clist_get:cN } % \begin{syntax} % "\clist_get:NN" % \end{syntax} % Functions that locally assign the left-most item of to the % token list variable . Item is not removed from ! If you % need a global return value you need to code something like this: % \begin{quote} % "\clist_get:NN" "\l_tmpa_tl" \\ % "\tl_gset_eq:NN" "\l_tmpa_tl" % \end{quote} % But if this kind of construction is used often enough a separate % function should be provided. % \end{function} % % % \section{Mapping functions} % % % We provide three types of mapping functions, each with their own % strengths. The "\clist_map_function:NN" is expandable whereas % "\clist_map_inline:Nn" type uses "##1" as a placeholder for the % current item in \m{clist}. Finally we have the % "\clist_map_variable:NNn" type which uses a user-defined variable as % placeholder. Both the |_inline| and |_variable| versions are nestable. % %\begin{function}{ % \clist_map_function:NN / (EXP) | % \clist_map_function:Nc / (EXP) | % \clist_map_function:cN / (EXP) | % \clist_map_function:cc / (EXP) | % \clist_map_function:nN / (EXP) | % \clist_map_function:nc / (EXP) | %} % \begin{syntax} % \cs{clist_map_function:NN} \meta{comma list} \meta{function} % \end{syntax} % Applies \meta{function} to every \meta{entry} stored in the % \meta{comma list}. The \meta{function} will receive one argument for % each iteration. The \meta{entries} in the \meta{comma list} are % supplied to the \meta{function} reading from the left to the right. % These function may be nested. %\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{entry} stored % within the \meta{comma list}. The \meta{inline function} should % consist of code which will receive the \meta{entry} as "#1". % One inline mapping can be nested inside another. The \meta{entries} % in the \meta{comma list} are supplied to the \meta{function} reading % from the left to the right. %\end{function} % % \begin{function}{\clist_map_variable:NNn | % \clist_map_variable:cNn | % \clist_map_variable:nNn | % } % \begin{syntax} % "\clist_map_variable:NNn" \Arg{action} % \end{syntax} % Assigns to each element in and then executes % which should contain . As the % operation performs an assignment, it is not expandable. % \begin{texnote} % These functions resemble the \LaTeXe{} function \tn{@for} but does % not borrow the somewhat strange syntax. % \end{texnote} % \end{function} % %\begin{function}{ \clist_map_break: / (EXP) } % \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 low % level \TeX\ errors. %\end{function} % % \section{Predicates and conditionals} % %\begin{function}{ % \clist_if_empty_p:N / (EXP) | % \clist_if_empty_p:c / (EXP) | % \clist_if_empty:N / (EXP) (TF) | % \clist_if_empty:c / (EXP) (TF) | %} % \begin{syntax} % \cs{clist_if_empty_p:N} \meta{clist} % \cs{clist_if_empty:NTF} \meta{clist} \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}{ % \clist_if_eq_p:NN / (EXP) | % \clist_if_eq_p:Nc / (EXP) | % \clist_if_eq_p:cN / (EXP) | % \clist_if_eq_p:cc / (EXP) | % \clist_if_eq:NN / (EXP) (TF) | % \clist_if_eq:Nc / (EXP) (TF) | % \clist_if_eq:cN / (EXP) (TF) | % \clist_if_eq:cc / (EXP) (TF) | %} % \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}{ % \clist_if_in:Nn / (TF) | % \clist_if_in:NV / (TF) | % \clist_if_in:No / (TF) | % \clist_if_in:cn / (TF) | % \clist_if_in:cV / (TF) | % \clist_if_in:co / (TF) | %} % \begin{syntax} % \cs{clist_if_in:NnTF} \meta{clist} \Arg{entry} \Arg{true code} % ~~\Arg{false code} % \end{syntax} % Tests if the \meta{entry} is present in the \meta{comma list} as % a discrete entry. The \meta{entry} cannot contain the tokens "{", "}" % or "#" (assuming the usual \TeX\ category codes apply). Either the % \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. %\end{function} % % \section{Higher level functions} % %\begin{function}{ % \clist_concat:NNN | % \clist_concat:ccc | %} % \begin{syntax} % \cs{clist_concat:NNN} \meta{clist1} \meta{clist2} \meta{clist3} % \end{syntax} % Concatenates the content of \meta{comma list2} and \meta{comma list3} % together and saves the result in \meta{comma list1}. % \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{clist1} \meta{clist2} \meta{clist3} % \end{syntax} % Concatenates the content of \meta{comma list2} and \meta{comma list3} % together and saves the result in \meta{comma list1}. % \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} % %\begin{function}{ \clist_remove_duplicates:N } % \begin{syntax} % \cs{clist_remove_duplicates:N} \meta{comma list} % \end{syntax} % Removes duplicate entries from the \meta{comma list}, leaving % left most entry in the \meta{comma list}. The removal is local % to the current \TeX\ group. %\end{function} % %\begin{function}{ \clist_gremove_duplicates:N } % \begin{syntax} % \cs{clist_gremove_duplicates:N} \meta{comma list} % \end{syntax} % Removes duplicate entries from the \meta{comma list}, leaving % left most entry in the \meta{comma list}. The removal is applied % globally. %\end{function} % %\begin{function}{ \clist_remove_element:Nn } % \begin{syntax} % \cs{clist_remove_element:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Removes each occurrence of \meta{entry} from the \meta{comma list}, % where \meta{entry} cannot contain the tokens "{", "}" or "#" % (assuming normal \TeX\ category codes). The removal is local % to the current \TeX\ group. %\end{function} % %\begin{function}{ \clist_gremove_element:Nn } % \begin{syntax} % \cs{clist_gremove_element:Nn} \meta{comma list} \Arg{entry} % \end{syntax} % Removes each occurrence of \meta{entry} from the \meta{comma list}, % where \meta{entry} cannot contain the tokens "{", "}" or "#" % (assuming normal \TeX\ category codes). The removal applied % globally. %\end{function} % % \section{Functions for `comma-list stacks'} % % Special comma-lists in \LaTeX3 are `stacks' with their usual operations % of `push', `pop', and `top'. They are internally implemented as % comma-lists and share some of the functions (like "\clist_new:N" etc.) % % % \begin{function}{% % \clist_push:Nn | % \clist_push:NV | % \clist_push:No | % \clist_push:cn | % \clist_gpush:Nn | % \clist_gpush:NV | % \clist_gpush:No | % \clist_gpush:cn | % } % \begin{syntax} % "\clist_push:Nn" \Arg{token list} % \end{syntax} % Locally or globally pushes as a single item onto the % . might get expanded before the operation. % \end{function} % % \begin{function}{% % \clist_pop:NN | % \clist_pop:cN | % \clist_gpop:NN | % \clist_gpop:cN | % } % \begin{syntax} % "\clist_pop:NN" % \end{syntax} % Functions that assign the top item of to the token % list variable and removes it from ! % \end{function} % % \begin{function}{% % \clist_top:NN | % \clist_top:cN | % } % \begin{syntax} % "\clist_top:NN" % \end{syntax} % Functions that locally assign the top item of to the token % list variable . Item is not removed from ! % \end{function} % % \section{Internal functions} % % \begin{function}{\clist_if_empty_err:N} % \begin{syntax} % "\clist_if_empty_err:N" % \end{syntax} % Signals an \LaTeX3 error if is empty. % \end{function} % % \begin{function}{\clist_pop_aux:nnNN} % \begin{syntax} % "\clist_pop_aux:nnNN" % \end{syntax} % Function that assigns the left-most item of to using % and assigns the tail to using . This % function could be used to implement a global return function. % \end{function} % % \begin{function}{% % \clist_get_aux:w | % \clist_pop_aux:w | % \clist_pop_auxi:w | % \clist_put_aux:NNnnNn | % } % \begin{syntax}\end{syntax} % Functions used to implement put and get operations. They are not for % meant for direct use. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3clist} implementation} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \package_check_loaded_expl: % %<*initex|package> % \end{macrocode} % % \subsection{Allocation and initialisation} % % \begin{macro}{\clist_new:N} % \begin{macro}{\clist_new:c} % Comma-Lists are implemented using token lists. % \begin{macrocode} \cs_new_eq:NN \clist_new:N \tl_new:N \cs_generate_variant:Nn \clist_new:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\clist_clear:N} % \begin{macro}{\clist_clear:c} % \begin{macro}{\clist_gclear:N} % \begin{macro}{\clist_gclear:c} % Clearing a comma-list is the same as clearing a token list. % \begin{macrocode} \cs_new_eq:NN \clist_clear:N \tl_clear:N \cs_generate_variant:Nn \clist_clear:N {c} \cs_new_eq:NN \clist_gclear:N \tl_gclear:N \cs_generate_variant:Nn \clist_gclear:N {c} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_clear_new:N} % \begin{macro}{\clist_clear_new:c} % \begin{macro}{\clist_gclear_new:N} % \begin{macro}{\clist_gclear_new:c} % Clearing a comma-list is the same as clearing a token list. % \begin{macrocode} \cs_new_eq:NN \clist_clear_new:N \tl_clear_new:N \cs_generate_variant:Nn \clist_clear_new:N {c} \cs_new_eq:NN \clist_gclear_new:N \tl_gclear_new:N \cs_generate_variant:Nn \clist_gclear_new:N {c} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_set_eq:NN} % \begin{macro}{\clist_set_eq:cN} % \begin{macro}{\clist_set_eq:Nc} % \begin{macro}{\clist_set_eq:cc} % We can set one \meta{clist} equal to another. % \begin{macrocode} \cs_new_eq:NN \clist_set_eq:NN \tl_set_eq:NN \cs_new_eq:NN \clist_set_eq:cN \tl_set_eq:cN \cs_new_eq:NN \clist_set_eq:Nc \tl_set_eq:Nc \cs_new_eq:NN \clist_set_eq:cc \tl_set_eq:cc % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_gset_eq:NN} % \begin{macro}{\clist_gset_eq:cN} % \begin{macro}{\clist_gset_eq:Nc} % \begin{macro}{\clist_gset_eq:cc} % An of course globally which seems to be needed far more often. % \begin{macrocode} \cs_new_eq:NN \clist_gset_eq:NN \tl_gset_eq:NN \cs_new_eq:NN \clist_gset_eq:cN \tl_gset_eq:cN \cs_new_eq:NN \clist_gset_eq:Nc \tl_gset_eq:Nc \cs_new_eq:NN \clist_gset_eq:cc \tl_gset_eq:cc % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Predicates and conditionals} % % \begin{macro}{\clist_if_empty_p:N,\clist_if_empty_p:c} % \begin{macro}[TF]{\clist_if_empty:N,\clist_if_empty:c} % \begin{macrocode} \prg_new_eq_conditional:NNn \clist_if_empty:N \tl_if_empty:N {p,TF,T,F} \prg_new_eq_conditional:NNn \clist_if_empty:c \tl_if_empty:c {p,TF,T,F} % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_if_empty_err:N} % Signals an error if the comma-list is empty. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_if_empty_err:N #1 { \if_meaning:w #1 \c_empty_tl \tl_clear:N \l_kernel_testa_tl % catch prefixes \msg_kernel_bug:x {Empty~comma-list~`\token_to_str:N #1'} \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_if_eq_p:NN,\clist_if_eq_p:Nc, % \clist_if_eq_p:cN,\clist_if_eq_p:cc} % \begin{macro}[TF]{\clist_if_eq:NN,\clist_if_eq:cN, % \clist_if_eq:Nc,\clist_if_eq:cc} % Returns |\c_true| iff the two comma-lists are equal. % \begin{macrocode} \prg_new_eq_conditional:NNn \clist_if_eq:NN \tl_if_eq:NN {p,TF,T,F} \prg_new_eq_conditional:NNn \clist_if_eq:cN \tl_if_eq:cN {p,TF,T,F} \prg_new_eq_conditional:NNn \clist_if_eq:Nc \tl_if_eq:Nc {p,TF,T,F} \prg_new_eq_conditional:NNn \clist_if_eq:cc \tl_if_eq:cc {p,TF,T,F} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\clist_if_in:Nn} % \begin{macro}[TF]{\clist_if_in:NV,\clist_if_in:No,\clist_if_in:cn, % \clist_if_in:cV,\clist_if_in:co} % |\clist_if_in:NnTF| \m{clist}\m{item} \m{true~case} \m{false~case} % will check whether \m{item} is in \m{clist} and then either execute % the \m{true~case} or the \m{false~case}. \m{true~case} and % \m{false~case} may contain incomplete |\if_charcode:w| % statements. % \begin{macrocode} \prg_new_protected_conditional:Nnn \clist_if_in:Nn {TF,T,F} { \cs_set: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 } % \end{macrocode} % \begin{macrocode} \cs_generate_variant:Nn \clist_if_in:NnTF {NV,No,cn,cV,co} \cs_generate_variant:Nn \clist_if_in:NnT {NV,No,cn,cV,co} \cs_generate_variant:Nn \clist_if_in:NnF {NV,No,cn,cV,co} % \end{macrocode} % \end{macro} % \end{macro} % % % \subsection{Retrieving data} % % \begin{macro}{\clist_use:N} % \begin{macro}{\clist_use:c} % Using a \meta{clist} is just executing it but % if \m{clist} equals |\scan_stop:| it is probably stemming % from a |\cs:w ... \cs_end:| that was created by mistake somewhere. % \begin{macrocode} \cs_new_nopar:Npn \clist_use:N #1 { \if_meaning:w #1 \scan_stop: \msg_kernel_bug:x { Comma~list~ `\token_to_str:N #1'~ has~ an~ erroneous~ structure!} \else: \exp_after:wN #1 \fi: } \cs_generate_variant:Nn \clist_use:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\clist_get:NN} % \begin{macro}{\clist_get:cN} % \begin{macro}{\clist_get_aux:w} % |\clist_get:NN |\meta{comma-list}\meta{cmd} defines \meta{cmd} to be the % left-most element of \meta{comma-list}. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_get:NN #1 { \clist_if_empty_err:N #1 \exp_after:wN \clist_get_aux:w #1,\q_stop } % \end{macrocode} % \begin{macrocode} \cs_new_protected:Npn \clist_get_aux:w #1,#2\q_stop #3 { \tl_set:Nn #3{#1} } % \end{macrocode} % \begin{macrocode} \cs_generate_variant:Nn \clist_get:NN {cN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_pop_aux:nnNN} % \begin{macro}{\clist_pop_aux:w} % \begin{macro}{\clist_pop_auxi:w} % |\clist_pop_aux:nnNN| \meta{def$\sb1$} \meta{def$\sb2$} \meta{comma-list} % \meta{cmd} assigns the left-most element of \meta{comma-list} to % \meta{cmd} using \meta{def$\sb2$}, and assigns the tail of % \meta{comma-list} to \meta{comma-list} using \meta{def$\sb1$}. % \begin{macrocode} \cs_new_protected:Npn \clist_pop_aux:nnNN #1#2#3 { \clist_if_empty_err:N #3 \exp_after:wN \clist_pop_aux:w #3,\q_nil\q_stop #1#2#3 } % \end{macrocode} % After the assignmnets below, if there was only one element in the original % clist, it now contains only |\q_nil|. % \begin{macrocode} \cs_new_protected:Npn \clist_pop_aux:w #1,#2\q_stop #3#4#5#6 { #4 #6 {#1} #3 #5 {#2} \quark_if_nil:NTF #5 { #3 #5 {} }{ \clist_pop_auxi:w #2 #3#5 } } % \end{macrocode} % \begin{macrocode} \cs_new:Npn \clist_pop_auxi:w #1,\q_nil #2#3 { #2#3{#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_show:N} % \begin{macro}{\clist_show:c} % \begin{macrocode} \cs_new_eq:NN \clist_show:N \tl_show:N \cs_new_eq:NN \clist_show:c \tl_show:c % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\clist_display:N} % \begin{macro}{\clist_display:c} % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_display:N #1 { \iow_term:x { Comma-list~\token_to_str:N #1~contains~ the~elements~(without~outer~braces): } \toks_clear:N \l_tmpa_toks \clist_map_inline:Nn #1 { \toks_if_empty:NF \l_tmpa_toks { \toks_put_right:Nx \l_tmpa_toks {^^J>~} } \toks_put_right:Nx \l_tmpa_toks { \c_space_tl \iow_char:N \{ \exp_not:n {##1} \iow_char:N \} } } \toks_show:N \l_tmpa_toks } \cs_generate_variant:Nn \clist_display:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Storing data} % % \begin{macro}{\clist_put_aux:NNnnNn} % The generic put function. % When adding we have to distinguish between an empty \meta{clist} % and one that contains at least one item (otherwise we accumulate % commas). % % MH says: Perhaps we should make sure that empty arguments don't get % on the stack as that is probably a mistake. That's what I've % implemented here. Since |\tl_if_empty:nF| is expandable prefixes % are still allowed. % \begin{macrocode} \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} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_put_left:Nn} % \begin{macro}{\clist_put_left:NV} % \begin{macro}{\clist_put_left:No} % \begin{macro}{\clist_put_left:Nx} % \begin{macro}{\clist_put_left:cn} % \begin{macro}{\clist_put_left:cV} % \begin{macro}{\clist_put_left:co} % The operations for adding to the left. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_put_left:Nn { \clist_put_aux:NNnnNn \tl_set:Nn \tl_put_left:Nn {} , } \cs_generate_variant:Nn \clist_put_left:Nn {NV,No,Nx,cn,cV,co} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_gput_left:Nn} % \begin{macro}{\clist_gput_left:NV} % \begin{macro}{\clist_gput_left:No} % \begin{macro}{\clist_gput_left:Nx} % \begin{macro}{\clist_gput_left:cn} % \begin{macro}{\clist_gput_left:cV} % \begin{macro}{\clist_gput_left:co} % Global versions. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_gput_left:Nn { \clist_put_aux:NNnnNn \tl_gset:Nn \tl_gput_left:Nn {} , } \cs_generate_variant:Nn \clist_gput_left:Nn {NV,No,Nx,cn,cV,co} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_put_right:Nn} % \begin{macro}{\clist_put_right:NV} % \begin{macro}{\clist_put_right:No} % \begin{macro}{\clist_put_right:Nx} % \begin{macro}{\clist_put_right:cn} % \begin{macro}{\clist_put_right:cV} % \begin{macro}{\clist_put_right:co} % Adding something to the right side is almost the same. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_put_right:Nn { \clist_put_aux:NNnnNn \tl_set:Nn \tl_put_right:Nn , {} } \cs_generate_variant:Nn \clist_put_right:Nn {NV,No,Nx,cn,cV,co} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_gput_right:Nn} % \begin{macro}{\clist_gput_right:NV} % \begin{macro}{\clist_gput_right:No} % \begin{macro}{\clist_gput_right:Nx} % \begin{macro}{\clist_gput_right:cn} % \begin{macro}{\clist_gput_right:cV} % \begin{macro}{\clist_gput_right:co} % And here the global variants. % \begin{macrocode} \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_gput_right:Nn {NV,No,Nx,cn,cV,co} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \subsection{Mapping} % %\begin{macro}{\clist_map_function:NN} %\begin{macro}{\clist_map_function:Nc} %\begin{macro}{\clist_map_function:cN} %\begin{macro}{\clist_map_function:cc} %\begin{macro}{\clist_map_function:nN} %\begin{macro}{\clist_map_function:nc} %\begin{macro}{\clist_map_inline:Nn} %\begin{macro}{\clist_map_inline:cn} %\begin{macro}{\clist_map_inline:nn} %\begin{macro}{\clist_map_break:} % Using the above creating the comma mappings is easy.. % \begin{macrocode} \prg_new_map_functions:Nn , { clist } \cs_generate_variant:Nn \clist_map_function:NN { Nc } \cs_generate_variant:Nn \clist_map_function:NN { c } \cs_generate_variant:Nn \clist_map_function:NN { cc } \cs_generate_variant:Nn \clist_map_inline:Nn { c } \cs_generate_variant:Nn \clist_map_inline:Nn { nc } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % % \begin{macro}{\clist_map_variable:nNn} % \begin{macro}{\clist_map_variable:NNn} % \begin{macro}{\clist_map_variable:cNn} % |\clist_map_variable:NNn| \meta{comma-list} \meta{temp} \meta{action} assigns % \meta{temp} to each element and executes \meta{action}. % \begin{macrocode} \cs_new_protected:Npn \clist_map_variable:nNn #1#2#3 { \tl_if_empty:nF {#1} { \clist_map_variable_aux:Nnw #2 {#3} #1 , \q_recursion_tail , \q_recursion_stop } } % \end{macrocode} % Something for v/V % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_map_variable:NNn {\exp_args:No \clist_map_variable:nNn} \cs_generate_variant:Nn\clist_map_variable:NNn {cNn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux]{\clist_map_variable_aux:Nnw} % The general loop. Assign the temp variable |#1| to the current item % |#3| and then check if that's the stop marker. If it is, break the % loop. If not, execute the action |#2| and continue. % \begin{macrocode} \cs_new_protected:Npn \clist_map_variable_aux:Nnw #1#2#3,{ \cs_set_nopar:Npn #1{#3} \quark_if_recursion_tail_stop:N #1 #2 \clist_map_variable_aux:Nnw #1{#2} } % \end{macrocode} % \end{macro} % % \subsection{Higher level functions} % % \begin{macro}[aux]{\clist_concat_aux:NNNN} % \begin{macro}{\clist_concat:NNN,\clist_concat:ccc} % \begin{macro}{\clist_gconcat:NNN,\clist_gconcat:ccc} % |\clist_gconcat:NNN| \m{clist~1} \m{clist~2} \m{clist~3} will globally % assign \m{clist~1} the concatenation of \m{clist~2} and % \m{clist~3}. % % Again the situation is a bit more complicated because of the use % of commas between items, so if either list is empty we have to avoid % adding a comma. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_concat_aux:NNNN #1#2#3#4 { \tl_set:No \l_tmpa_tl {#3} \tl_set:No \l_tmpb_tl {#4} #1 #2 { \exp_not:V \l_tmpa_tl \tl_if_empty:NF \l_tmpa_tl { \tl_if_empty:NF \l_tmpb_tl , } \exp_not:V \l_tmpb_tl } } \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_generate_variant:Nn \clist_concat:NNN {ccc} \cs_generate_variant:Nn \clist_gconcat:NNN {ccc} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux]{\l_clist_remove_clist} % A common scratch space for the removal routines. % \begin{macrocode} \clist_new:N \l_clist_remove_clist % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\clist_remove_duplicates_aux:NN} % \begin{macro}[aux]{\clist_remove_duplicates_aux:n} % \begin{macro}{\clist_remove_duplicates:N} % \begin{macro}{\clist_gremove_duplicates:N} % Removing duplicate entries in a \meta{clist} is fairly straight % forward. We use a temporary variable and then go through the list % from left to right. For each element check if the element is % already present in the list. % \begin{macrocode} \cs_new_protected:Npn \clist_remove_duplicates_aux:NN #1#2 { \clist_clear:N \l_clist_remove_clist \clist_map_function:NN #2 \clist_remove_duplicates_aux:n #1 #2 \l_clist_remove_clist } \cs_new_protected:Npn \clist_remove_duplicates_aux:n #1 { \clist_if_in:NnF \l_clist_remove_clist {#1} { \clist_put_right:Nn \l_clist_remove_clist {#1} } } % \end{macrocode} % The high level functions are just for telling if it should be a % local or global setting. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_remove_duplicates:N { \clist_remove_duplicates_aux:NN \clist_set_eq:NN } \cs_new_protected_nopar:Npn \clist_gremove_duplicates:N { \clist_remove_duplicates_aux:NN \clist_gset_eq:NN } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % %\begin{macro}{\clist_remove_element:Nn} %\begin{macro}{\clist_gremove_element:Nn} %\begin{macro}[aux]{\clist_remove_element_aux:NNn} %\begin{macro}[aux]{\clist_remove_element_aux:n} % The same general idea is used for removing elements: the parent % functions just set things up for the internal ones. % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_remove_element:Nn { \clist_remove_element_aux:NNn \clist_set_eq:NN } \cs_new_protected_nopar:Npn \clist_gremove_element:Nn { \clist_remove_element_aux:NNn \clist_gset_eq:NN } \cs_new_protected:Npn \clist_remove_element_aux:NNn #1#2#3 { \clist_clear:N \l_clist_remove_clist \cs_set:Npn \clist_remove_element_aux:n ##1 { \str_if_eq:nnF {#3} {##1} { \clist_put_right:Nn \l_clist_remove_clist {##1} } } \clist_map_function:NN #2 \clist_remove_element_aux:n #1 #2 \l_clist_remove_clist } \cs_new:Npn \clist_remove_element_aux:n #1 { } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % % \subsection{Stack operations} % % We build stacks from comma-lists, but here we put the % specific functions together. % % % \begin{macro}{\clist_push:Nn} % \begin{macro}{\clist_push:No} % \begin{macro}{\clist_push:NV} % \begin{macro}{\clist_push:cn} % \begin{macro}{\clist_pop:NN} % \begin{macro}{\clist_pop:cN} % Since comma-lists can be used as stacks, we ought to have both % `push' and `pop'. In most cases they are nothing more then new % names for old functions. % \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:cn \clist_put_left:cn \cs_new_protected_nopar:Npn \clist_pop:NN {\clist_pop_aux:nnNN \tl_set:Nn \tl_set:Nn} \cs_generate_variant:Nn \clist_pop:NN {cN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\clist_gpush:Nn} % \begin{macro}{\clist_gpush:No} % \begin{macro}{\clist_gpush:NV} % \begin{macro}{\clist_gpush:cn} % \begin{macro}{\clist_gpop:NN} % \begin{macro}{\clist_gpop:cN} % I don't agree with Denys that one needs only local stacks, % actually I believe that one will probably need the functions % here more often. In case of |\clist_gpop:NN| the value is % nevertheless returned locally. % \begin{macrocode} \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_generate_variant:Nn \clist_gpush:Nn {cn} % \end{macrocode} % % \begin{macrocode} \cs_new_protected_nopar:Npn \clist_gpop:NN {\clist_pop_aux:nnNN \tl_gset:Nn \tl_set:Nn} \cs_generate_variant:Nn \clist_gpop:NN {cN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_top:NN} % \begin{macro}{\clist_top:cN} % Looking at the top element of the stack without removing it is % done with this operation. % \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} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % Show token usage: % \begin{macrocode} %<*showmemory> %\showMemUsage % % \end{macrocode} % % \end{implementation} % \PrintIndex % % % % \endinput