diff options
author | Karl Berry <karl@freefriends.org> | 2018-04-30 22:39:09 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2018-04-30 22:39:09 +0000 |
commit | a28e1bfccd6fe522fcd296426e4d89c7fb4f19b6 (patch) | |
tree | b72347e08575b56fcfed87c4d837b48dcfd1e02a /Master/texmf-dist/source/latex/l3kernel/l3clist.dtx | |
parent | b40c37a9e22b8235ff9c0bc256d00f7af94304c3 (diff) |
l3 (30apr18)
git-svn-id: svn://tug.org/texlive/trunk@47516 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3clist.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3clist.dtx | 485 |
1 files changed, 303 insertions, 182 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx b/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx index a11c54cb818..8825e0c6dab 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx @@ -2,7 +2,7 @@ % %% File: l3clist.dtx Copyright (C) 2004-2011 Frank Mittelbach, %% The LaTeX3 project -%% (C) 2012-2017 The LaTeX3 Project +%% (C) 2012-2018 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 @@ -43,34 +43,58 @@ % }^^A % } % -% \date{Released 2018/03/05} +% \date{Released 2018-04-30} % % \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, +% or right end of the list. The features of this data type largely +% overlap with those of the sequence data type, for instance applying a +% function to every item, removing duplicate items, extracting a given +% item, using the comma list with specified separators, and so on. +% +% Several items can be added at once. To ease input of comma lists from +% data provided by a user outside an \cs{ExplSyntaxOn} \ldots{} +% \cs{ExplSyntaxOff} block, spaces are removed from both sides of each +% comma-delimited argument upon input. Blank arguments are ignored, to +% allow for trailing commas or repeated commas (which may otherwise +% arise when concatenating comma lists \enquote{by hand}). In addition, +% a set of braces is removed if the result of space-trimming is braced: +% this allows the storage of any item in a comma list. For instance, % \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 } +% \clist_put_left:Nn \l_my_clist { ~a~ , ~{b}~ , c~\d } +% \clist_put_right:Nn \l_my_clist { ~{e~} , , {{f}} , } % \end{verbatim} -% results in |\l_my_clist| containing |a,{b},{c~},d|. -% Comma lists cannot contain empty items, thus +% results in |\l_my_clist| containing |a,b,c~\d,{e~},{{f}}| namely the +% five items |a|, |b|, |c~\d|, |e~| and~|{f}|. Comma lists normally do +% not contain empty items so the following gives an empty comma list: % \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} -% leaves \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. The sequence data type should be preferred -% to comma lists if items are to contain |{|, |}|, or |#| (assuming the -% usual \TeX{} category codes apply). +% and it leaves \texttt{true} in the input stream. To include an +% \enquote{unsafe} item (empty, or one that contains a comma, or starts +% or ends with a space, or is a single brace group), surround it with +% braces. +% +% Comma lists are particularly useful when implementing user interfaces, +% or when interfacing with \LaTeXe{} or other code that expects or +% provides comma list data. Almost all operations on comma lists are +% noticeably slower than those on sequences so converting the data to +% sequences using \cs{seq_set_from_clist:Nn} (see \pkg{l3seq}) may be +% advisable if speed is important. The exception is that +% \cs{clist_if_in:NnTF} and \cs{clist_remove_duplicates:N} may be faster +% than their sequence analogues for large lists. However, these +% functions work slowly for \enquote{unsafe} items that must be braced, +% and may produce errors when their argument contains |{|, |}| or |#| +% (assuming the usual \TeX{} category codes apply). In addition, comma +% lists cannot store quarks \cs{q_mark} or \cs{q_stop}. The sequence +% data type should thus certainly be preferred to comma lists to store +% such items. % % \section{Creating and initialising comma lists} % @@ -189,7 +213,12 @@ % \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. +% Blank items are omitted, spaces are removed from both sides of each +% item, then a set of braces is removed if the resulting space-trimmed +% item is braced. +% To store some \meta{tokens} as a single \meta{item} even if the +% \meta{tokens} contain commas or spaces, add a set of braces: +% \cs{clist_set:Nn} \meta{comma list} |{| \Arg{tokens} |}|. % \end{function} % % \begin{function}[updated = 2011-09-05] @@ -207,7 +236,12 @@ % \cs{clist_put_left:Nn} \meta{comma list} |{|\meta{item_1},\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. +% Blank items are omitted, spaces are removed from both sides of each +% item, then a set of braces is removed if the resulting space-trimmed +% item is braced. +% To append some \meta{tokens} as a single \meta{item} even if the +% \meta{tokens} contain commas or spaces, add a set of braces: +% \cs{clist_put_left:Nn} \meta{comma list} |{| \Arg{tokens} |}|. % \end{function} % % \begin{function}[updated = 2011-09-05] @@ -225,7 +259,12 @@ % \cs{clist_put_right:Nn} \meta{comma list} |{|\meta{item_1},\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. +% Blank items are omitted, spaces are removed from both sides of each +% item, then a set of braces is removed if the resulting space-trimmed +% item is braced. +% To append some \meta{tokens} as a single \meta{item} even if the +% \meta{tokens} contain commas or spaces, add a set of braces: +% \cs{clist_put_right:Nn} \meta{comma list} |{| \Arg{tokens} |}|. % \end{function} % % \section{Modifying comma lists} @@ -250,7 +289,7 @@ % 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 does not work if any of the items in the +% Furthermore, it may fail if any of the items in the % \meta{comma list} contains |{|, |}|, or |#| % (assuming the usual \TeX{} category codes apply). % \end{texnote} @@ -268,7 +307,7 @@ % 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 |#| +% The function may fail if the \meta{item} contains |{|, |}|, or |#| % (assuming the usual \TeX{} category codes apply). % \end{texnote} % \end{function} @@ -289,7 +328,9 @@ % \cs{clist_reverse:n} \Arg{comma list} % \end{syntax} % Leaves the items in the \meta{comma list} in the input stream in -% reverse order. Braces and spaces are preserved by this process. +% reverse order. Contrarily to other what is done for other +% \texttt{n}-type \meta{comma list} arguments, braces and spaces are +% preserved by this process. % \begin{texnote} % The result is returned within \tn{unexpanded}, which means that the % comma list does not expand further when appearing in an @@ -341,17 +382,15 @@ % \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, +% In the case of an \texttt{n}-type \meta{comma list}, the usual rules +% of space trimming and brace stripping apply. Hence, % \begin{verbatim} % \clist_if_in:nnTF { a , {b}~ , {b} , c } { b } {true} {false} % \end{verbatim} -% yields \texttt{false}. +% yields \texttt{true}. % \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. +% The function may fail if the \meta{item} contains |{|, |}|, or |#| +% (assuming the usual \TeX{} category codes apply). % \end{texnote} % \end{function} % @@ -364,7 +403,7 @@ % 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 +% and the result is passed to the mapped function. Thus, if the % 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|'. @@ -413,7 +452,7 @@ % \begin{syntax} % \cs{clist_map_break:} % \end{syntax} -% Used to terminate a |\clist_map_...| function before all +% Used to terminate a \cs[no-index]{clist_map_\ldots{}} function before all % entries in the \meta{comma list} have been processed. This % normally takes place within a conditional statement, for example % \begin{verbatim} @@ -426,7 +465,7 @@ % } % } % \end{verbatim} -% Use outside of a |\clist_map_...| scenario leads to low +% Use outside of a \cs[no-index]{clist_map_\ldots{}} scenario leads to low % level \TeX{} errors. % \begin{texnote} % When the mapping is broken, additional tokens may be inserted @@ -440,7 +479,7 @@ % \begin{syntax} % \cs{clist_map_break:n} \Arg{code} % \end{syntax} -% Used to terminate a |\clist_map_...| function before all +% Used to terminate a \cs[no-index]{clist_map_\ldots{}} function before all % entries in the \meta{comma list} have been processed, inserting % the \meta{code} after the mapping has ended. This % normally takes place within a conditional statement, for example @@ -454,7 +493,7 @@ % } % } % \end{verbatim} -% Use outside of a |\clist_map_...| scenario leads to low +% Use outside of a \cs[no-index]{clist_map_\ldots{}} scenario leads to low % level \TeX{} errors. % \begin{texnote} % When the mapping is broken, additional tokens may be inserted @@ -472,7 +511,7 @@ % 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} includes those which are duplicates, -% \emph{i.e.}~every item in a \meta{comma list} is unique. +% \emph{i.e.}~every item in a \meta{comma list} is counted. % \end{function} % % \section{Using the content of comma lists directly} @@ -624,7 +663,8 @@ % \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. +% Spaces are removed from both sides of each item as for any +% \texttt{n}-type comma list. % \end{function} % % \section{Using a single item} @@ -740,6 +780,110 @@ % \end{macrocode} % \end{macro} % +% \subsection{Removing spaces around items} +% +% \begin{macro}{\@@_trim_next:w} +% Called as \cs{exp:w} \cs{@@_trim_next:w} \cs{prg_do_nothing:} +% \meta{comma list} \ldots{} it expands to \Arg{trimmed item} where +% the \meta{trimmed item} is the first non-empty result from removing +% spaces from both ends of comma-delimited items in the \meta{comma +% list}. The \cs{prg_do_nothing:} marker avoids losing braces. The +% test for blank items is a somewhat optimized \cs{tl_if_empty:oTF} +% construction; if blank, another item is sought, otherwise trim +% spaces. +% \begin{macrocode} +\cs_new:Npn \@@_trim_next:w #1 , + { + \tl_if_empty:oTF { \use_none:nn #1 ? } + { \@@_trim_next:w \prg_do_nothing: } + { \tl_trim_spaces_apply:oN {#1} \exp_end: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP]{\@@_sanitize:n} +% \begin{macro}{\@@_sanitize:Nn} +% The auxiliary \cs{@@_sanitize:Nn} receives a delimiter +% (\cs{c_empty_tl} the first time, afterwards a comma) and that item +% as arguments. Unless we are done with the loop it calls +% \cs{@@_wrap_item:w} to unbrace the item (using a comma delimiter is +% safe since |#2| came from removing spaces from an argument delimited +% by a comma) and possibly re-brace it if needed. +% \begin{macrocode} +\cs_new:Npn \@@_sanitize:n #1 + { + \exp_after:wN \@@_sanitize:Nn \exp_after:wN \c_empty_tl + \exp:w \@@_trim_next:w \prg_do_nothing: + #1 , \q_recursion_tail , \q_recursion_stop + } +\cs_new:Npn \@@_sanitize:Nn #1#2 + { + \quark_if_recursion_tail_stop:n {#2} + #1 \@@_wrap_item:w #2 , + \exp_after:wN \@@_sanitize:Nn \exp_after:wN , + \exp:w \@@_trim_next:w \prg_do_nothing: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_wrap:n} +% \begin{macro}{\@@_if_wrap:w} +% True if the argument must be wrapped to avoid getting altered by some +% clist operations. That is the case whenever the argument +% \begin{itemize} +% \item starts or end with a space or contains a comma, +% \item is empty, or +% \item consists of a single braced group. +% \end{itemize} +% All \pkg{l3clist} functions go through the same test when they need +% to determine whether to brace an item, so it is not a problem that +% this test has false positives such as \enquote{\cs{q_mark} +% \texttt{?}}. If the argument starts or end with a space or contains +% a comma then one of the three arguments of \cs{@@_if_wrap:w} will +% have its end delimiter (partly) in one of the three copies of |#1| +% in \cs{@@_if_wrap:nTF}; this has a knock-on effect meaning that the +% result of the expansion is not empty; in that case, wrap. +% Otherwise, the argument is safe unless it starts with a brace group +% (or is empty) and it is empty or consists of a single +% \texttt{n}-type argument. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_wrap:n #1 { TF } + { + \tl_if_empty:oTF + { + \@@_if_wrap:w + \q_mark ? #1 ~ \q_mark ? ~ #1 \q_mark , ~ \q_mark #1 , + } + { + \tl_if_head_is_group:nTF { #1 { } } + { + \tl_if_empty:nTF {#1} + { \prg_return_true: } + { + \tl_if_empty:oTF { \use_none:n #1} + { \prg_return_true: } + { \prg_return_false: } + } + } + { \prg_return_false: } + } + { \prg_return_true: } + } +\cs_new:Npn \@@_if_wrap:w #1 \q_mark ? ~ #2 ~ \q_mark #3 , { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_wrap_item:w} +% Safe items are put in \cs{exp_not:n}, otherwise we put an extra set +% of braces. +% \begin{macrocode} +\cs_new:Npn \@@_wrap_item:w #1 , + { \@@_if_wrap:nTF {#1} { \exp_not:n { {#1} } } { \exp_not:n {#1} } } +% \end{macrocode} +% \end{macro} +% % \subsection{Allocation and initialisation} % % \begin{macro}{\clist_new:N, \clist_new:c} @@ -756,12 +900,11 @@ % \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. +% Creating and initializing a constant comma list is done by +% sanitizing all items (stripping spaces and braces). % \begin{macrocode} \cs_new_protected:Npn \clist_const:Nn #1#2 - { \tl_const:Nx #1 { \@@_trim_spaces:n {#2} } } + { \tl_const:Nx #1 { \@@_sanitize:n {#2} } } \cs_generate_variant:Nn \clist_const:Nn { c , Nx , cx } % \end{macrocode} % \end{macro} @@ -829,13 +972,11 @@ % \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc % } % \UnitTested -% \begin{macro}{\@@_set_from_seq:NNNN} -% \begin{macro}{\@@_wrap_item:n} -% \begin{macro}{\@@_set_from_seq:w} -% 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{macro}{\@@_set_from_seq:NNNN, \@@_set_from_seq:n} +% Setting a comma list from a comma-separated list is done using a +% simple mapping. Safe items are put in \cs{exp_not:n}, otherwise we +% put 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 { \@@_set_from_seq:NNNN \clist_clear:N \tl_set:Nx } @@ -848,19 +989,18 @@ { #2 #3 { - \exp_last_unbraced:Nf \use_none:n - { \seq_map_function:NN #4 \@@_wrap_item:n } + \exp_after:wN \use_none:n \exp:w \exp_end_continue_f:w + \seq_map_function:NN #4 \@@_set_from_seq:n } } } -\cs_new:Npn \@@_wrap_item:n #1 +\cs_new:Npn \@@_set_from_seq:n #1 { , - \tl_if_empty:oTF { \@@_set_from_seq:w #1 ~ , #1 ~ } - { \exp_not:n {#1} } + \@@_if_wrap:nTF {#1} { \exp_not:n { {#1} } } + { \exp_not:n {#1} } } -\cs_new:Npn \@@_set_from_seq: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 } @@ -869,8 +1009,6 @@ % \end{macro} % \end{macro} % \end{macro} -% \end{macro} -% \end{macro} % % \begin{macro}{\clist_concat:NNN, \clist_concat:ccc} % \UnitTested @@ -911,66 +1049,6 @@ % \end{macrocode} % \end{macro} % -% \subsection{Removing spaces around items} -% -% \begin{macro}[EXP]{\@@_trim_spaces_generic:nw} -% \begin{macro}{\@@_trim_spaces_generic:nn} -% \begin{syntax} -% \cs{@@_trim_spaces_generic:nw} \Arg{code} \cs{q_mark} \meta{item} |,| -% \end{syntax} -% 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. We reuse a \pkg{l3tl} internal function, whose -% first argument must start with \cs{q_mark}. That trims the item -% |#2|, then feeds the result (after having to do an \texttt{o}-type -% expansion) to \cs{@@_trim_spaces_generic:nn} which places the -% \meta{code} in front of the \meta{trimmed item}. -% \begin{macrocode} -\cs_new:Npn \@@_trim_spaces_generic:nw #1#2 , - { - \__tl_trim_spaces:nn {#2} - { \exp_args:No \@@_trim_spaces_generic:nn } {#1} - } -\cs_new:Npn \@@_trim_spaces_generic:nn #1#2 { #2 {#1} } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[rEXP]{\@@_trim_spaces:n} -% \begin{macro}{\@@_trim_spaces:nn} -% The first argument of \cs{@@_trim_spaces: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 \@@_trim_spaces:n #1 - { - \@@_trim_spaces_generic:nw - { \@@_trim_spaces:nn { } } - \q_mark #1 , - \q_recursion_tail, \q_recursion_stop - } -\cs_new:Npn \@@_trim_spaces:nn #1 #2 - { - \quark_if_recursion_tail_stop:n {#2} - \tl_if_empty:nTF {#2} - { - \@@_trim_spaces_generic:nw - { \@@_trim_spaces:nn {#1} } \q_mark - } - { - #1 \exp_not:n {#2} - \@@_trim_spaces_generic:nw - { \@@_trim_spaces:nn { , } } \q_mark - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% % \subsection{Adding data to comma lists} % % \begin{macro} @@ -989,9 +1067,9 @@ % } % \begin{macrocode} \cs_new_protected:Npn \clist_set:Nn #1#2 - { \tl_set:Nx #1 { \@@_trim_spaces:n {#2} } } + { \tl_set:Nx #1 { \@@_sanitize:n {#2} } } \cs_new_protected:Npn \clist_gset:Nn #1#2 - { \tl_gset:Nx #1 { \@@_trim_spaces:n {#2} } } + { \tl_gset:Nx #1 { \@@_sanitize: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} @@ -1015,8 +1093,9 @@ % } % \UnitTested % \begin{macro}{\@@_put_left:NNNn} -% Comma lists cannot hold empty values: there are therefore a couple -% of sanity checks to avoid accumulating commas. +% Everything is based on concatenation after storing in +% \cs{l_@@_internal_clist}. This avoids having to worry here about +% space-trimming and so on. % \begin{macrocode} \cs_new_protected:Npn \clist_put_left:Nn { \@@_put_left:NNNn \clist_concat:NNN \clist_set:Nn } @@ -1078,7 +1157,11 @@ % \UnitTested % \begin{macro}{\@@_get:wN} % Getting an item from the left of a comma list is pretty easy: just -% trim off the first item using the comma. +% trim off the first item using the comma. No need to trim spaces as +% comma-list \emph{variables} are assumed to have \enquote{cleaned-up} +% items. (Note that grabbing a comma-delimited item removes an outer +% pair of braces if present, exactly as needed to uncover the +% underlying item.) % \begin{macrocode} \cs_new_protected:Npn \clist_get:NN #1#2 { @@ -1211,10 +1294,11 @@ % % \subsection{Modifying comma lists} % -% \begin{variable}{\l_@@_internal_remove_clist} -% An internal comma list for the removal routines. +% \begin{variable}{\l_@@_internal_remove_clist, \l_@@_internal_remove_seq} +% An internal comma list and a sequence for the removal routines. % \begin{macrocode} \clist_new:N \l_@@_internal_remove_clist +\seq_new:N \l_@@_internal_remove_seq % \end{macrocode} % \end{variable} % @@ -1250,11 +1334,20 @@ % \UnitTested % \begin{macro}{\clist_gremove_all:Nn, \clist_gremove_all:cn} % \UnitTested -% \begin{macro}{\@@_remove_all:NNn} +% \begin{macro}{\@@_remove_all:NNNn} % \begin{macro}{\@@_remove_all:w} % \begin{macro}{\@@_remove_all:} -% 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, +% The method used here for safe items is very similar to +% \cs{tl_replace_all:Nnn}. However, if the item contains commas or +% leading/trailing spaces, or is empty, or consists of a single brace +% group, we know that it can only appear within braces so the code +% would fail; instead just convert to a sequence and do the removal +% with \pkg{l3seq} code (it involves somewhat elaborate code to do +% most of the work expandably but the final token list comparisons +% non-expandably). +% +% For \enquote{safe} items, 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 @@ -1276,28 +1369,36 @@ % the second step removes it. % \begin{macrocode} \cs_new_protected:Npn \clist_remove_all:Nn - { \@@_remove_all:NNn \tl_set:Nx } + { \@@_remove_all:NNNn \clist_set_from_seq:NN \tl_set:Nx } \cs_new_protected:Npn \clist_gremove_all:Nn - { \@@_remove_all:NNn \tl_gset:Nx } -\cs_new_protected:Npn \@@_remove_all:NNn #1#2#3 + { \@@_remove_all:NNNn \clist_gset_from_seq:NN \tl_gset:Nx } +\cs_new_protected:Npn \@@_remove_all:NNNn #1#2#3#4 { - \cs_set:Npn \@@_tmp:w ##1 , #3 , - { - ##1 - , \q_mark , \use_none_delimit_by_q_stop:w , - \@@_remove_all: - } - #1 #2 + \@@_if_wrap:nTF {#4} { - \exp_after:wN \@@_remove_all: - #2 , \q_mark , #3 , \q_stop + \seq_set_from_clist:NN \l_@@_internal_remove_seq #3 + \seq_remove_all:Nn \l_@@_internal_remove_seq {#4} + #1 #3 \l_@@_internal_remove_seq } - \clist_if_empty:NF #2 { - #1 #2 + \cs_set:Npn \@@_tmp:w ##1 , #4 , + { + ##1 + , \q_mark , \use_none_delimit_by_q_stop:w , + \@@_remove_all: + } + #2 #3 + { + \exp_after:wN \@@_remove_all: + #3 , \q_mark , #4 , \q_stop + } + \clist_if_empty:NF #3 { - \exp_args:No \exp_not:o - { \exp_after:wN \use_none:n #2 } + #2 #3 + { + \exp_args:No \exp_not:o + { \exp_after:wN \use_none:n #3 } + } } } } @@ -1425,26 +1526,46 @@ % \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}{\@@_if_in_return:nn} +% \begin{macro}{\@@_if_in_return:nnN} % \UnitTested -% See description of the \cs{tl_if_in:Nn} function for details. -% We simply surround the comma list, and the item, with commas. +% For \enquote{safe} items, we simply surround the comma list, and the +% item, with commas, then use the same code as for \cs{tl_if_in:Nn}. +% For \enquote{unsafe} items we follow the same route as +% \cs{seq_if_in:Nn}, mapping through the list a comparison function. +% If found, return \texttt{true} and remove \cs{prg_return_false:}. % \begin{macrocode} \prg_new_protected_conditional:Npnn \clist_if_in:Nn #1#2 { T , F , TF } { - \exp_args:No \@@_if_in_return:nn #1 {#2} + \exp_args:No \@@_if_in_return:nnN #1 {#2} #1 } \prg_new_protected_conditional:Npnn \clist_if_in:nn #1#2 { T , F , TF } { \clist_set:Nn \l_@@_internal_clist {#1} - \exp_args:No \@@_if_in_return:nn \l_@@_internal_clist {#2} + \exp_args:No \@@_if_in_return:nnN \l_@@_internal_clist {#2} + \l_@@_internal_clist } -\cs_new_protected:Npn \@@_if_in_return:nn #1#2 +\cs_new_protected:Npn \@@_if_in_return:nnN #1#2#3 { - \cs_set:Npn \@@_tmp:w ##1 ,#2, { } - \tl_if_empty:oTF - { \@@_tmp:w ,#1, {} {} ,#2, } - { \prg_return_false: } { \prg_return_true: } + \@@_if_wrap:nTF {#2} + { + \cs_set:Npx \@@_tmp:w ##1 + { + \exp_not:N \tl_if_eq:nnT {##1} + \exp_not:n + { + {#2} + { \clist_map_break:n { \prg_return_true: \use_none:n } } + } + } + \clist_map_function:NN #3 \@@_tmp:w + \prg_return_false: + } + { + \cs_set:Npn \@@_tmp:w ##1 ,#2, { } + \tl_if_empty:oTF + { \@@_tmp:w ,#1, {} {} ,#2, } + { \prg_return_false: } { \prg_return_true: } + } } \prg_generate_conditional_variant:Nnn \clist_if_in:Nn { NV , No , c , cV , co } { T , F , TF } @@ -1463,8 +1584,8 @@ % 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{@@_map_function:Nw} is used -% directly in \cs{clist_map_inline:Nn}. Change with care. +% The auxiliary function \cs{@@_map_function:Nw} is also used +% in \cs{clist_map_inline:Nn}. % \begin{macrocode} \cs_new:Npn \clist_map_function:NN #1#2 { @@ -1472,12 +1593,12 @@ { \exp_last_unbraced:NNo \@@_map_function:Nw #2 #1 , \q_recursion_tail , - \__prg_break_point:Nn \clist_map_break: { } + \prg_break_point:Nn \clist_map_break: { } } } \cs_new:Npn \@@_map_function:Nw #1#2 , { - \__quark_if_recursion_tail_break:nN {#2} \clist_map_break: + \quark_if_recursion_tail_break:nN {#2} \clist_map_break: #1 {#2} \@@_map_function:Nw #1 } @@ -1492,25 +1613,24 @@ % \begin{macro}{\@@_map_unbrace:Nw} % The \texttt{n}-type mapping function is a bit more awkward, % since spaces must be trimmed from each item. -% Space trimming is again based on \cs{@@_trim_spaces_generic:nw}. -% The auxiliary \cs{@@_map_function_n: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{@@_map_unbrace:Nw}. +% Space trimming is again based on \cs{@@_trim_next:w}. +% The auxiliary \cs{@@_map_function_n:Nn} receives as arguments the +% function, and the next non-empty item (after space trimming but +% before brace removal). One level of braces is removed by +% \cs{@@_map_unbrace:Nw}. % \begin{macrocode} \cs_new:Npn \clist_map_function:nN #1#2 { - \@@_trim_spaces_generic:nw { \@@_map_function_n:Nn #2 } - \q_mark #1, \q_recursion_tail, - \__prg_break_point:Nn \clist_map_break: { } + \exp_after:wN \@@_map_function_n:Nn \exp_after:wN #2 + \exp:w \@@_trim_next:w \prg_do_nothing: #1 , \q_recursion_tail , + \prg_break_point:Nn \clist_map_break: { } } \cs_new:Npn \@@_map_function_n:Nn #1 #2 { - \__quark_if_recursion_tail_break:nN {#2} \clist_map_break: - \tl_if_empty:nF {#2} { \@@_map_unbrace:Nw #1 #2, } - \@@_trim_spaces_generic:nw { \@@_map_function_n:Nn #1 } - \q_mark + \quark_if_recursion_tail_break:nN {#2} \clist_map_break: + \@@_map_unbrace:Nw #1 #2, + \exp_after:wN \@@_map_function_n:Nn \exp_after:wN #1 + \exp:w \@@_trim_next:w \prg_do_nothing: } \cs_new:Npn \@@_map_unbrace:Nw #1 #2, { #1 {#2} } % \end{macrocode} @@ -1538,14 +1658,14 @@ { \clist_if_empty:NF #1 { - \int_gincr:N \g__prg_map_int + \int_gincr:N \g__kernel_prg_map_int \cs_gset_protected:cpn - { __prg_map_ \int_use:N \g__prg_map_int :w } ##1 {#2} + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2} \exp_last_unbraced:Nco \@@_map_function:Nw - { __prg_map_ \int_use:N \g__prg_map_int :w } + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } #1 , \q_recursion_tail , - \__prg_break_point:Nn \clist_map_break: - { \int_gdecr:N \g__prg_map_int } + \prg_break_point:Nn \clist_map_break: + { \int_gdecr:N \g__kernel_prg_map_int } } } \cs_new_protected:Npn \clist_map_inline:nn #1 @@ -1567,6 +1687,8 @@ % 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. +% The strange \cs{use:n} avoids unlikely problems when |#2| would +% contain \cs{q_recursion_stop}. % \begin{macrocode} \cs_new_protected:Npn \clist_map_variable:NNn #1#2#3 { @@ -1576,7 +1698,7 @@ { \@@_map_variable:Nnw #2 {#3} } #1 , \q_recursion_tail , \q_recursion_stop - \__prg_break_point:Nn \clist_map_break: { } + \prg_break_point:Nn \clist_map_break: { } } } \cs_new_protected:Npn \clist_map_variable:nNn #1 @@ -1598,12 +1720,12 @@ % \end{macro} % % \begin{macro}{\clist_map_break:, \clist_map_break:n} -% The break statements use the general \cs{__prg_map_break:Nn} mechanism. +% The break statements use the general \cs{prg_map_break:Nn} mechanism. % \begin{macrocode} \cs_new:Npn \clist_map_break: - { \__prg_map_break:Nn \clist_map_break: { } } + { \prg_map_break:Nn \clist_map_break: { } } \cs_new:Npn \clist_map_break:n - { \__prg_map_break:Nn \clist_map_break: } + { \prg_map_break:Nn \clist_map_break: } % \end{macrocode} % \end{macro} % @@ -1769,6 +1891,7 @@ % \@@_item_n:nw, % \@@_item_n_loop:nw, % \@@_item_n_end:n, +% \@@_item_n_strip:n, % \@@_item_n_strip:w} % This starts in the same way as \cs{clist_item:Nn} by counting the items % of the comma list. The final item should be space-trimmed before being @@ -1800,10 +1923,8 @@ } } \cs_new:Npn \@@_item_n_end:n #1 #2 \q_stop - { - \__tl_trim_spaces:nn { \q_mark #1 } - { \exp_last_unbraced:No \@@_item_n_strip:w } , - } + { \tl_trim_spaces_apply:nN {#1} \@@_item_n_strip:n } +\cs_new:Npn \@@_item_n_strip:n #1 { \@@_item_n_strip:w #1 , } \cs_new:Npn \@@_item_n_strip:w #1 , { \exp_not:n {#1} } % \end{macrocode} % \end{macro} @@ -1812,7 +1933,7 @@ % \subsection{Viewing comma lists} % % \begin{macro}{\clist_show:N, \clist_show:c, \clist_log:N, \clist_log:c, \@@_show:NN} -% Apply the general \cs{__kernel_check_defined:NT} and +% Apply the general \cs{__kernel_chk_defined:NT} and % \cs{msg_show:nnnnnn}. % \begin{macrocode} \cs_new_protected:Npn \clist_show:N { \@@_show:NN \msg_show:nnxxxx } @@ -1821,7 +1942,7 @@ \cs_generate_variant:Nn \clist_log:N { c } \cs_new_protected:Npn \@@_show:NN #1#2 { - \__kernel_check_defined:NT #2 + \__kernel_chk_defined:NT #2 { #1 { LaTeX/kernel } { show-clist } { \token_to_str:N #2 } |