diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3str.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3str.dtx | 214 |
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 |