summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3clist.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3clist.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3clist.dtx210
1 files changed, 113 insertions, 97 deletions
diff --git a/macros/latex/contrib/l3kernel/l3clist.dtx b/macros/latex/contrib/l3kernel/l3clist.dtx
index f61fedb483..147fb46675 100644
--- a/macros/latex/contrib/l3kernel/l3clist.dtx
+++ b/macros/latex/contrib/l3kernel/l3clist.dtx
@@ -44,21 +44,22 @@
% }^^A
% }
%
-% \date{Released 2021-05-11}
+% \date{Released 2021-05-25}
%
% \maketitle
%
% \begin{documentation}
%
-% Comma lists contain ordered data where items can be added to the left
-% or right end of the list. This data type allows basic list
+% Comma lists (in short, |clist|) contain ordered data where items can
+% be added to the left or right end of the list. This data type allows
+% basic list
% manipulations such as adding/removing items, applying a function to
% every item, removing duplicate items, extracting a given item, using
% the comma list with specified separators, and so on. Sequences
% (defined in \pkg{l3seq}) are safer, faster, and provide more features,
% so they should often be preferred to comma lists. Comma lists are
% mostly useful when interfacing with \LaTeXe{} or other code that
-% expects or provides comma list data.
+% expects or provides items separated by commas.
%
% Several items can be added at once. To ease input of comma lists from
% data provided by a user outside an \cs{ExplSyntaxOn} \ldots{}
@@ -75,10 +76,10 @@
% \end{verbatim}
% 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:
+% not contain empty or blank 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_set:Nn \l_my_clist { , ~ , , }
% \clist_if_empty:NTF \l_my_clist { true } { false }
% \end{verbatim}
% and it leaves \texttt{true} in the input stream. To include an
@@ -86,6 +87,17 @@
% or ends with a space, or is a single brace group), surround it with
% braces.
%
+% Any |n|-type token list is a valid comma list input for \pkg{l3clist}
+% functions, which will split the token list at every comma and process
+% the items as described above. On the other hand, |N|-type functions
+% expect comma list variables, which are particular token list variables
+% in which this processing of items (and removal of blank items) has
+% already occurred. Because comma list variables are token list
+% variables, expanding them once yields their items separated by commas,
+% and \pkg{l3tl} functions such as \cs{tl_show:N} can be applied to
+% them. (These functions often have \pkg{l3clist} analogues, which
+% should be preferred.)
+%
% 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
@@ -156,7 +168,10 @@
% \cs{clist_set_eq:NN} \meta{comma list_1} \meta{comma list_2}
% \end{syntax}
% Sets the content of \meta{comma list_1} equal to that of
-% \meta{comma list_2}.
+% \meta{comma list_2}. To set a token list variable equal to a comma
+% list variable, use \cs{tl_set_eq:NN}. Conversely, setting a comma
+% list variable to a token list is unadvisable unless one checks
+% space-trimming and related issues.
% \end{function}
%
% \begin{function}[added = 2014-07-17]
@@ -831,22 +846,6 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
-% Internal recursion quarks.
-% \begin{macrocode}
-\quark_new:N \q_@@_recursion_tail
-\quark_new:N \q_@@_recursion_stop
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{macro}[EXP]{\@@_if_recursion_tail_break:nN,\@@_if_recursion_tail_stop:n}
-% Functions to query recursion quarks.
-% \begin{macrocode}
-\__kernel_quark_new_test:N \@@_if_recursion_tail_break:nN
-\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:n
-% \end{macrocode}
-% \end{macro}
-%
% \begin{macro}{\@@_tmp:w}
% A temporary function for various purposes.
% \begin{macrocode}
@@ -888,11 +887,11 @@
{
\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
+ #1 , \s_@@_stop \prg_break: , \prg_break_point:
}
\cs_new:Npn \@@_sanitize:Nn #1#2
{
- \@@_if_recursion_tail_stop:n {#2}
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
#1 \@@_wrap_item:w #2 ,
\exp_after:wN \@@_sanitize:Nn \exp_after:wN ,
\exp:w \@@_trim_next:w \prg_do_nothing:
@@ -910,10 +909,7 @@
% \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{s_@@_mark}
-% \texttt{?}}. If the argument starts or end with a space or contains
+% If the argument starts or ends 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
@@ -1394,7 +1390,13 @@
\clist_map_inline:Nn #2
{
\clist_if_in:NnF \l_@@_internal_remove_clist {##1}
- { \clist_put_right:Nn \l_@@_internal_remove_clist {##1} }
+ {
+ \tl_put_right:Nx \l_@@_internal_remove_clist
+ {
+ \clist_if_empty:NF \l_@@_internal_remove_clist { , }
+ \@@_if_wrap:nTF {##1} { \exp_not:n { {##1} } } { \exp_not:n {##1} }
+ }
+ }
}
#1 #2 \l_@@_internal_remove_clist
}
@@ -1654,28 +1656,38 @@
%
% \begin{macro}{\clist_map_function:NN, \clist_map_function:cN}
% \UnitTested
-% \begin{macro}{\@@_map_function:nw}
+% \begin{macro}{\@@_map_function:Nw, \@@_map_function_end:w}
% If the variable is empty, the mapping is skipped (otherwise,
% that comma-list would be seen as consisting of one empty item).
-% Then loop over the comma-list, grabbing one comma-delimited
-% item at a time. The end is marked by \cs{q_@@_recursion_tail}.
-% The auxiliary function \cs{@@_map_function:nw} is also used
-% in \cs{clist_map_inline:Nn}.
+% Then loop over the comma-list, grabbing eight comma-delimited items
+% at a time. The end is marked by \cs{s_@@_stop}, which may not appear
+% in any of the items. Once the last group of eight items has been
+% reached, we go through them more slowly using
+% \cs{@@_map_function_end:w}. The auxiliary function
+% \cs{@@_map_function:Nw} is also used in some other clist mappings.
% \begin{macrocode}
\cs_new:Npn \clist_map_function:NN #1#2
{
\clist_if_empty:NF #1
{
- \exp_last_unbraced:NNo \@@_map_function:nw #2 #1
- , \q_@@_recursion_tail ,
+ \exp_after:wN \@@_map_function:Nw \exp_after:wN #2 #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
\prg_break_point:Nn \clist_map_break: { }
}
}
-\cs_new:Npn \@@_map_function:nw #1#2 ,
+\cs_new:Npn \@@_map_function:Nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_function_end:w \s_@@_stop
+ #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+ \@@_map_function:Nw #1
+ }
+\cs_new:Npn \@@_map_function_end:w \s_@@_stop #1#2
{
- \@@_if_recursion_tail_break:nN {#2} \clist_map_break:
+ \@@_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s_@@_stop
#1 {#2}
- \@@_map_function:nw {#1}
+ \@@_map_function_end:w \s_@@_stop
}
\cs_generate_variant:Nn \clist_map_function:NN { c }
% \end{macrocode}
@@ -1697,12 +1709,13 @@
\cs_new:Npn \clist_map_function:nN #1#2
{
\exp_after:wN \@@_map_function_n:Nn \exp_after:wN #2
- \exp:w \@@_trim_next:w \prg_do_nothing: #1 , \q_@@_recursion_tail ,
+ \exp:w \@@_trim_next:w \prg_do_nothing: #1 ,
+ \s_@@_stop \clist_map_break: ,
\prg_break_point:Nn \clist_map_break: { }
}
\cs_new:Npn \@@_map_function_n:Nn #1 #2
{
- \@@_if_recursion_tail_break:nN {#2} \clist_map_break:
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
\@@_map_unbrace:wn #2 , #1
\exp_after:wN \@@_map_function_n:Nn \exp_after:wN #1
\exp:w \@@_trim_next:w \prg_do_nothing:
@@ -1736,9 +1749,11 @@
\int_gincr:N \g__kernel_prg_map_int
\cs_gset_protected:cpn
{ @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
- \exp_last_unbraced:Nco \@@_map_function:nw
+ \exp_last_unbraced:Nco \@@_map_function:Nw
{ @@_map_ \int_use:N \g__kernel_prg_map_int :w }
- #1 , \q_@@_recursion_tail ,
+ #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
\prg_break_point:Nn \clist_map_break:
{ \int_gdecr:N \g__kernel_prg_map_int }
}
@@ -1756,65 +1771,63 @@
% \begin{macro}{\clist_map_variable:NNn, \clist_map_variable:cNn}
% \UnitTested
% \begin{macro}{\clist_map_variable:nNn}
-% \begin{macro}{\@@_map_variable:Nnw}
-% As for other comma-list mappings, filter out the case of
-% an empty list. Same approach as \cs{clist_map_function:Nn},
-% additionally we store each item in the given variable.
-% As for inline mappings, space trimming for the \texttt{n}
-% variant is done by storing the comma list in a variable.
-% The quark test is done before assigning the item to the variable:
-% this avoids storing a quark which the user wouldn't expect.
-% The strange \cs{use:n} avoids unlikely problems when |#2| would
-% contain \cs{q_@@_recursion_stop}.
+% \begin{macro}{\@@_map_variable:Nnn}
+% The |N|-type version is a straightforward application of
+% \cs{clist_map_tokens:Nn}, calling \cs{@@_map_variable:Nnn} for each
+% item to assign the variable and run the user's code. The |n|-type
+% version is \emph{not} implemented in terms of the |n|-type function
+% \cs{clist_map_tokens:Nn}, because here we are allowed to clean up
+% the |n|-type comma list non-expandably.
% \begin{macrocode}
\cs_new_protected:Npn \clist_map_variable:NNn #1#2#3
- {
- \clist_if_empty:NF #1
- {
- \exp_args:Nno \use:nn
- { \@@_map_variable:Nnw #2 {#3} }
- #1
- , \q_@@_recursion_tail , \q_@@_recursion_stop
- \prg_break_point:Nn \clist_map_break: { }
- }
- }
+ { \clist_map_tokens:Nn #1 { \@@_map_variable:Nnn #2 {#3} } }
+\cs_generate_variant:Nn \clist_map_variable:NNn { c }
+\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+ { \tl_set:Nn #1 {#3} #2 }
\cs_new_protected:Npn \clist_map_variable:nNn #1
{
\clist_set:Nn \l_@@_internal_clist {#1}
\clist_map_variable:NNn \l_@@_internal_clist
}
-\cs_new_protected:Npn \@@_map_variable:Nnw #1#2#3,
- {
- \@@_if_recursion_tail_stop:n {#3}
- \tl_set:Nn #1 {#3}
- \use:n {#2}
- \@@_map_variable:Nnw #1 {#2}
- }
-\cs_generate_variant:Nn \clist_map_variable:NNn { c }
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\clist_map_tokens:Nn, \clist_map_tokens:cn}
-% \begin{macro}{\@@_map_tokens:nw}
+% \begin{macro}{\@@_map_tokens:nw, \@@_map_tokens_end:w}
% Essentially a copy of \cs{clist_map_function:NN} with braces added.
% \begin{macrocode}
\cs_new:Npn \clist_map_tokens:Nn #1#2
{
\clist_if_empty:NF #1
{
- \exp_last_unbraced:Nno \@@_map_function:nw {#2} #1
- , \q_@@_recursion_tail ,
+ \exp_last_unbraced:Nno \@@_map_tokens:nw {#2} #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
\prg_break_point:Nn \clist_map_break: { }
}
}
+\cs_new:Npn \@@_map_tokens:nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_tokens_end:w \s_@@_stop
+ \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+ \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+ \@@_map_tokens:nw {#1}
+ }
+\cs_new:Npn \@@_map_tokens_end:w \s_@@_stop \use:n #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s_@@_stop
+ #1 {#2}
+ \@@_map_tokens_end:w \s_@@_stop
+ }
\cs_generate_variant:Nn \clist_map_tokens:Nn { c }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\clist_map_tokens:nn, \@@_map_tokens_n:nw, \@@_use_ii_i:nn}
+% \begin{macro}{\clist_map_tokens:nn, \@@_map_tokens_n:nw}
% Similar to \cs{clist_map_function:nN} but with a different way of
% grabbing items because we cannot use \cs{exp_after:wN} to pass the
% \meta{code}.
@@ -1822,20 +1835,19 @@
\cs_new:Npn \clist_map_tokens:nn #1#2
{
\@@_map_tokens_n:nw {#2}
- \prg_do_nothing: #1 , \q_@@_recursion_tail ,
+ \prg_do_nothing: #1 , \s_@@_stop \clist_map_break: ,
\prg_break_point:Nn \clist_map_break: { }
}
\cs_new:Npn \@@_map_tokens_n:nw #1#2 ,
{
\tl_if_empty:oF { \use_none:nn #2 ? }
{
- \exp_args:No \@@_if_recursion_tail_break:nN {#2} \clist_map_break:
- \tl_trim_spaces_apply:oN {#2} \@@_use_ii_i:nn
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ \tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
\@@_map_unbrace:wn , {#1}
}
\@@_map_tokens_n:nw {#1} \prg_do_nothing:
}
-\cs_new:Npn \@@_use_ii_i:nn #1#2 { #2 #1 }
% \end{macrocode}
% \end{macro}
%
@@ -1869,22 +1881,26 @@
}
}
\cs_generate_variant:Nn \clist_count:N { c }
-\cs_new:Npx \clist_count:n #1
+\cs_new:Npn \@@_count:n #1 { + 1 }
+\cs_set_protected:Npn \@@_tmp:w #1
{
- \exp_not:N \int_eval:n
+ \cs_new:Npn \clist_count:n ##1
{
- 0
- \exp_not:N \@@_count:w \c_space_tl
- #1 \exp_not:n { , \q_@@_recursion_tail , \q_@@_recursion_stop }
+ \int_eval:n
+ {
+ 0
+ \@@_count:w #1
+ ##1 , \s_@@_stop \prg_break: , \prg_break_point:
+ }
+ }
+ \cs_new:Npn \@@_count:w ##1 ,
+ {
+ \@@_use_none_delimit_by_s_stop:w ##1 \s_@@_stop
+ \tl_if_blank:nF {##1} { + 1 }
+ \@@_count:w #1
}
}
-\cs_new:Npn \@@_count:n #1 { + 1 }
-\cs_new:Npx \@@_count:w #1 ,
- {
- \exp_not:n { \exp_args:Nf \@@_if_recursion_tail_stop:n } {#1}
- \exp_not:N \tl_if_blank:nF {#1} { + 1 }
- \exp_not:N \@@_count:w \c_space_tl
- }
+\exp_args:No \@@_tmp:w \c_space_tl
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -1907,15 +1923,15 @@
% quarks), 5: the rest of the comma list, 6: a \meta{continuation}
% function (\texttt{use_ii} or \texttt{use_iii} with its
% \meta{separator} argument), 7: junk, and 8: the temporary result,
-% which is built in a brace group following \cs{q_@@_stop}. The
+% which is built in a brace group following \cs{s_@@_stop}. The
% \meta{separator} and the first of the three items are placed in the
% result, then we use the \meta{continuation}, placing the remaining
% two items after it. When we begin this loop, the three items really
-% belong to the comma list, the first \cs{q_@@_mark} is taken as a
+% belong to the comma list, the first \cs{s_@@_mark} is taken as a
% delimiter to the \texttt{use_ii} function, and the continuation is
% \texttt{use_ii} itself. When we reach the last two items of the
-% original token list, \cs{q_@@_mark} is taken as a third item, and now
-% the second \cs{q_@@_mark} serves as a delimiter to \texttt{use_ii},
+% original token list, \cs{s_@@_mark} is taken as a third item, and now
+% the second \cs{s_@@_mark} serves as a delimiter to \texttt{use_ii},
% switching to the other \meta{continuation}, \texttt{use_iii}, which
% uses the \meta{separator between final two}.
% \begin{macrocode}
@@ -1993,7 +2009,7 @@
{ \@@_use:Nw #1#2 ; }
{
\@@_use_none_delimit_by_s_mark:w #4 #1 \s_@@_mark
- \tl_trim_spaces_apply:oN {#4} \@@_use_ii_i:nn
+ \tl_trim_spaces_apply:oN {#4} \use_ii_i:nn
\@@_map_unbrace:wn , { #2 ; }
}
#3 ; \prg_do_nothing: