summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3str.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3str.dtx214
1 files changed, 128 insertions, 86 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
index 5823e3c5c8e..2c1d4158c55 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
@@ -7,7 +7,7 @@
% 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
+% https://www.latex-project.org/lppl.txt
%
% This file is part of the "l3kernel bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
@@ -21,7 +21,7 @@
% for those people who are interested.
%
%<*driver>
-\documentclass[full]{l3doc}
+\documentclass[full,kernel]{l3doc}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
@@ -41,7 +41,7 @@
% }^^A
% }
%
-% \date{Released 2017/11/14}
+% \date{Released 2017/12/05}
%
% \maketitle
%
@@ -155,6 +155,8 @@
% Concatenates the content of \meta{str~var_2} and \meta{str~var_3}
% together and saves the result in \meta{str~var_1}. The \meta{str~var_2}
% is placed at the left side of the new string variable.
+% The \meta{str~var_2} and \meta{str~var_3} must indeed be strings, as
+% this function does not convert their contents to a string.
% \end{function}
%
% \section{Adding data to string variables}
@@ -406,71 +408,73 @@
%
% \section{Mapping to strings}
%
-% \begin{function}[added = 2017-10-08, rEXP]
+% \begin{function}[added = 2017-11-14, rEXP]
% {\str_map_function:NN, \str_map_function:cN}
% \begin{syntax}
% \cs{str_map_function:NN} \meta{str~var} \meta{function}
% \end{syntax}
-% Applies \meta{function} to every \meta{character} in the \meta{str~var}.
+% Applies \meta{function} to every \meta{character} in the
+% \meta{str~var} including spaces.
% See also \cs{str_map_function:nN}.
% \end{function}
%
-% \begin{function}[added = 2017-10-08, rEXP]
+% \begin{function}[added = 2017-11-14, rEXP]
% {\str_map_function:nN}
% \begin{syntax}
% \cs{str_map_function:nN} \Arg{token list} \meta{function}
% \end{syntax}
% Converts the \meta{token list} to a \meta{string} then
-% applies \meta{function} to every \meta{character} in the \meta{string}.
+% applies \meta{function} to every \meta{character} in the
+% \meta{string} including spaces.
% See also \cs{str_map_function:NN}.
% \end{function}
%
-% \begin{function}[added = 2017-10-08]
+% \begin{function}[added = 2017-11-14]
% {\str_map_inline:Nn, \str_map_inline:cn}
% \begin{syntax}
% \cs{str_map_inline:Nn} \meta{str~var} \Arg{inline function}
% \end{syntax}
% Applies the \meta{inline function} to every \meta{character} in the
-% \meta{str~var}. The \meta{inline function} should consist of code which
-% receives the \meta{character} as |#1|. One in line mapping can be nested
-% inside another. See also \cs{str_map_function:NN}.
+% \meta{str~var} including spaces.
+% The \meta{inline function} should consist of code which
+% receives the \meta{character} as |#1|. See also \cs{str_map_function:NN}.
% \end{function}
%
-% \begin{function}[added = 2017-10-08]
+% \begin{function}[added = 2017-11-14]
% {\str_map_inline:nn}
% \begin{syntax}
% \cs{str_map_inline:nn} \Arg{token list} \Arg{inline function}
% \end{syntax}
% Converts the \meta{token list} to a \meta{string} then
% applies the \meta{inline function} to every \meta{character} in the
-% \meta{string}. The \meta{inline function} should consist of code which
-% receives the \meta{character} as |#1|. One in line mapping can be nested
-% inside another. See also \cs{str_map_function:NN}.
+% \meta{string} including spaces.
+% The \meta{inline function} should consist of code which
+% receives the \meta{character} as |#1|. See also \cs{str_map_function:NN}.
% \end{function}
%
-% \begin{function}[added = 2017-10-08]
+% \begin{function}[added = 2017-11-14]
% {\str_map_variable:NNn, \str_map_variable:cNn}
% \begin{syntax}
-% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{function}
+% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{code}
% \end{syntax}
-% Applies the \meta{function} to every \meta{character} in
-% the \meta{str~var}. The \meta{function} should consist of code
-% which receives the \meta{character} stored in the \meta{variable}.
-% One variable mapping can be nested inside another. See also
-% \cs{str_map_inline:Nn}.
+% Stores each \meta{character} of the \meta{string} (including spaces)
+% in turn in the (string or token list) \meta{variable} and applies
+% the \meta{code}. The \meta{code} will usually make use of the
+% \meta{variable}, but this is not enforced. The assignments to the
+% \meta{variable} are local. See also \cs{str_map_inline:Nn}.
% \end{function}
%
-% \begin{function}[added = 2017-10-08]
+% \begin{function}[added = 2017-11-14]
% {\str_map_variable:nNn}
% \begin{syntax}
-% \cs{str_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{function}
+% \cs{str_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{code}
% \end{syntax}
-% Converts the \meta{token list} to a \meta{string} then
-% applies the \meta{function} to every \meta{character} in the \meta{string}.
-% The \meta{function} should consist of code
-% which receives the \meta{character} stored in the \meta{variable}.
-% One variable mapping can be nested inside another. See also
-% \cs{str_map_inline:Nn}.
+% Converts the \meta{token list} to a \meta{string} then stores each
+% \meta{character} in the \meta{string} (including spaces) in turn in
+% the (string or token list) \meta{variable} and applies the
+% \meta{code}. The \meta{code} will usually make use of the
+% \meta{variable}, but this is not enforced. The assignments to the
+% \meta{variable} are local. See also \cs{str_map_inline:Nn}.
% \end{function}
%
% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:}
@@ -491,8 +495,8 @@
% Use outside of a \cs[no-index]{str_map_\ldots} scenario leads to low
% level \TeX{} errors.
% \begin{texnote}
-% When the mapping is broken, additional tokens may be inserted by the
-% internal macro \cs{__prg_break_point:Nn} before continuing with the
+% When the mapping is broken, additional tokens may be inserted
+% before continuing with the
% code that follows the loop.
% This depends on the design of the mapping function.
% \end{texnote}
@@ -500,25 +504,25 @@
%
% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:n}
% \begin{syntax}
-% \cs{str_map_break:n} \Arg{tokens}
+% \cs{str_map_break:n} \Arg{code}
% \end{syntax}
% Used to terminate a \cs[no-index]{str_map_\ldots} function before all
% characters in the \meta{string} have been processed, inserting
-% the \meta{tokens} after the mapping has ended. This
+% the \meta{code} after the mapping has ended. This
% normally takes place within a conditional statement, for example
% \begin{verbatim}
% \str_map_inline:Nn \l_my_str
% {
% \str_if_eq:nnT { #1 } { bingo }
-% { \str_map_break:n { <tokens> } }
+% { \str_map_break:n { <code> } }
% % Do something useful
% }
% \end{verbatim}
% Use outside of a \cs[no-index]{str_map_\ldots} scenario leads to low
% level \TeX{} errors.
% \begin{texnote}
-% When the mapping is broken, additional tokens may be inserted by the
-% internal macro \cs{__prg_break_point:Nn} before the \meta{tokens} are
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
% inserted into the input stream.
% This depends on the design of the mapping function.
% \end{texnote}
@@ -978,9 +982,9 @@
% \str_replace_once:Nnn, \str_replace_once:cnn,
% \str_greplace_once:Nnn, \str_greplace_once:cnn
% }
-% \begin{macro}[aux]{\@@_replace:NNNnn}
-% \begin{macro}[aux]{\@@_replace_aux:NNNnnn}
-% \begin{macro}[aux]{\@@_replace_next:w}
+% \begin{macro}{\@@_replace:NNNnn}
+% \begin{macro}{\@@_replace_aux:NNNnnn}
+% \begin{macro}{\@@_replace_next:w}
% Start by applying \cs{tl_to_str:n} to convert the old and new token
% lists to strings, and also apply \cs{tl_to_str:N} to avoid any
% issues if we are fed a token list variable. Then the code is a much
@@ -1082,8 +1086,8 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int, EXP]{\@@_if_eq_x:nn}
-% \begin{macro}[aux, EXP]{\@@_escape_x:n}
+% \begin{macro}[EXP]{\@@_if_eq_x:nn}
+% \begin{macro}[EXP]{\@@_escape_x:n}
% String comparisons rely on the primitive \cs[index=pdfstrcmp]{(pdf)strcmp} if available:
% \LuaTeX{} does not have it, so emulation is required. As the net result
% is that we do not \emph{always} use the primitive, the correct approach
@@ -1123,7 +1127,7 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[int, EXP]{\@@_if_eq_x_return:nn}
+% \begin{macro}[EXP]{\@@_if_eq_x_return:nn}
% It turns out that we often need to compare a token list
% with the result of applying some function to it, and
% return with \cs{prg_return_true/false:}. This test is
@@ -1218,8 +1222,8 @@
%
% \begin{macro}[EXP, noTF]
% {\str_case:nn, \str_case:on, \str_case:nV, \str_case:nv, \str_case_x:nn}
-% \begin{macro}[EXP, aux]{\@@_case:nnTF, \@@_case_x:nnTF}
-% \begin{macro}[aux, EXP]
+% \begin{macro}[EXP]{\@@_case:nnTF, \@@_case_x:nnTF}
+% \begin{macro}[EXP]
% {\@@_case:nw, \@@_case_x:nw, \@@_case_end:nw}
% Much the same as \cs[index=tl_case:nn]{tl_case:nn(TF)} here:
% just a change in the internal comparison.
@@ -1297,22 +1301,43 @@
% \begin{macro}{\str_map_variable:NNn, \str_map_variable:cNn}
% \begin{macro}{\str_map_break:}
% \begin{macro}{\str_map_break:n}
-% Slightly awkward as we need to ensure detokenization: beyond that
-% just the usual token list mappings.
+% \begin{macro}[rEXP]{\@@_map_function:w, \@@_map_function:Nn}
+% \begin{macro}{\@@_map_inline:NN, \@@_map_variable:NnN}
+% The inline and variable mappings are similar to the usual token list
+% mappings but start out by turning the argument to an ``other
+% string''. Doing the same for the expandable function mapping would
+% require \cs{@@_to_other:n}, quadratic in the string length. To deal
+% with spaces in that case, \cs{@@_map_function:w} replaces the
+% following space by a braced space and a further call to itself.
+% These are received by \cs{@@_map_function:Nn}, which passes
+% the space to |#1| and calls \cs{@@_map_function:w} to deal with the
+% next space. The space before the braced space allows to optimize
+% the \cs{q_recursion_tail} test. Of course we need to include a
+% trailing space (the question mark is needed to avoid losing the
+% space when \TeX{} tokenizes the line).
+% At the cost of about three more auxiliaries this code could get a $9$
+% times speed up by testing only every $9$-th character for whether it
+% is \cs{q_recursion_tail} (also by converting $9$ spaces at a time in
+% the \cs{str_map_function:nN} case).
% \begin{macrocode}
\cs_new:Npn \str_map_function:nN #1#2
{
+ \exp_after:wN \@@_map_function:w
\exp_after:wN \@@_map_function:Nn \exp_after:wN #2
- \etex_detokenize:n {#1}
- \q_recursion_tail
+ \etex_detokenize:D {#1}
+ \q_recursion_tail ? ~
\__prg_break_point:Nn \str_map_break: { }
}
\cs_new:Npn \str_map_function:NN
{ \exp_args:No \str_map_function:nN }
+\cs_new:Npn \@@_map_function:w #1 ~
+ { #1 { ~ { ~ } \@@_map_function:w } }
\cs_new:Npn \@@_map_function:Nn #1#2
{
- \__quark_if_recursion_tail_break:nN {#2} \str_map_break:
- #1 {#2} \@@_map_function:Nn #1
+ \if_meaning:w \q_recursion_tail #2
+ \exp_after:wN \str_map_break:
+ \fi:
+ #1 #2 \@@_map_function:Nn #1
}
\cs_generate_variant:Nn \str_map_function:NN { c }
\cs_new_protected:Npn \str_map_inline:nn #1#2
@@ -1320,28 +1345,42 @@
\int_gincr:N \g__prg_map_int
\cs_gset_protected:cpn
{ __prg_map_ \int_use:N \g__prg_map_int :w } ##1 {#2}
- \exp_args:Nc \@@_map_function:Nn
- { __prg_map_ \int_use:N \g__prg_map_int :w }
- #1 \q_recursion_tail
+ \use:x
+ {
+ \exp_not:N \@@_map_inline:NN
+ \exp_not:c { __prg_map_ \int_use:N \g__prg_map_int :w }
+ \@@_to_other_fast:n {#1}
+ }
+ \q_recursion_tail
\__prg_break_point:Nn \str_map_break: { \int_gdecr:N \g__prg_map_int }
}
\cs_new_protected:Npn \str_map_inline:Nn
{ \exp_args:No \str_map_inline:nn }
\cs_generate_variant:Nn \str_map_inline:Nn { c }
+\cs_new:Npn \@@_map_inline:NN #1#2
+ {
+ \__quark_if_recursion_tail_break:NN #2 \str_map_break:
+ \exp_args:No #1 { \token_to_str:N #2 }
+ \@@_map_inline:NN #1
+ }
\cs_new_protected:Npn \str_map_variable:nNn #1#2#3
{
- \@@_map_variable:Nnn #2 {#3} #1
+ \use:x
+ {
+ \exp_not:n { \@@_map_variable:NnN #2 {#3} }
+ \@@_to_other_fast:n {#1}
+ }
\q_recursion_tail
\__prg_break_point:Nn \str_map_break: { }
}
\cs_new_protected:Npn \str_map_variable:NNn
{ \exp_args:No \str_map_variable:nNn }
-\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+\cs_new_protected:Npn \@@_map_variable:NnN #1#2#3
{
+ \__quark_if_recursion_tail_break:NN #3 \str_map_break:
\str_set:Nn #1 {#3}
- \__quark_if_recursion_tail_break:NN #1 \str_map_break:
\use:n {#2}
- \@@_map_variable:Nnn #1 {#2}
+ \@@_map_variable:NnN #1 {#2}
}
\cs_generate_variant:Nn \str_map_variable:NNn { c }
\cs_new:Npn \str_map_break:
@@ -1354,11 +1393,13 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \subsection{Accessing specific characters in a string}
%
-% \begin{macro}[EXP, int]{\@@_to_other:n}
-% \begin{macro}[EXP, aux]{\@@_to_other_loop:w, \@@_to_other_end:w}
+% \begin{macro}[EXP]{\@@_to_other:n}
+% \begin{macro}[EXP]{\@@_to_other_loop:w, \@@_to_other_end:w}
% First apply \cs{tl_to_str:n}, then replace all spaces by
% \enquote{other} spaces, $8$ at a time, storing the converted part of
% the string between the \cs{q_mark} and \cs{q_stop} markers. The end
@@ -1395,8 +1436,8 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[rEXP, int]{\@@_to_other_fast:n}
-% \begin{macro}[rEXP, aux]{\@@_to_other_fast_loop:w, \@@_to_other_fast_end:w}
+% \begin{macro}[rEXP]{\@@_to_other_fast:n}
+% \begin{macro}[rEXP]{\@@_to_other_fast_loop:w, \@@_to_other_fast_end:w}
% The difference with \cs{@@_to_other:n} is that the converted part is
% left in the input stream, making these commands only
% restricted-expandable.
@@ -1429,7 +1470,7 @@
%
% \begin{macro}[EXP]
% {\str_item:Nn, \str_item:cn, \str_item:nn, \str_item_ignore_spaces:nn}
-% \begin{macro}[EXP, aux]{\@@_item:nn, \@@_item:w}
+% \begin{macro}[EXP]{\@@_item:nn, \@@_item:w}
% The \cs{str_item:nn} hands its argument with spaces escaped to
% \cs{@@_item:nn}, and makes sure to turn the result back into
% a proper string (with category code~$10$ spaces) eventually. The
@@ -1459,7 +1500,7 @@
}
\cs_new:Npn \str_item_ignore_spaces:nn #1
{ \exp_args:No \@@_item:nn { \tl_to_str:n {#1} } }
-\__debug_patch_args:nNNpn { {#1} { (#2) } }
+\__debug_patch_args:nNNpn { {#1} { \__int_eval:n {#2} } }
\cs_new:Npn \@@_item:nn #1#2
{
\exp_after:wN \@@_item:w
@@ -1492,8 +1533,8 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[EXP, aux]{\@@_skip_exp_end:w}
-% \begin{macro}[EXP, aux]
+% \begin{macro}[EXP]{\@@_skip_exp_end:w}
+% \begin{macro}[EXP]
% {\@@_skip_loop:wNNNNNNNN, \@@_skip_end:w, \@@_skip_end:NNNNNNNN}
% Removes |max(#1,0)| characters from the input stream, and then
% leaves \cs{exp_end:}. This should be expanded using
@@ -1533,8 +1574,8 @@
%
% \begin{macro}[EXP]
% {\str_range:Nnn, \str_range:nnn, \str_range_ignore_spaces:nnn}
-% \begin{macro}[EXP, int]{\@@_range:nnn}
-% \begin{macro}[EXP, aux]{\@@_range:w, \@@_range:nnw}
+% \begin{macro}[EXP]{\@@_range:nnn}
+% \begin{macro}[EXP]{\@@_range:w, \@@_range:nnw}
% Sanitize the string. Then evaluate the arguments. At this stage we
% also decrement the \meta{start index}, since our goal is to know how
% many characters should be removed. Then limit the range to be
@@ -1556,7 +1597,8 @@
}
\cs_new:Npn \str_range_ignore_spaces:nnn #1
{ \exp_args:No \@@_range:nnn { \tl_to_str:n {#1} } }
-\__debug_patch_args:nNNpn { {#1} { (#2) } { (#3) } }
+\__debug_patch_args:nNNpn
+ { {#1} { \__int_eval:n {#2} } { \__int_eval:n {#3} } }
\cs_new:Npn \@@_range:nnn #1#2#3
{
\exp_after:wN \@@_range:w
@@ -1581,7 +1623,7 @@
% \end{macro}
% \end{macro}
% \end{macro}
-% \begin{macro}[EXP, aux]{\@@_range_normalize:nn}
+% \begin{macro}[EXP]{\@@_range_normalize:nn}
% This function converts an \meta{index} argument into an explicit
% position in the string (a result of $0$ denoting \enquote{out of
% bounds}). Expects two explicit integer arguments: the
@@ -1610,8 +1652,8 @@
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[EXP, aux]{\@@_collect_delimit_by_q_stop:w}
-% \begin{macro}[EXP, aux]
+% \begin{macro}[EXP]{\@@_collect_delimit_by_q_stop:w}
+% \begin{macro}[EXP]
% {
% \@@_collect_loop:wn, \@@_collect_loop:wnNNNNNNN,
% \@@_collect_end:wn, \@@_collect_end:nnnnnnnnw
@@ -1659,7 +1701,7 @@
%
% \begin{macro}[EXP]
% {\str_count_spaces:N, \str_count_spaces:c, \str_count_spaces:n}
-% \begin{macro}[EXP, aux]{\@@_count_spaces_loop:w}
+% \begin{macro}[EXP]{\@@_count_spaces_loop:w}
% To speed up this function, we grab and discard $9$ space-delimited
% arguments in each iteration of the loop. The loop stops when the
% last argument is one of the trailing |X|\meta{number}, and that
@@ -1692,8 +1734,8 @@
%
% \begin{macro}[EXP]
% {\str_count:N, \str_count:c, \str_count:n, \str_count_ignore_spaces:n}
-% \begin{macro}[EXP, int]{\@@_count:n}
-% \begin{macro}[EXP, aux]{\@@_count_aux:n, \@@_count_loop:NNNNNNNNN}
+% \begin{macro}[EXP]{\@@_count:n}
+% \begin{macro}[EXP]{\@@_count_aux:n, \@@_count_loop:NNNNNNNNN}
% To count characters in a string we could first escape all spaces
% using \cs{@@_to_other:n}, then pass the result to \cs{tl_count:n}.
% However, the escaping step would be quadratic in the number of
@@ -1755,7 +1797,7 @@
%
% \begin{macro}[EXP]
% {\str_head:N, \str_head:c, \str_head:n, \str_head_ignore_spaces:n}
-% \begin{macro}[EXP, aux]{\@@_head:w}
+% \begin{macro}[EXP]{\@@_head:w}
% The \texttt{_ignore_spaces} variant applies \cs{tl_to_str:n} then
% grabs the first item, thus skipping spaces.
% As usual, \cs{str_head:N} expands its argument and
@@ -1792,7 +1834,7 @@
%
% \begin{macro}[EXP]
% {\str_tail:N, \str_tail:c, \str_tail:n, \str_tail_ignore_spaces:n}
-% \begin{macro}[EXP, aux]{\@@_tail_auxi:w, \@@_tail_auxii:w}
+% \begin{macro}[EXP]{\@@_tail_auxi:w, \@@_tail_auxii:w}
% Getting the tail is a little bit more convoluted than the head of a
% string. We hit the front of the string with \cs{reverse_if:N}
% \cs{if_charcode:w} \cs{scan_stop:}. This removes the first
@@ -1835,15 +1877,15 @@
% \str_lower_case:n, \str_lower_case:f,
% \str_upper_case:n, \str_upper_case:f
% }
-% \begin{macro}[aux, EXP]{\@@_change_case:nn}
-% \begin{macro}[aux, EXP]{\@@_change_case_aux:nn}
-% \begin{macro}[aux, EXP]{\@@_change_case_result:n}
-% \begin{macro}[aux, EXP]{\@@_change_case_output:nw, \@@_change_case_output:fw}
-% \begin{macro}[aux, EXP]{\@@_change_case_end:nw}
-% \begin{macro}[aux, EXP]{\@@_change_case_loop:nw}
-% \begin{macro}[aux, EXP]{\@@_change_case_space:n}
-% \begin{macro}[aux, EXP]{\@@_change_case_char:nN}
-% \begin{macro}[aux]
+% \begin{macro}[EXP]{\@@_change_case:nn}
+% \begin{macro}[EXP]{\@@_change_case_aux:nn}
+% \begin{macro}[EXP]{\@@_change_case_result:n}
+% \begin{macro}[EXP]{\@@_change_case_output:nw, \@@_change_case_output:fw}
+% \begin{macro}[EXP]{\@@_change_case_end:nw}
+% \begin{macro}[EXP]{\@@_change_case_loop:nw}
+% \begin{macro}[EXP]{\@@_change_case_space:n}
+% \begin{macro}[EXP]{\@@_change_case_char:nN}
+% \begin{macro}
% {\@@_lookup_lower:N, \@@_lookup_upper:N, \@@_lookup_fold:N}
% Case changing for programmatic reasons is done by first detokenizing
% input then doing a simple loop that only has to worry about spaces