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.dtx448
1 files changed, 429 insertions, 19 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
index 6b29004467d..5823e3c5c8e 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
@@ -41,7 +41,7 @@
% }^^A
% }
%
-% \date{Released 2017/09/18}
+% \date{Released 2017/11/14}
%
% \maketitle
%
@@ -76,10 +76,6 @@
% This data type simplifies comparison of variables since there are no concerns
% about expansion of their contents.
%
-% Note that as string variables are a special case of token list variables
-% the coverage of \cs[no-index]{str_\ldots{}:N} functions is somewhat smaller than
-% \cs[no-index]{tl_\ldots{}:N}.
-%
% The functions \cs{cs_to_str:N}, \cs{tl_to_str:n}, \cs{tl_to_str:N} and
% \cs{token_to_str:N} (and variants) generate strings from the appropriate
% input: these are documented in \pkg{l3basics}, \pkg{l3tl} and \pkg{l3token},
@@ -148,6 +144,19 @@
% \meta{str~var_2}.
% \end{function}
%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_concat:NNN, \str_concat:ccc,
+% \str_gconcat:NNN, \str_gconcat:ccc
+% }
+% \begin{syntax}
+% \cs{str_concat:NNN} \meta{str~var_1} \meta{str~var_2} \meta{str~var_3}
+% \end{syntax}
+% 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.
+% \end{function}
+%
% \section{Adding data to string variables}
%
% \begin{function}[added = 2015-09-18]
@@ -192,6 +201,72 @@
% var} are not automatically converted to a string.
% \end{function}
%
+% \subsection{Modifying string variables}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_replace_once:Nnn, \str_replace_once:cnn,
+% \str_greplace_once:Nnn, \str_greplace_once:cnn
+% }
+% \begin{syntax}
+% \cs{str_replace_once:Nnn} \meta{str~var} \Arg{old} \Arg{new}
+% \end{syntax}
+% Converts the \meta{old} and \meta{new} token lists to strings, then
+% replaces the first (leftmost) occurrence of \meta{old string} in the
+% \meta{str~var} with \meta{new string}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_replace_all:Nnn, \str_replace_all:cnn,
+% \str_greplace_all:Nnn, \str_greplace_all:cnn
+% }
+% \begin{syntax}
+% \cs{str_replace_all:Nnn} \meta{str~var} \Arg{old} \Arg{new}
+% \end{syntax}
+% Converts the \meta{old} and \meta{new} token lists to strings, then
+% replaces all occurrences of \meta{old string} in the
+% \meta{str~var} with \meta{new string}.
+% As this function
+% operates from left to right, the pattern \meta{old string}
+% may remain after the replacement (see \cs{str_remove_all:Nn}
+% for an example).
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_remove_once:Nn, \str_remove_once:cn,
+% \str_gremove_once:Nn, \str_gremove_once:cn
+% }
+% \begin{syntax}
+% \cs{str_remove_once:Nn} \meta{str~var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% removes the first (leftmost) occurrence of \meta{string} from the
+% \meta{str~var}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_remove_all:Nn, \str_remove_all:cn,
+% \str_gremove_all:Nn, \str_gremove_all:cn
+% }
+% \begin{syntax}
+% \cs{str_remove_all:Nn} \meta{str~var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% removes all occurrences of \meta{string} from the
+% \meta{str~var}.
+% As this function
+% operates from left to right, the pattern \meta{string}
+% may remain after the removal, for instance,
+% \begin{quote}
+% \cs{str_set:Nn} \cs{l_tmpa_str} |{abbccd}|
+% \cs{str_remove_all:Nn} \cs{l_tmpa_str} |{bc}|
+% \end{quote}
+% results in \cs{l_tmpa_str} containing \texttt{abcd}.
+% \end{function}
+%
% \subsection{String conditionals}
%
% \begin{function}[EXP, pTF, added = 2015-09-18]
@@ -221,7 +296,8 @@
% \cs{str_if_eq:NNTF} \meta{str~var_1} \meta{str~var_2} \Arg{true code} \Arg{false code}
% \end{syntax}
% Compares the content of two \meta{str variables} and
-% is logically \texttt{true} if the two contain the same characters.
+% is logically \texttt{true} if the two contain the same characters
+% in the same order.
% \end{function}
%
% \begin{function}[EXP,pTF]
@@ -234,7 +310,8 @@
% \cs{str_if_eq:nnTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
% \end{syntax}
% Compares the two \meta{token lists} on a character by character
-% basis, and is \texttt{true} if the two lists contain the same
+% basis (namely after converting them to strings),
+% and is \texttt{true} if the two \meta{strings} contain the same
% characters in the same order. Thus for example
% \begin{verbatim}
% \str_if_eq_p:no { abc } { \tl_to_str:n { abc } }
@@ -247,8 +324,9 @@
% \cs{str_if_eq_x_p:nn} \Arg{tl_1} \Arg{tl_2}
% \cs{str_if_eq_x:nnTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
% \end{syntax}
-% Compares the full expansion of two \meta{token lists} on a character by
-% character basis, and is \texttt{true} if the two lists contain the same
+% Fully expands the two \meta{token lists} and converts them to \meta{strings}, then
+% compares these on a character by character basis:
+% it is \texttt{true} if the two \meta{strings} contain the same
% characters in the same order. Thus for example
% \begin{verbatim}
% \str_if_eq_x_p:nn { abc } { \tl_to_str:n { abc } }
@@ -256,6 +334,23 @@
% is logically \texttt{true}.
% \end{function}
%
+% \begin{function}[TF, added = 2017-10-08]{\str_if_in:Nn, \str_if_in:cn}
+% \begin{syntax}
+% \cs{str_if_in:NnTF} \meta{str~var} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} and
+% tests if that \meta{string} is found in the content of the
+% \meta{str~var}.
+% \end{function}
+%
+% \begin{function}[TF, added = 2017-10-08]{\str_if_in:nn}
+% \begin{syntax}
+% \cs{str_if_in:nnTF} \meta{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Converts both \meta{token lists} to \meta{strings} and
+% tests whether \meta{string_2} is found inside \meta{string_1}.
+% \end{function}
+%
% \begin{function}[added = 2013-07-24, updated = 2015-02-28, EXP, noTF]
% {\str_case:nn, \str_case:on, \str_case:nV, \str_case:nv}
% \begin{syntax}
@@ -269,8 +364,9 @@
% ~~\Arg{true code}
% ~~\Arg{false code}
% \end{syntax}
-% This function compares the \meta{test string} in turn with each
-% of the \meta{string cases}. If the two are equal (as described for
+% Compares the \meta{test string} in turn with each
+% of the \meta{string cases} (all token lists are converted to strings).
+% If the two are equal (as described for
% \cs{str_if_eq:nnTF}) then the associated \meta{code} is left in the
% input stream and other cases are discarded. If any of the
% cases are matched, the \meta{true code} is also inserted into the
@@ -292,8 +388,9 @@
% ~~\Arg{true code}
% ~~\Arg{false code}
% \end{syntax}
-% This function compares the full expansion of the \meta{test string}
-% in turn with the full expansion of the \meta{string cases}. If the two
+% Compares the full expansion of the \meta{test string}
+% in turn with the full expansion of the \meta{string cases}
+% (all token lists are converted to strings). If the two
% full expansions are equal (as described for \cs{str_if_eq:nnTF}) then the
% associated \meta{code} is left in the input stream
% and other cases are discarded. If any of the
@@ -307,6 +404,126 @@
% not be used within this string.
% \end{function}
%
+% \section{Mapping to strings}
+%
+% \begin{function}[added = 2017-10-08, 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}.
+% See also \cs{str_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08, 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}.
+% See also \cs{str_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {\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}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {\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}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {\str_map_variable:NNn, \str_map_variable:cNn}
+% \begin{syntax}
+% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{function}
+% \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}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {\str_map_variable:nNn}
+% \begin{syntax}
+% \cs{str_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{function}
+% \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}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:}
+% \begin{syntax}
+% \cs{str_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{str_map_\ldots} function before all
+% characters in the \meta{string} have been processed. 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: }
+% % Do something useful
+% }
+% \end{verbatim}
+% See also \cs{str_map_break:n}.
+% 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
+% code that follows the loop.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:n}
+% \begin{syntax}
+% \cs{str_map_break:n} \Arg{tokens}
+% \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
+% 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> } }
+% % 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
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
% \section{Working with the content of strings}
%
% \begin{function}[EXP, added = 2015-09-18]{\str_use:N, \str_use:c}
@@ -465,7 +682,7 @@
% These functions should \emph{not} be used for
% \begin{itemize}
% \item Caseless comparisons: use \cs{str_fold_case:n} for this
-% situation (case folding is district from lower casing).
+% situation (case folding is distinct from lower casing).
% \item Case changing text for typesetting: see the
% \cs[index=tl_lower_case:n]{tl_lower_case:n(n)},
% \cs[index=tl_upper_case:n]{tl_upper_case:n(n)} and
@@ -672,6 +889,8 @@
% \str_set_eq:NN, \str_set_eq:cN, \str_set_eq:Nc, \str_set_eq:cc,
% \str_gset_eq:NN, \str_gset_eq:cN, \str_gset_eq:Nc, \str_gset_eq:cc
% }
+% \begin{macro}
+% {\str_concat:NNN, \str_concat:ccc, \str_gconcat:NNN, \str_gconcat:ccc}
% A string is simply a token list. The full mapping system isn't set up
% yet so do things by hand.
% \begin{macrocode}
@@ -698,9 +917,14 @@
\cs_new_eq:NN \str_gset_eq:NN \tl_gset_eq:NN
\cs_generate_variant:Nn \str_set_eq:NN { c , Nc , cc }
\cs_generate_variant:Nn \str_gset_eq:NN { c , Nc , cc }
+\cs_new_eq:NN \str_concat:NNN \tl_concat:NNN
+\cs_new_eq:NN \str_gconcat:NNN \tl_gconcat:NNN
+\cs_generate_variant:Nn \str_concat:NNN { ccc }
+\cs_generate_variant:Nn \str_gconcat:NNN { ccc }
% \end{macrocode}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \begin{macro}
% {
@@ -745,6 +969,103 @@
% \end{macrocode}
% \end{macro}
%
+% \subsection{Modifying string variables}
+%
+% \begin{macro}
+% {
+% \str_replace_all:Nnn, \str_replace_all:cnn,
+% \str_greplace_all:Nnn, \str_greplace_all:cnn,
+% \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}
+% 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
+% simplified version of the token list code because neither the
+% delimiter nor the replacement can contain macro parameters or
+% braces. The delimiter \cs{q_mark} cannot appear in the string to
+% edit so it is used in all cases. Some |x|-expansion is unnecessary.
+% There is no need to avoid losing braces nor to protect against
+% expansion. The ending code is much simplified and does not need to
+% hide in braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_replace_once:Nnn
+ { \@@_replace:NNNnn \prg_do_nothing: \tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_once:Nnn
+ { \@@_replace:NNNnn \prg_do_nothing: \tl_gset:Nx }
+\cs_new_protected:Npn \str_replace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \tl_gset:Nx }
+\cs_generate_variant:Nn \str_replace_once:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_once:Nnn { c }
+\cs_generate_variant:Nn \str_replace_all:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_all:Nnn { c }
+\cs_new_protected:Npn \@@_replace:NNNnn #1#2#3#4#5
+ {
+ \tl_if_empty:nTF {#4}
+ {
+ \__msg_kernel_error:nnx { kernel } { empty-search-pattern } {#5}
+ }
+ {
+ \use:x
+ {
+ \exp_not:n { \@@_replace_aux:NNNnnn #1 #2 #3 }
+ { \tl_to_str:N #3 }
+ { \tl_to_str:n {#4} } { \tl_to_str:n {#5} }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_replace_aux:NNNnnn #1#2#3#4#5#6
+ {
+ \cs_set:Npn \@@_replace_next:w ##1 #5 { ##1 #6 #1 }
+ #2 #3
+ {
+ \@@_replace_next:w
+ #4
+ \use_none_delimit_by_q_stop:w
+ #5
+ \q_stop
+ }
+ }
+\cs_new_eq:NN \@@_replace_next:w ?
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\str_remove_once:Nn, \str_remove_once:cn}
+% \begin{macro}{\str_gremove_once:Nn, \str_gremove_once:cn}
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_remove_once:Nn #1#2
+ { \str_replace_once:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_once:Nn #1#2
+ { \str_greplace_once:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_once:Nn { c }
+\cs_generate_variant:Nn \str_gremove_once:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\str_remove_all:Nn, \str_remove_all:cn}
+% \begin{macro}{\str_gremove_all:Nn, \str_gremove_all:cn}
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_remove_all:Nn #1#2
+ { \str_replace_all:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_all:Nn #1#2
+ { \str_greplace_all:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_all:Nn { c }
+\cs_generate_variant:Nn \str_gremove_all:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
% \subsection{String comparisons}
%
% \begin{macro}[pTF, EXP]
@@ -871,6 +1192,30 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[TF]{\str_if_in:Nn, \str_if_in:cn \str_if_in:nn}
+% Everything here needs to be detokenized but beyond that it is a
+% simple token list test. It would be faster to fine-tune the |T|,
+% |F|, |TF| variants by calling the appropriate variant of
+% \cs{tl_if_in:nnTF} directly but that takes more code.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \str_if_in:Nn #1#2 { T , F , TF }
+ {
+ \use:x
+ { \tl_if_in:nnTF { \tl_to_str:N #1 } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \str_if_in:NnT { c }
+\cs_generate_variant:Nn \str_if_in:NnF { c }
+\cs_generate_variant:Nn \str_if_in:NnTF { c }
+\prg_new_protected_conditional:Npnn \str_if_in:nn #1#2 { T , F , TF }
+ {
+ \use:x
+ { \tl_if_in:nnTF { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \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}
@@ -945,6 +1290,71 @@
% \end{macro}
% \end{macro}
%
+% \subsection{Mapping to strings}
+%
+% \begin{macro}[rEXP]{\str_map_function:NN, \str_map_function:cN}
+% \begin{macro}{\str_map_inline:Nn, \str_map_inline:cn}
+% \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{macrocode}
+\cs_new:Npn \str_map_function:nN #1#2
+ {
+ \exp_after:wN \@@_map_function:Nn \exp_after:wN #2
+ \etex_detokenize:n {#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:Nn #1#2
+ {
+ \__quark_if_recursion_tail_break:nN {#2} \str_map_break:
+ #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
+ {
+ \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
+ \__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_protected:Npn \str_map_variable:nNn #1#2#3
+ {
+ \@@_map_variable:Nnn #2 {#3} #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
+ {
+ \str_set:Nn #1 {#3}
+ \__quark_if_recursion_tail_break:NN #1 \str_map_break:
+ \use:n {#2}
+ \@@_map_variable:Nnn #1 {#2}
+ }
+\cs_generate_variant:Nn \str_map_variable:NNn { c }
+\cs_new:Npn \str_map_break:
+ { \__prg_map_break:Nn \str_map_break: { } }
+\cs_new:Npn \str_map_break:n
+ { \__prg_map_break:Nn \str_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Accessing specific characters in a string}
%
% \begin{macro}[EXP, int]{\@@_to_other:n}
@@ -965,7 +1375,7 @@
}
\group_begin:
\tex_lccode:D `\* = `\ %
-\tex_lccode:D `\A = `\A
+\tex_lccode:D `\A = `\A %
\tex_lowercase:D
{
\group_end:
@@ -998,7 +1408,7 @@
}
\group_begin:
\tex_lccode:D `\* = `\ %
-\tex_lccode:D `\A = `\A
+\tex_lccode:D `\A = `\A %
\tex_lowercase:D
{
\group_end:
@@ -1632,7 +2042,7 @@
\tex_catcode:D `\# = 12 \scan_stop:
\tex_catcode:D `\ = 10 \scan_stop:
\tex_openin:D \g_@@_data_ior = #1 \scan_stop:
- \cs_if_exist:NT \utex_char:D
+ \cs_if_exist:NT \utex_char:D
{ \@@_map_loop: }
\tex_closein:D \g_@@_data_ior
\group_end:
@@ -1688,7 +2098,7 @@
{ \utex_char:D "#5 }
}
}
-% \end{macrocode}
+% \end{macrocode}
% Parse the main Unicode data file for title case exceptions (the one-to-one
% lower and upper case mappings it contains are all be covered by the \TeX{}
% data).
@@ -1710,7 +2120,7 @@
}
}
\@@_map_inline:n { UnicodeData.txt }
-% \end{macrocode}
+% \end{macrocode}
% The set up for case folding is in two parts. For the basic (core) mappings,
% folding is the same as lower casing in most positions so only store
% the differences. For the more complex foldings, always store the result,