% \iffalse %% File: l3clist.dtx Copyright (C) 2005-2006 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/cgi-bin/cvsweb.cgi/ %% %% 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 745 2008-08-03 18:50:46Z morten $ {L3 Experimental comma separated lists} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass{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 % % % % \section{Comma lists} % % \LaTeX3 implements a data type called `clist (comma-lists)'. These % are special % token lists that can be accessed via special function on the `left'. % Appending tokens is possible at both ends. Appended token lists can be % accessed only as a union. The token lists that form the individual % items of a comma-list might contain any tokens except for commas that % are used to structure comma-lists (braces are need if commas are % part of the value). It is also possible to map functions on such % comma-lists so that they are executed for every item of the comma-list. % % All functions that return items from a comma-list in some \m{tlp} assume % that the \m{tlp} is local. See remarks below if you need a global % returned value. % % The defined functions are not orthogonal in the sense that every % possible variation possible is actually available. If you need a new % variant use the expansion functions described in the package % \texttt{l3expan} to build it. % % Adding items to the left of a comma-list can currently be done with % either something like "\clist_put_left:Nn" or with a ``stack'' function % like "\clist_push:Nn" which has the same effect. Maybe one should % therefore remove the ``left'' functions totally. % % \subsection{Functions} % % \begin{function}{% % \clist_new:N | % \clist_new:c | % } % \begin{syntax} % "\clist_new:N" % \end{syntax} % Defines to be a variable of type clist. % \end{function} % % \begin{function}{% % \clist_clear:N | % \clist_clear:c | % \clist_gclear:N | % \clist_gclear:c | % } % \begin{syntax} % "\clist_clear:N" % \end{syntax} % These functions locally or globally clear . % \end{function} % % \begin{function}{% % \clist_put_left:Nn | % \clist_put_left:No | % \clist_put_left:Nx | % \clist_put_left:cn | % \clist_put_right:Nn | % \clist_put_right:No | % \clist_put_right:Nx | % } % \begin{syntax} % "\clist_put_left:Nn" % \end{syntax} % Locally appends as a single item to the left % or right of . might get expanded before % appending. % \end{function} % % \begin{function}{% % \clist_gput_left:Nn | % \clist_gput_right:Nn | % \clist_gput_right:No | % \clist_gput_right:cn | % \clist_gput_right:co | % \clist_gput_right:cc | % } % \begin{syntax} % "\clist_gput_left:Nn" % \end{syntax} % Globally appends as a single item to the left % or right of . % \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 pointer . 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_tlp" \\ % "\tlp_gset_eq:NN" "\l_tmpa_tlp" % \end{quote} % But if this kind of construction is used often enough a separate % function should be provided. % \end{function} % % \begin{function}{% % \clist_set_eq:NN % } % \begin{syntax} % "\clist_set_eq:NN" % \end{syntax} % Function that locally makes identical to . % \end{function} % % % \begin{function}{% % \clist_gset_eq:NN | % \clist_gset_eq:cN | % \clist_gset_eq:Nc | % \clist_gset_eq:cc % } % \begin{syntax} % "\clist_gset_eq:NN" % \end{syntax} % Function that globally makes identical to . % \end{function} % % % \begin{function}{% % \clist_concat:NNN | % \clist_gconcat:NNN | % \clist_gconcat:NNc | % \clist_gconcat:ccc | % } % \begin{syntax} % "\clist_gconcat:NNN" % \end{syntax} % Function that concatenates and and globally assigns the % result to . % \end{function} % % % \begin{function}{% % \clist_remove_duplicates:N | % \clist_gremove_duplicates:N | % } % \begin{syntax} % "\clist_gremove_duplicates:N" % \end{syntax} % Function that removes any duplicate entries in . % \end{function} % % \begin{function}{% % \clist_use:N | % \clist_use:c % } % \begin{syntax} % "\clist_use:N" % \end{syntax} % Function that inserts the into the processing stream. Mainly % useful if one knows what the contains, e.g., for displaying % the content of template parameters. % \end{function} % % % \subsection{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| % \clist_map_function:cN| % \clist_map_function:nN % } % \begin{syntax} % "\clist_map_function:NN" % \end{syntax} % This function applies (which must be a function with one % argument) to every item of . is not executed % within a sub-group so that side effects can be achieved locally. The % operation is expandable which means that it can be used within write % operations etc. % \end{function} % % \begin{function}{\clist_map_inline:Nn | % \clist_map_inline:cn | % \clist_map_inline:nn | % } % \begin{syntax} % "\clist_map_inline:Nn" "{" "}" % \end{syntax} % Applies (which should be the direct coding for a % function with one argument (i.e.\ use "##1" as the placeholder for % this argument)) to every item of . is not % executed within a sub-group so that side effects can be achieved locally. % The operation is not expandable which means that it can't be used % within write operations etc. These functions can be nested. % \end{function} % % % \begin{function}{\clist_map_variable:NNn | % \clist_map_variable:cNn | % \clist_map_variable:nNn | % } % \begin{syntax} % "\clist_map_variable:NNn" "{" "}" % \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:w | % } % \begin{syntax} % "\clist_map_break:w" % \end{syntax} % For breaking out of a loop. To be used inside "TF" type functions as % in the example below. % \begin{verbatim} % \def_new:Npn \test_function:n #1 { % \int_compare:nNnTF {#1}> 3 {\clist_map_break:w}{``#1''} % } % \clist_map_function:nN {1,2,3,4,5,6,7,8}\test_function:n % \end{verbatim} % This would return "``1''``2''``3''". % \end{function} % % % \subsection{Predicates and conditionals} % % \begin{function}{\clist_if_empty_p:N} % \begin{syntax} % "\clist_if_empty_p:N" % \end{syntax} % This predicate returns `true' if is `empty' i.e., doesn't % contain any tokens. % \end{function} % % \begin{function}{% % \clist_if_empty:NTF | % \clist_if_empty:cTF | % \clist_if_empty:NF | % \clist_if_empty:cF | % } % \begin{syntax} % "\clist_if_empty:NTF" "{" "}{" "}" % \end{syntax} % Set of conditionals that test whether or not a particular % is empty and if so executes either or . % \end{function} % % \begin{function}{% % \clist_if_eq:NNTF | % } % \begin{syntax} % "\clist_if_eq:NNTF" "{" % "}{" "}" % \end{syntax} % Check if and are equal and execute % either or accordingly. % \end{function} % % \begin{function}{% % \clist_if_in:NnTF | % \clist_if_in:NoTF | % \clist_if_in:cnTF | % \clist_if_in:coTF | % } % \begin{syntax} % "\clist_if_in:NnTF" "{" "}{" "}{" % "}" % \end{syntax} % Function that tests if is in . Depending on the result % either or is executed. % \end{function} % % \subsection{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} % % % % \begin{function}{% % \clist_map_function_aux:Nw | % \clist_map_inline_aux:Nw | % \clist_map_variable_aux:Nnw | % } % \begin{syntax}\end{syntax} % Internal helper functions for the mapping functions. % \end{function} % % % \begin{function}{% % \clist_concat_aux:NNNN | % \clist_remove_duplicates_aux:NN | % \clist_remove_duplicates_aux:n | % \l_clist_remove_duplicates_clist | % } % \begin{syntax}\end{syntax} % Functions that help concatenate s and remove duplicate % elements from a . % \end{function} % % % % \subsection{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:No | % \clist_push:cn | % \clist_gpush:Nn | % \clist_gpush:No | % \clist_gpush:cn | % } % \begin{syntax} % "\clist_push:Nn" "{" "}" % \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 pointer 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 pointer . Item is not removed from ! % \end{function} % % \StopEventually{} % % Use "clist" functions. % % % \subsection {The Implementation} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %\ProvidesExplPackage % {\filename}{\filedate}{\fileversion}{\filedescription} %<*package> \NeedsTeXFormat{LaTeX2e} %\RequirePackage{l3prg,l3quark} %\RequirePackage{l3chk} % %<*initex|package> % \end{macrocode} % % % \begin{macro}{\clist_new:N} % \begin{macro}{\clist_new:c} % Comma-Lists are implemented using token lists. % \begin{macrocode} \def_new:Npn \clist_new:N #1{\tlp_new:Nn #1{}} \def_new:Npn \clist_new:c {\exp_args:Nc \clist_new:N} % \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} \let_new:NN \clist_clear:N \tlp_clear:N \let_new:NN \clist_clear:c \tlp_clear:c \let_new:NN \clist_gclear:N \tlp_gclear:N \let_new:NN \clist_gclear:c \tlp_gclear:c % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_set_eq:NN} % We can set one \meta{clist} equal to another. % \begin{macrocode} \let_new:NN \clist_set_eq:NN \let:NN % \end{macrocode} % \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} % An of course globally which seems to be needed far more often. % \begin{macrocode} \let_new:NN \clist_gset_eq:NN \glet:NN \def_new:Npn \clist_gset_eq:cN {\exp_args:Nc \clist_gset_eq:NN} \def_new:Npn \clist_gset_eq:Nc {\exp_args:NNc \clist_gset_eq:NN} \def_new:Npn \clist_gset_eq:cc {\exp_args:Ncc \clist_gset_eq:NN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_if_empty_p:N} % A predicate which evaluates to |\c_true| iff the comma-list is empty. % \begin{macrocode} \let_new:NN \clist_if_empty_p:N \tlp_if_empty_p:N % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_if_empty:NTF} % \begin{macro}{\clist_if_empty:NT} % \begin{macro}{\clist_if_empty:NF} % \begin{macro}{\clist_if_empty:cTF} % \begin{macro}{\clist_if_empty:cT} % \begin{macro}{\clist_if_empty:cF} % |\clist_if_empty:NTF|\m{clist}\m{true~case}\m{false~case} will check % whether the \m{clist} is empty and then select one of the other % arguments. |\clist_if_empty:cTF| turns its first argument into a % control comma-list to get the name of the comma-list. % \begin{macrocode} \def_test_function_new:npn {clist_if_empty:N}#1{\if_meaning:NN#1\c_empty_tlp} \def_new:Npn \clist_if_empty:cTF {\exp_args:Nc\clist_if_empty:NTF} \def_new:Npn \clist_if_empty:cT {\exp_args:Nc\clist_if_empty:NT} \def_new:Npn \clist_if_empty:cF {\exp_args:Nc\clist_if_empty:NF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_if_empty_err:N} % Signals an error if the comma-list is empty. % \begin{macrocode} \def_new:Npn \clist_if_empty_err:N #1{ \if_meaning:NN#1\c_empty_tlp \tlp_clear:N \l_testa_tlp % catch prefixes \err_latex_bug:x{Empty~comma-list~`\token_to_string:N#1'} \fi:} % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_if_eq:NNTF} % As comma lists are token list pointers internally this is just an % alias. % \begin{macrocode} \let_new:NN \clist_if_eq:NNTF \tlp_if_eq:NNTF % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_get:NN} % \begin{macro}{\clist_get:cN} % |\clist_get:NN |\zz{comma-list}\zz{cmd} defines \zz{cmd} to be the % left-most element of \zz{comma-list}. % \begin{macrocode} \def_new:Npn \clist_get:NN #1{ \clist_if_empty_err:N #1 \exp_after:NN\clist_get_aux:w #1,\q_stop} % \end{macrocode} % \begin{macrocode} \def_new:Npn \clist_get_aux:w #1,#2\q_stop #3{\tlp_set:Nn #3{#1}} % \end{macrocode} % \begin{macrocode} \def_new:Npn \clist_get:cN {\exp_args:Nc \clist_get:NN} % \end{macrocode} % \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| \zz{def$\sb1$} \zz{def$\sb2$} \zz{comma-list} % \zz{cmd} assigns the left-most element of \zz{comma-list} to % \zz{cmd} using \zz{def$\sb2$}, and assigns the tail of % \zz{comma-list} to \zz{comma-list} using \zz{def$\sb1$}. % \begin{macrocode} \def_new:Npn \clist_pop_aux:nnNN #1#2#3{ \clist_if_empty_err:N #3 \exp_after:NN\clist_pop_aux:w #3,\q_nil\q_stop #1#2#3} % \end{macrocode} % \begin{macrocode} \def_new:Npn \clist_pop_aux:w #1,#2\q_stop #3#4#5#6{ #4#6{#1} #3#5{#2} % \end{macrocode} % If there was only one element in the original clist, it now contains % only |\q_nil|. % \begin{macrocode} \quark_if_nil:NTF #5 { #3#5{} } { \clist_pop_auxi:w #2 #3#5 } } % \end{macrocode} % \begin{macrocode} \def_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_put_aux:NNnnNn} % The generic put function. % \begin{macrocode} \def_new:Npn \clist_put_aux:NNnnNn #1#2#3#4#5#6{ % \end{macrocode} % When adding we have to distinguish between an empty \meta{clist} % and one that contains at least one item (otherwise we accumulate % commas). % \begin{macrocode} \clist_if_empty:NTF#5 {#1 #5{#6}} % \end{macrocode} % 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 |\tlist_if_empty:nF| is expandable prefixes % are still allowed. % \begin{macrocode} { \tlist_if_empty:nF {#6}{ #2 #5{#3#6#4} } } } % \end{macrocode} % \end{macro} % \begin{macro}{\clist_put_left:Nn} % \begin{macro}{\clist_put_left:No} % \begin{macro}{\clist_put_left:Nx} % \begin{macro}{\clist_put_left:cn} % The operations for adding to the left. % \begin{macrocode} \def_new:Npn \clist_put_left:Nn { \clist_put_aux:NNnnNn \tlp_set:Nn \tlp_put_left:Nn {} , } \def_new:Npn \clist_put_left:cn {\exp_args:Nc \clist_put_left:Nn} \def_new:Npn \clist_put_left:No {\exp_args:NNo\clist_put_left:Nn} \def_new:Npn \clist_put_left:Nx {\exp_args:Nnx\clist_put_left:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\clist_gput_left:Nn} % Global versions. % \begin{macrocode} \def_new:Npn \clist_gput_left:Nn { \clist_put_aux:NNnnNn \tlp_gset:Nn \tlp_gput_left:Nn {} , } % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_put_right:Nn} % \begin{macro}{\clist_put_right:cn} % \begin{macro}{\clist_put_right:No} % \begin{macro}{\clist_put_right:Nx} % Adding something to the right side is almost the same. % \begin{macrocode} \def_new:Npn \clist_put_right:Nn { \clist_put_aux:NNnnNn \tlp_set:Nn \tlp_put_right:Nn , {} } \def_new:Npn \clist_put_right:cn {\exp_args:Nc \clist_put_right:Nn} \def_new:Npn \clist_put_right:No {\exp_args:Nno\clist_put_right:Nn} \def_new:Npn \clist_put_right:Nx {\exp_args:Nnx\clist_put_right:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_gput_right:Nn} % \begin{macro}{\clist_gput_right:No} % \begin{macro}{\clist_gput_right:cn} % \begin{macro}{\clist_gput_right:co} % \begin{macro}{\clist_gput_right:cc} % \begin{macro}{\clist_gput_right:NC} % An here the global variants. % \begin{macrocode} \def_new:Npn \clist_gput_right:Nn { \clist_put_aux:NNnnNn \tlp_gset:Nn \tlp_gput_right:Nn , {} } \def_new:Npn \clist_gput_right:No {\exp_args:NNo \clist_gput_right:Nn} \def_new:Npn \clist_gput_right:cn {\exp_args:Nc \clist_gput_right:Nn} \def_new:Npn \clist_gput_right:co {\exp_args:Nco \clist_gput_right:Nn} \def_new:Npn \clist_gput_right:cc {\exp_args:Ncc \clist_gput_right:Nn} % \end{macrocode} % \begin{macrocode} \def_new:Npn \clist_gput_right:NC {\exp_args:NNC \clist_gput_right:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_map_function:nN} % \begin{macro}{\clist_map_function:cN} % \begin{macro}{\clist_map_function:NN} % |\clist_map_function:NN| \zz{comma-list} \zz{cmd} applies \zz{cmd} to each % element of \zz{comma-list}, from left to right. % \begin{macrocode} \def_new:Npn \clist_map_function:NN #1#2{ \clist_if_empty:NF #1 { \exp_after:NN \clist_map_function_aux:Nw \exp_after:NN #2 #1 , \q_recursion_tail , \q_recursion_stop } } \def_new:Npn \clist_map_function:cN{\exp_args:Nc\clist_map_function:NN} \def_new:Npn \clist_map_function:nN #1#2{ \tlist_if_blank:nF {#1} { \clist_map_function_aux:Nw #2 #1 , \q_recursion_tail , \q_recursion_stop } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\clist_map_function_aux:Nw} % The general loop. Tests if we hit the first stop marker and exits if % we did. If we didn't, place the function "#1" in front of the % element "#2", which is surrounded by braces. % \begin{macrocode} \def_long_new:Npn \clist_map_function_aux:Nw #1#2,{ \quark_if_recursion_tail_stop:n{#2} #1{#2} \clist_map_function_aux:Nw #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_map_break:w} % The break statement is easy. Same as in other modules, gobble % everything up to the special recursion stop marker. % \begin{macrocode} \let_new:NN \clist_map_break:w \use_none_delimit_by_q_recursion_stop:w % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_map_inline:Nn} % \begin{macro}{\clist_map_inline:cn} % \begin{macro}{\clist_map_inline:nn} % The inline type is faster but not expandable. In order to make it % nestable, we use a counter to keep track of the nesting level so % that all of the functions called have distict names. A simpler % approach would of course be to use grouping and thus the save % stack but then you lose the ability to do things locally. % % A funny little thing occured in one document: The command setting % up the first call of |\clist_map_inline:Nn| was used in a tabular % cell and the inline code used |\\| so the loop broke as soon as % this happened. Lesson to be learned from this: If you wish to have % group like structure but not using the groupings of \TeX, then do % every operation globally. % \begin{macrocode} \int_new:N \g_clist_inline_level_int \def_long_new:Npn \clist_map_inline:Nn #1#2{ \clist_if_empty:NF #1 { \int_gincr:N \g_clist_inline_level_int \gdef_long:cpn {clist_map_inline_ \int_use:N \g_clist_inline_level_int :n} ##1{#2} % \end{macrocode} % Recall that the |E| in |\exp_args:NcE| means `single token expanded % once and no braces added'. It is a lot more efficient to carry over % the special function rather than constructing the same csname over % and over again, so we just do it once. We reuse % |\clist_map_function_aux:Nw| for the actual loop. % \begin{macrocode} \exp_args:NcE \clist_map_function_aux:Nw {clist_map_inline_ \int_use:N \g_clist_inline_level_int :n} #1 , \q_recursion_tail , \q_recursion_stop \int_gdecr:N \g_clist_inline_level_int } } \def_new:Npn \clist_map_inline:cn{\exp_args:Nc\clist_map_inline:Nn} \def_long_new:Npn \clist_map_inline:nn #1#2{ \tlist_if_empty:nF {#1} { \int_gincr:N \g_clist_inline_level_int \gdef_long:cpn {clist_map_inline_ \int_use:N \g_clist_inline_level_int :n} ##1{#2} \exp_args:Nc \clist_map_function_aux:Nw {clist_map_inline_ \int_use:N \g_clist_inline_level_int :n} #1 , \q_recursion_tail , \q_recursion_stop \int_gdecr:N \g_clist_inline_level_int } } % \end{macrocode} % \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:NNn| \zz{comma-list} \zz{temp} \zz{action} assigns % \zz{temp} to each element and executes \zz{action}. % \begin{macrocode} \def_new:Npn \clist_map_variable:nNn #1#2#3{ \tlist_if_empty:nF{#1} { \clist_map_variable_aux:Nnw #2 {#3} #1 , \q_recursion_tail , \q_recursion_stop } } \def_new:Npn \clist_map_variable:NNn {\exp_args:No \clist_map_variable:nNn} \def_new:Npn \clist_map_variable:cNn {\exp_args:Nc \clist_map_variable:NNn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\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} \def_new:Npn \clist_map_variable_aux:Nnw #1#2#3,{ \def:Npn #1{#3} \quark_if_recursion_tail_stop:N #1 #2 \clist_map_variable_aux:Nnw #1{#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_concat_aux:NNNN} % \begin{macro}{\clist_concat:NNN} % \begin{macro}{\clist_gconcat:NNN} % \begin{macro}{\clist_gconcat:NNc} % \begin{macro}{\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}. % \begin{macrocode} \def_new:Npn \clist_concat_aux:NNNN #1#2#3#4{ \toks_set:No \l_tmpa_toks {#3} \toks_set:No \l_tmpb_toks {#4} #1 #2 { \toks_use:N \l_tmpa_toks % \end{macrocode} % 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} \toks_if_empty:NF \l_tmpa_toks {\toks_if_empty:NF \l_tmpb_toks ,} \toks_use:N \l_tmpb_toks } } \def_new:Npn \clist_concat:NNN {\clist_concat_aux:NNNN \tlp_set:Nx} \def_new:Npn \clist_gconcat:NNN {\clist_concat_aux:NNNN \tlp_gset:Nx} % \end{macrocode} % And the usual versions. % \begin{macrocode} \def_new:Npn \clist_gconcat:NNc{\exp_args:Nnnc\clist_gconcat:NNN} \def_new:Npn \clist_gconcat:ccc{\exp_args:Nccc\clist_gconcat:NNN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\clist_remove_duplicates_aux:NN} % \begin{macro}{\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} \def:Npn \clist_remove_duplicates_aux:NN #1#2 { \clist_clear:N \l_clist_remove_duplicates_clist \clist_map_function:NN #2 \clist_remove_duplicates_aux:n #1 #2 \l_clist_remove_duplicates_clist } \def:Npn \clist_remove_duplicates_aux:n #1 { \clist_if_in:NnTF \l_clist_remove_duplicates_clist {#1} {} {\clist_put_right:Nn \l_clist_remove_duplicates_clist {#1}} } % \end{macrocode} % The high level functions are just for telling if it should be a % local or global setting. % \begin{macrocode} \def_new:Npn \clist_remove_duplicates:N { \clist_remove_duplicates_aux:NN \clist_set_eq:NN } \def_new: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}{\l_clist_remove_duplicates_clist} % \begin{macrocode} \clist_new:N \l_clist_remove_duplicates_clist % \end{macrocode} % \end{macro} % % \begin{macro}{\clist_use:N} % \begin{macro}{\clist_use:c} % Using a \meta{clist} is just executing it but\dots % \begin{macrocode} \def_new:Npn \clist_use:N #1 { \if_meaning:NN #1 \scan_stop: % \end{macrocode} % \dots\ if \m{clist} equals |\scan_stop:| it is probably stemming % from a |\cs:w ... \cs_end:| that was created by mistake somewhere. % \begin{macrocode} \err_latex_bug:x {Comma~list~ `\token_to_string:N #1'~ has~ an~ erroneous~ structure!} \else: \exp_after:NN #1 \fi: } \def_new:Npn \clist_use:c {\exp_args:Nc \clist_use:N} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\clist_if_in:NnTF} % \begin{macro}{\clist_if_in:NoTF} % \begin{macro}{\clist_if_in:cnTF} % \begin{macro}{\clist_if_in:coTF} % |\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} \def_new:Npn \clist_if_in:NnTF #1#2{ \def:Npn \tmp:w ##1 ,#2, ##2##3\q_stop{ \if_meaning:NN\q_no_value##2 \exp_after:NN\use_arg_ii:nn \else: \exp_after:NN\use_arg_i:nn \fi: } \exp_after:NN \tmp:w \exp_after:NN , #1, #2, \q_no_value \q_stop } \def_new:Npn \clist_if_in:NoTF {\exp_args:NNo \clist_if_in:NnTF} \def_new:Npn \clist_if_in:coTF {\exp_args:Nco \clist_if_in:NnTF} \def_new:Npn \clist_if_in:cnTF {\exp_args:Nc \clist_if_in:NnTF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \subsubsection{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: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} \let_new:NN \clist_push:Nn \clist_put_left:Nn \let_new:NN \clist_push:No \clist_put_left:No \let_new:NN \clist_push:cn \clist_put_left:cn \def_new:Npn \clist_pop:NN {\clist_pop_aux:nnNN \tlp_set:Nn \tlp_set:Nn} \def_new:Npn \clist_pop:cN {\exp_args:Nc \clist_pop:NN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\clist_gpush:Nn} % \begin{macro}{\clist_gpush:No} % \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} \let_new:NN \clist_gpush:Nn \clist_gput_left:Nn \def_new:Npn \clist_gpush:No {\exp_args:NNo \clist_gpush:Nn} \def_new:Npn \clist_gpush:cn {\exp_args:Nc \clist_gpush:Nn} \def_new:Npn \clist_gpop:NN {\clist_pop_aux:nnNN \tlp_gset:Nn \tlp_set:Nn} \def_new:Npn \clist_gpop:cN {\exp_args:Nc \clist_gpop:NN} % \end{macrocode} % \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} \let_new:NN \clist_top:NN \clist_get:NN \let_new:NN \clist_top:cN \clist_get:cN % \end{macrocode} % \end{macro} % \end{macro} % % % Show token usage: % \begin{macrocode} % %<*showmemory> %\showMemUsage % % \end{macrocode} % % % % \endinput % % $Log$ % Revision 1.16 2006/07/30 15:45:46 morten % Made \clist_map_inline:Nn and variants nestable plus a little cleanup. % % Revision 1.15 2006/03/20 18:26:34 braams % Updated the copyright notice (2006) and demoted all implementation % sections to subsections and so on to clean up the toc for source3.tex % % Revision 1.14 2006/01/04 01:32:09 morten % Minor changes to code to reflect changes in l3tlp. % % Revision 1.13 2005/12/27 09:52:51 morten % Minor changes plus changed RCS information retrieval % % Revision 1.12 2005/10/27 23:14:49 morten % Improved loops. % % Revision 1.11 2005/04/12 16:45:22 morten % Enabled put operations. Added function that removes duplicate entries (mostly for % use after concatenating to clists) % % Revision 1.10 2005/04/12 13:22:13 morten % Mostly cosmetic changes. \clist_gconcat:NNN reprogrammed using "proper" methods, \clist_put_right:Nn likewise. % % Revision 1.9 2005/03/22 23:26:54 morten % Fix error message % % Revision 1.8 2005/03/16 22:36:43 braams % Added the tweaks necessary to be able to load with initex % % Revision 1.7 2005/03/11 21:36:36 braams % Fixed the use of RCS information; % Fixed a few documentation typos; added \StopEventually % Replaced \ifx by \if_meaning:NN, \c_if_empty_toks became \c_empty_toks % but needs further work according to Morten %