From 6ac3dbed4b71022bd6bb0cc25e8fc3df81198498 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Wed, 1 May 2024 03:00:54 +0000 Subject: CTAN sync 202405010300 --- macros/latex/contrib/l3kernel/l3str.dtx | 2226 ------------------------------- 1 file changed, 2226 deletions(-) delete mode 100644 macros/latex/contrib/l3kernel/l3str.dtx (limited to 'macros/latex/contrib/l3kernel/l3str.dtx') diff --git a/macros/latex/contrib/l3kernel/l3str.dtx b/macros/latex/contrib/l3kernel/l3str.dtx deleted file mode 100644 index 88ad33c74b..0000000000 --- a/macros/latex/contrib/l3kernel/l3str.dtx +++ /dev/null @@ -1,2226 +0,0 @@ -% \iffalse meta-comment -% -%% File: l3str.dtx -% -% Copyright (C) 2011-2024 The LaTeX Project -% -% It may be distributed and/or modified under the conditions of the -% LaTeX Project Public License (LPPL), either version 1.3c of this -% license or (at your option) any later version. The latest version -% of this license is in the file -% -% 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. -% -% ----------------------------------------------------------------------- -% -% The development version of the bundle can be found at -% -% https://github.com/latex3/latex3 -% -% for those people who are interested. -% -%<*driver> -\documentclass[full,kernel]{l3doc} -\begin{document} - \DocInput{\jobname.dtx} -\end{document} -% -% \fi -% -% \title{^^A -% The \pkg{l3str} module\\ Strings^^A -% } -% -% \author{^^A -% The \LaTeX{} Project\thanks -% {^^A -% E-mail: -% \href{mailto:latex-team@latex-project.org} -% {latex-team@latex-project.org}^^A -% }^^A -% } -% -% \date{Released 2024-03-14} -% -% \maketitle -% -% \begin{documentation} -% -% \TeX{} associates each character with a category code: as such, there is no -% concept of a \enquote{string} as commonly understood in many other -% programming languages. However, there are places where we wish to manipulate -% token lists while in some sense \enquote{ignoring} category codes: this is -% done by treating token lists as strings in a \TeX{} sense. -% -% A \TeX{} string (and thus an \pkg{expl3} string) is a series of characters -% which have category code $12$ (\enquote{other}) with the exception of -% space characters which have category code $10$ (\enquote{space}). Thus -% at a technical level, a \TeX{} string is a token list with the appropriate -% category codes. In this documentation, these are simply referred to as -% strings. -% -% String variables are simply specialised token lists, but by convention -% should be named with the suffix \texttt{\ldots{}str}. Such variables -% should contain characters with category code $12$ (other), except -% spaces, which have category code $10$ (blank space). All the -% functions in this module which accept a token list argument first -% convert it to a string using \cs{tl_to_str:n} for internal processing, -% and do not treat a token list or the corresponding string -% representation differently. -% -% As a string is a subset of the more general token list, it is sometimes unclear -% when one should be used over the other. -% Use a string variable for data that isn't primarily intended for typesetting -% and for which a level of protection from unwanted expansion is suitable. -% This data type simplifies comparison of variables since there are no concerns -% about expansion of their contents. -% -% 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}, -% respectively. -% -% Most expandable functions in this module come in three flavours: -% \begin{itemize} -% \item \cs[no-index]{str_\ldots{}:N}, which expect a token list or string -% variable as their argument; -% \item \cs[no-index]{str_\ldots{}:n}, taking any token list (or string) as an -% argument; -% \item \cs[no-index]{str_\ldots{}_ignore_spaces:n}, which ignores any space -% encountered during the operation: these functions are typically -% faster than those which take care of escaping spaces -% appropriately. -% \end{itemize} -% -% \section{Creating and initialising string variables} -% -% \begin{function}[added = 2015-09-18]{\str_new:N, \str_new:c} -% \begin{syntax} -% \cs{str_new:N} \meta{str~var} -% \end{syntax} -% Creates a new \meta{str~var} or raises an error if the name is -% already taken. The declaration is global. The \meta{str~var} is -% initially empty. -% \end{function} -% -% \begin{function}[added = 2015-09-18, updated = 2018-07-28] -% { -% \str_const:Nn, \str_const:NV, \str_const:Ne, -% \str_const:cn, \str_const:cV, \str_const:ce -% } -% \begin{syntax} -% \cs{str_const:Nn} \meta{str~var} \Arg{token list} -% \end{syntax} -% Creates a new constant \meta{str~var} or raises an error if the name -% is already taken. The value of the \meta{str~var} is set -% globally to the \meta{token list}, converted to a string. -% \end{function} -% -% \begin{function}[added = 2015-09-18] -% {\str_clear:N, \str_clear:c, \str_gclear:N, \str_gclear:c} -% \begin{syntax} -% \cs{str_clear:N} \meta{str~var} -% \end{syntax} -% Clears the content of the \meta{str~var}. -% \end{function} -% -% \begin{function}[added = 2015-09-18] -% { -% \str_clear_new:N, \str_clear_new:c, -% \str_gclear_new:N, \str_gclear_new:c -% } -% \begin{syntax} -% \cs{str_clear_new:N} \meta{str~var} -% \end{syntax} -% Ensures that the \meta{str~var} exists globally by applying -% \cs{str_new:N} if necessary, then applies -% \cs[index=str_clear:N]{str_(g)clear:N} to leave -% the \meta{str~var} empty. -% \end{function} -% -% \begin{function}[added = 2015-09-18] -% { -% \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{syntax} -% \cs{str_set_eq:NN} \meta{str~var_1} \meta{str~var_2} -% \end{syntax} -% Sets the content of \meta{str~var_1} equal to that of -% \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. -% 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} -% -% \begin{function}[EXP, pTF, added = 2015-09-18] -% {\str_if_exist:N, \str_if_exist:c} -% \begin{syntax} -% \cs{str_if_exist_p:N} \meta{str~var} -% \cs{str_if_exist:NTF} \meta{str~var} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests whether the \meta{str~var} is currently defined. This does not -% check that the \meta{str~var} really is a string. -% \end{function} -% -% \section{Adding data to string variables} -% -% \begin{function}[added = 2015-09-18, updated = 2018-07-28] -% { -% \str_set:Nn, \str_set:NV, \str_set:Ne, -% \str_set:cn, \str_set:cV, \str_set:ce, -% \str_gset:Nn, \str_gset:NV, \str_gset:Ne, -% \str_gset:cn, \str_gset:cV, \str_gset:ce -% } -% \begin{syntax} -% \cs{str_set:Nn} \meta{str var} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, and stores the -% result in \meta{str var}. -% \end{function} -% -% \begin{function}[added = 2015-09-18, updated = 2018-07-28] -% { -% \str_put_left:Nn, \str_put_left:NV, \str_put_left:Ne, -% \str_put_left:cn, \str_put_left:cV, \str_put_left:ce, -% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Ne, -% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:ce -% } -% \begin{syntax} -% \cs{str_put_left:Nn} \meta{str var} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, and prepends the -% result to \meta{str var}. The current contents of the \meta{str -% var} are not automatically converted to a string. -% \end{function} -% -% \begin{function}[added = 2015-09-18, updated = 2018-07-28] -% { -% \str_put_right:Nn, \str_put_right:NV, \str_put_right:Ne, -% \str_put_right:cn, \str_put_right:cV, \str_put_right:Ne, -% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Ne, -% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:ce -% } -% \begin{syntax} -% \cs{str_put_right:Nn} \meta{str var} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, and appends the -% result to \meta{str var}. The current contents of the \meta{str -% var} are not automatically converted to a string. -% \end{function} -% -% \section{String conditionals} -% -% \begin{function}[EXP,pTF, added = 2015-09-18, updated = 2022-03-21] -% {\str_if_empty:N, \str_if_empty:c, \str_if_empty:n} -% \begin{syntax} -% \cs{str_if_empty_p:N} \meta{str~var} -% \cs{str_if_empty:NTF} \meta{str~var} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests if the \meta{string variable} is entirely empty -% (\emph{i.e.}~contains no characters at all). -% \end{function} -% -% \begin{function}[EXP,pTF, added = 2015-09-18] -% {\str_if_eq:NN, \str_if_eq:Nc, \str_if_eq:cN, \str_if_eq:cc} -% \begin{syntax} -% \cs{str_if_eq_p:NN} \meta{str~var_1} \meta{str~var_2} -% \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 -% in the same order. See \cs{tl_if_eq:NNTF} to compare tokens -% (including their category codes) rather than characters. -% \end{function} -% -% \begin{function}[EXP,pTF, updated = 2018-06-18] -% { -% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:no, -% \str_if_eq:nV, \str_if_eq:VV, \str_if_eq:vn, \str_if_eq:nv, -% \str_if_eq:ee -% } -% \begin{syntax} -% \cs{str_if_eq_p:nn} \Arg{tl_1} \Arg{tl_2} -% \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 (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 } } -% \end{verbatim} -% is logically \texttt{true}. See \cs{tl_if_eq:nnTF} to compare -% tokens (including their category codes) rather than characters. -% \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} \Arg{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 = 2022-03-21, EXP, noTF] -% { -% \str_case:nn, \str_case:Vn, \str_case:Nn, \str_case:on, \str_case:en, -% \str_case:nV, \str_case:nv -% } -% \begin{syntax} -% \cs{str_case:nnTF} \Arg{test string} \\ -% ~~|{| \\ -% ~~~~\Arg{string case_1} \Arg{code case_1} \\ -% ~~~~\Arg{string case_2} \Arg{code case_2} \\ -% ~~~~\ldots \\ -% ~~~~\Arg{string case_n} \Arg{code case_n} \\ -% ~~|}| \\ -% ~~\Arg{true code} -% ~~\Arg{false code} -% \end{syntax} -% Compares the \meta{test string} in turn with each -% of the \meta{string case}s (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 -% input stream (after the code for the appropriate case), while if none -% match then the \meta{false code} is inserted. The function -% \cs{str_case:nn}, which does nothing if there is no match, is also -% available. -% -% This set of functions performs no expansion on each -% \meta{string~case} argument, so any variable in there will be -% compared as a string. If expansion is needed in the -% \meta{string~case}s, then \cs[no-index]{str_case_e:nn(TF)} should -% be used instead. -% \end{function} -% -% \begin{function}[added = 2018-06-19, EXP, noTF] -% {\str_case_e:nn, \str_case_e:en} -% \begin{syntax} -% \cs{str_case_e:nnTF} \Arg{test string} \\ -% ~~|{| \\ -% ~~~~\Arg{string case_1} \Arg{code case_1} \\ -% ~~~~\Arg{string case_2} \Arg{code case_2} \\ -% ~~~~\ldots \\ -% ~~~~\Arg{string case_n} \Arg{code case_n} \\ -% ~~|}| \\ -% ~~\Arg{true code} -% ~~\Arg{false code} -% \end{syntax} -% Compares the full expansion of the \meta{test string} -% in turn with the full expansion of the \meta{string case}s -% (all token lists are converted to strings). If the two -% full expansions are equal (as described for \cs{str_if_eq:eeTF}) 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 -% input stream (after the code for the appropriate case), while if none -% match then the \meta{false code} is inserted. The function -% \cs{str_case_e:nn}, which does nothing if there is no match, is also -% available. -% In \cs[index=str_case_e:nnTF]{str_case_e:nn(TF)}, the \meta{test string} -% is expanded in each comparison, and must always yield the same result: -% for example, random numbers must not be used within this string. -% \end{function} -% -% \begin{function}[EXP, pTF, added = 2021-05-17]{\str_compare:nNn, \str_compare:eNe} -% \begin{syntax} -% \cs{str_compare_p:nNn} \Arg{tl_1} \meta{relation} \Arg{tl_2} -% \cs{str_compare:nNnTF} \Arg{tl_1} \meta{relation} \Arg{tl_2} \Arg{true code} \Arg{false code} -% \end{syntax} -% Compares the two \meta{token lists} on a character by character -% basis (namely after converting them to strings) in a lexicographic -% order according to the character codes of the characters. The -% \meta{relation} can be |<|, |=|, or~|>| and the test is -% \texttt{true} under the following conditions: -% \begin{itemize} -% \item for |<|, if the first string is earlier than the second in lexicographic order; -% \item for |=|, if the two strings have exactly the same characters; -% \item for |>|, if the first string is later than the second in lexicographic order. -% \end{itemize} -% Thus for example the following is logically \texttt{true}: -% \begin{verbatim} -% \str_compare_p:nNn { ab } < { abc } -% \end{verbatim} -% \begin{texnote} -% This is a wrapper around the \TeX{} primitive -% \cs[index=pdfstrcmp]{(pdf)strcmp}. It is meant for programming -% and not for sorting textual contents, as it simply considers -% character codes and not more elaborate considerations of grapheme -% clusters, locale, etc. -% \end{texnote} -% \end{function} -% -% \section{Mapping over strings} -% -% All mappings are done at the current group level, \emph{i.e.}~any -% local assignments made by the \meta{function} or \meta{code} discussed -% below remain in effect after the loop. -% -% \begin{function}[added = 2017-11-14, rEXP] -% {\str_map_function:nN, \str_map_function:NN, \str_map_function:cN} -% \begin{syntax} -% \cs{str_map_function:nN} \Arg{token list} \meta{function} -% \cs{str_map_function:NN} \meta{str~var} \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} including spaces. -% \end{function} -% -% \begin{function}[added = 2017-11-14] -% {\str_map_inline:nn, \str_map_inline:Nn, \str_map_inline:cn} -% \begin{syntax} -% \cs{str_map_inline:nn} \Arg{token list} \Arg{inline function} -% \cs{str_map_inline:Nn} \meta{str~var} \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{str~var} including spaces. -% The \meta{inline function} should consist of code which -% receives the \meta{character} as |#1|. -% \end{function} -% -% \begin{function}[rEXP, added = 2021-05-05] -% {\str_map_tokens:nn, \str_map_tokens:Nn, \str_map_tokens:cn} -% \begin{syntax} -% \cs{str_map_tokens:nn} \Arg{token list} \Arg{code} -% \cs{str_map_tokens:Nn} \meta{str~var} \Arg{code} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string} then applies -% \meta{code} to every \meta{character} in the \meta{string} including -% spaces. The \meta{code} receives each character as a trailing brace -% group. This is equivalent to \cs{str_map_function:nN} if the -% \meta{code} consists of a single function. -% \end{function} -% -% \begin{function}[added = 2017-11-14] -% {\str_map_variable:nNn, \str_map_variable:NNn, \str_map_variable:cNn} -% \begin{syntax} -% \cs{str_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{code} -% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{code} -% \end{syntax} -% 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. Its value after the loop is the last -% \meta{character} in the \meta{string}, or its original value if the -% \meta{string} is empty. 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 -% 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{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{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 { } } -% % 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 -% before the \meta{code} is -% 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} -% \begin{syntax} -% \cs{str_use:N} \meta{str~var} -% \end{syntax} -% Recovers the content of a \meta{str~var} and places it -% directly in the input stream. An error is raised if the variable -% does not exist or if it is invalid. Note that it is possible to use -% a \meta{str} directly without an accessor function. -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% {\str_count:N, \str_count:c, \str_count:n, \str_count_ignore_spaces:n} -% \begin{syntax} -% \cs{str_count:n} \Arg{token list} -% \end{syntax} -% Leaves in the input stream the number of characters in the string -% representation of \meta{token list}, as an integer denotation. The -% functions differ in their treatment of spaces. In the case of -% \cs{str_count:N} and \cs{str_count:n}, all characters including -% spaces are counted. The \cs{str_count_ignore_spaces:n} function -% leaves the number of non-space characters in the input stream. -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% {\str_count_spaces:N, \str_count_spaces:c, \str_count_spaces:n} -% \begin{syntax} -% \cs{str_count_spaces:n} \Arg{token list} -% \end{syntax} -% Leaves in the input stream the number of space characters in the -% string representation of \meta{token list}, as an integer -% denotation. Of course, this function has no \texttt{_ignore_spaces} -% variant. -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% {\str_head:N, \str_head:c, \str_head:n, \str_head_ignore_spaces:n} -% \begin{syntax} -% \cs{str_head:n} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} into a \meta{string}. The first -% character in the \meta{string} is then left in the input stream, -% with category code \enquote{other}. The functions differ if the -% first character is a space: \cs{str_head:N} and \cs{str_head:n} -% return a space token with category code~$10$ (blank space), while -% the \cs{str_head_ignore_spaces:n} function ignores this space -% character and leaves the first non-space character in the input -% stream. If the \meta{string} is empty (or only contains spaces in -% the case of the \texttt{_ignore_spaces} function), then nothing is -% left on the input stream. -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% {\str_tail:N, \str_tail:c, \str_tail:n, \str_tail_ignore_spaces:n} -% \begin{syntax} -% \cs{str_tail:n} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, removes the first -% character, and leaves the remaining characters (if any) in the input -% stream, with category codes $12$ and $10$ (for spaces). The -% functions differ in the case where the first character is a space: -% \cs{str_tail:N} and \cs{str_tail:n} only trim that space, while -% \cs{str_tail_ignore_spaces:n} removes the first non-space character -% and any space before it. If the \meta{token list} is empty (or -% blank in the case of the \texttt{_ignore_spaces} variant), then -% nothing is left on the input stream. -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% {\str_item:Nn, \str_item:nn, \str_item_ignore_spaces:nn} -% \begin{syntax} -% \cs{str_item:nn} \Arg{token list} \Arg{integer expression} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, and leaves in the -% input stream the character in position \meta{integer expression} of -% the \meta{string}, starting at $1$ for the first (left-most) -% character. In the case of \cs{str_item:Nn} and \cs{str_item:nn}, -% all characters including spaces are taken into account. The -% \cs{str_item_ignore_spaces:nn} function skips spaces when counting -% characters. If the \meta{integer expression} is negative, -% characters are counted from the end of the \meta{string}. Hence, -% $-1$ is the right-most character, \emph{etc.} -% \end{function} -% -% \begin{function}[EXP, added = 2015-09-18] -% { -% \str_range:Nnn, \str_range:cnn, \str_range:nnn, -% \str_range_ignore_spaces:nnn -% } -% \begin{syntax} -% \cs{str_range:nnn} \Arg{token list} \Arg{start index} \Arg{end index} -% \end{syntax} -% Converts the \meta{token list} to a \meta{string}, and leaves in the -% input stream the characters from the \meta{start index} to the -% \meta{end index} inclusive. Spaces are preserved and counted as items -% (contrast this with \cs{tl_range:nnn} where spaces are not counted as -% items and are possibly discarded from the output). -% -% Here \meta{start index} and \meta{end index} should be integer denotations. -% For describing in detail the functions' behavior, let $m$ and $n$ be the start -% and end index respectively. If either is $0$, the result is empty. A positive -% index means `start counting from the left end', a negative index means -% `start counting from the right end'. Let $l$ be the count of the token list. -% -% The \emph{actual start point} is determined as $M=m$ if~$m>0$ and as $M=l+m+1$ -% if~$m<0$. Similarly the \emph{actual end point} is $N=n$ if~$n>0$ and $N=l+n+1$ -% if~$n<0$. If $M>N$, the result is empty. Otherwise it consists of all items from -% position $M$ to position $N$ inclusive; for the purpose of this rule, we can -% imagine that the token list extends at infinity on either side, with void items -% at positions $s$ for $s\le0$ or $s>l$. -% For instance, -% \begin{verbatim} -% \iow_term:e { \str_range:nnn { abcdef } { 2 } { 5 } } -% \iow_term:e { \str_range:nnn { abcdef } { -4 } { -1 } } -% \iow_term:e { \str_range:nnn { abcdef } { -2 } { -1 } } -% \iow_term:e { \str_range:nnn { abcdef } { 0 } { -1 } } -% \end{verbatim} -% prints \texttt{bcde}, \texttt{cdef}, \texttt{ef}, and an empty -% line to the terminal. The \meta{start index} must always be smaller than -% or equal to the \meta{end index}: if this is not the case then no output -% is generated. Thus -% \begin{verbatim} -% \iow_term:e { \str_range:nnn { abcdef } { 5 } { 2 } } -% \iow_term:e { \str_range:nnn { abcdef } { -1 } { -4 } } -% \end{verbatim} -% both yield empty strings. -% \end{function} -% -% ^^A If this stays in the same {function} environment, we get a really -% ^^A awful page break. Perhaps we should add a way to allow a page break -% ^^A in a function environment... -% The behavior of \cs{str_range_ignore_spaces:nnn} is similar, but spaces -% are removed before starting the job. The input -% \begin{verbatim} -% \iow_term:e { \str_range:nnn { abcdefg } { 2 } { 5 } } -% \iow_term:e { \str_range:nnn { abcdefg } { 2 } { -3 } } -% \iow_term:e { \str_range:nnn { abcdefg } { -6 } { 5 } } -% \iow_term:e { \str_range:nnn { abcdefg } { -6 } { -3 } } -% -% \iow_term:e { \str_range:nnn { abc~efg } { 2 } { 5 } } -% \iow_term:e { \str_range:nnn { abc~efg } { 2 } { -3 } } -% \iow_term:e { \str_range:nnn { abc~efg } { -6 } { 5 } } -% \iow_term:e { \str_range:nnn { abc~efg } { -6 } { -3 } } -% -% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { 5 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { -3 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { 5 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { -3 } } -% -% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { 5 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { -3 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { -6 } { 5 } } -% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { -6 } { -3 } } -% \end{verbatim} -% will print four instances of |bcde|, four instances of |bc e| and eight -% instances of |bcde|. -% ^^A\end{function} -% -% \section{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} -% -% \section{String manipulation} -% -% \begin{function}[EXP, added = 2019-11-26] -% { -% \str_lowercase:n, \str_lowercase:f, -% \str_uppercase:n, \str_uppercase:f -% } -% \begin{syntax} -% \cs{str_lowercase:n} \Arg{tokens} -% \cs{str_uppercase:n} \Arg{tokens} -% \end{syntax} -% Converts the input \meta{tokens} to their string representation, as -% described for \cs{tl_to_str:n}, and then to the lower or upper -% case representation using a one-to-one mapping as described by the -% Unicode Consortium file |UnicodeData.txt|. -% -% These functions are intended for case changing programmatic data in -% places where upper/lower case distinctions are meaningful. One example -% would be automatically generating a function name from user input where -% some case changing is needed. In this situation the input is programmatic, -% not textual, case does have meaning and a language-independent one-to-one -% mapping is appropriate. For example -% \begin{verbatim} -% \cs_new_protected:Npn \myfunc:nn #1#2 -% { -% \cs_set_protected:cpn -% { -% user -% \str_uppercase:f { \tl_head:n {#1} } -% \str_lowercase:f { \tl_tail:n {#1} } -% } -% { #2 } -% } -% \end{verbatim} -% would be used to generate a function with an auto-generated name consisting -% of the upper case equivalent of the supplied name followed by the lower -% case equivalent of the rest of the input. -% -% These functions should \emph{not} be used for -% \begin{itemize} -% \item Caseless comparisons: use \cs{str_casefold:n} for this -% situation (case folding is distinct from lower casing). -% \item Case changing text for typesetting: see the -% \cs[index=text_lowercase:n]{text_lowercase:n(n)}, -% \cs[index=text_uppercase:n]{text_uppercase:n(n)} and -% \cs[index=text_titlecase_all:n]{text_titlecase_(all|once):n(n)} functions which -% correctly deal with context-dependence and other factors appropriate -% to text case changing. -% \end{itemize} -% \end{function} -% -% \begin{function}[EXP, added = 2022-10-16] -% {\str_casefold:n, \str_casefold:V} -% \begin{syntax} -% \cs{str_casefold:n} \Arg{tokens} -% \end{syntax} -% Converts the input \meta{tokens} to their string representation, as -% described for \cs{tl_to_str:n}, and then folds the case of the resulting -% \meta{string} to remove case information. The result of this process is -% left in the input stream. -% -% String folding is a process used for material such as identifiers rather -% than for \enquote{text}. The folding provided by \cs{str_casefold:n} -% follows the mappings provided by the \href{http://www.unicode.org}^^A -% {Unicode Consortium}, who -% \href{http://www.unicode.org/faq/casemap_charprop.html#2}{state}: -% \begin{quote} -% Case folding is primarily used for caseless comparison of text, such -% as identifiers in a computer program, rather than actual text -% transformation. Case folding in Unicode is based on the lowercase -% mapping, but includes additional changes to the source text to help make -% it language-insensitive and consistent. As a result, case-folded text -% should be used solely for internal processing and generally should not be -% stored or displayed to the end user. -% \end{quote} -% The folding approach implemented by \cs{str_casefold:n} follows the -% \enquote{full} scheme defined by the Unicode Consortium -% (\emph{e.g.}~\SS folds to \texttt{SS}). As case-folding is -% a language-insensitive process, there is no special treatment of -% Turkic input (\emph{i.e.}~\texttt{I} always folds to \texttt{i} and -% not to \texttt{\i}). -% \end{function} -% -% \begin{function}[added = 2023-05-19, EXP]{\str_mdfive_hash:n, \str_mdfive_hash:e} -% \begin{syntax} -% \cs{str_mdfive_hash:n} \Arg{tl} -% \end{syntax} -% Expands to the MD5 sum generated from the \meta{tl}, which is converted -% to a \meta{string} as described for \cs{tl_to_str:n}. -% \end{function} -% -% \section{Viewing strings} -% -% \begin{function}[added = 2015-09-18, updated = 2021-04-29] -% {\str_show:N, \str_show:c, \str_show:n} -% \begin{syntax} -% \cs{str_show:N} \meta{str~var} -% \end{syntax} -% Displays the content of the \meta{str~var} on the terminal. -% \end{function} -% -% \begin{function}[added = 2019-02-15, updated = 2021-04-29] -% {\str_log:N, \str_log:c, \str_log:n} -% \begin{syntax} -% \cs{str_log:N} \meta{str~var} -% \end{syntax} -% Writes the content of the \meta{str~var} in the log file. -% \end{function} -% -% \section{Constant strings} -% -% \begin{variable}[added = 2015-09-19, updated = 2020-12-22, module = str] -% { -% \c_ampersand_str, -% \c_atsign_str, -% \c_backslash_str, -% \c_left_brace_str, -% \c_right_brace_str, -% \c_circumflex_str, -% \c_colon_str, -% \c_dollar_str, -% \c_hash_str, -% \c_percent_str, -% \c_tilde_str, -% \c_underscore_str, -% \c_zero_str -% } -% Constant strings, containing a single character token, with category -% code $12$. -% \end{variable} -% -% \begin{variable}[added = 2023-12-07]{\c_empty_str} -% Constant that is always empty. -% \end{variable} -% -% \section{Scratch strings} -% -% \begin{variable}{\l_tmpa_str, \l_tmpb_str} -% Scratch strings for local assignment. These are never used by -% the kernel code, and so are safe for use with any \LaTeX3-defined -% function. However, they may be overwritten by other non-kernel -% code and so should only be used for short-term storage. -% \end{variable} -% -% \begin{variable}{\g_tmpa_str, \g_tmpb_str} -% Scratch strings for global assignment. These are never used by -% the kernel code, and so are safe for use with any \LaTeX3-defined -% function. However, they may be overwritten by other non-kernel -% code and so should only be used for short-term storage. -% \end{variable} -% -% \end{documentation} -% -% \begin{implementation} -% -% \section{\pkg{l3str} implementation} -% -% \begin{macrocode} -%<*package> -% \end{macrocode} -% -% \begin{macrocode} -%<@@=str> -% \end{macrocode} -% -% \subsection{Internal auxiliaries} -% -% \begin{variable}{\s_@@_mark,\s_@@_stop} -% Internal scan marks. -% \begin{macrocode} -\scan_new:N \s_@@_mark -\scan_new:N \s_@@_stop -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[EXP]{ -% \@@_use_none_delimit_by_s_stop:w, -% \@@_use_i_delimit_by_s_stop:nw -% } -% Functions to gobble up to a scan mark. -% \begin{macrocode} -\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { } -\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1} -% \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_do:Nn -% } -% 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_do:Nn -% \end{macrocode} -% \end{macro} -% -% \subsection{Creating and setting string variables} -% -% \begin{macro} -% { -% \str_new:N, \str_new:c, -% \str_use:N, \str_use:c, -% \str_clear:N, \str_clear:c, -% \str_gclear:N,\str_gclear:c, -% \str_clear_new:N, \str_clear_new:c, -% \str_gclear_new:N, \str_gclear_new:c -% } -% \begin{macro} -% { -% \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} -\group_begin: - \cs_set_protected:Npn \@@_tmp:n #1 - { - \tl_if_blank:nF {#1} - { - \cs_new_eq:cc { str_ #1 :N } { tl_ #1 :N } - \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :N } { c } - \@@_tmp:n - } - } - \@@_tmp:n - { new } - { use } - { clear } - { gclear } - { clear_new } - { gclear_new } - { } -\group_end: -\cs_new_eq:NN \str_set_eq:NN \tl_set_eq:NN -\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} -% { -% \str_set:Nn, \str_set:NV, \str_set:Ne, \str_set:Nx, -% \str_set:cn, \str_set:cV, \str_set:ce, \str_set:cx, -% \str_gset:Nn, \str_gset:NV, \str_gset:Ne, \str_gset:Nx, -% \str_gset:cn, \str_gset:cV, \str_gset:ce, \str_gset:cx, -% \str_const:Nn, \str_const:NV, \str_const:Ne, \str_const:Nx, -% \str_const:cn, \str_const:cV, \str_const:ce, \str_const:cx, -% \str_put_left:Nn, \str_put_left:NV, \str_put_left:Ne, \str_put_left:Nx, -% \str_put_left:cn, \str_put_left:cV, \str_put_left:ce, \str_put_left:cx, -% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Ne, \str_gput_left:Nx, -% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:ce, \str_gput_left:cx, -% \str_put_right:Nn, \str_put_right:NV, \str_put_right:Ne, \str_put_right:Nx, -% \str_put_right:cn, \str_put_right:cV, \str_put_right:ce, \str_put_right:cx, -% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Ne, \str_gput_right:Nx, -% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:ce, \str_gput_right:cx -% } -% Similar to corresponding \pkg{l3tl} base functions, except that -% \cs{__kernel_exp_not:w} is replaced with \cs{__kernel_tl_to_str:w}. -% Just like token list, string constants use \cs{cs_gset_nopar:Npe} -% instead of \cs{__kernel_tl_gset:Nx} so that the scope checking for -% |c| is applied when \pkg{l3debug} is used. -% To maintain backward compatibility, in -% \cs[index=str_put_left:Nn]{str_(g)put_left:Nn} and -% \cs[index=str_put_right:Nn]{str_(g)put_right:Nn}, -% contents of string variables are wrapped in \cs{__kernel_exp_not:w} -% to prevent further expansion. -% \begin{macrocode} -\cs_new_protected:Npn \str_set:Nn #1#2 - { \__kernel_tl_set:Nx #1 { \__kernel_tl_to_str:w {#2} } } -\cs_gset_protected:Npn \str_gset:Nn #1#2 - { \__kernel_tl_gset:Nx #1 { \__kernel_tl_to_str:w {#2} } } -\cs_new_protected:Npn \str_const:Nn #1#2 - { - \__kernel_chk_if_free_cs:N #1 - \cs_gset_nopar:Npe #1 { \__kernel_tl_to_str:w {#2} } - } -\cs_new_protected:Npn \str_put_left:Nn #1#2 - { - \__kernel_tl_set:Nx #1 - { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} } - } -\cs_new_protected:Npn \str_gput_left:Nn #1#2 - { - \__kernel_tl_gset:Nx #1 - { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} } - } -\cs_new_protected:Npn \str_put_right:Nn #1#2 - { - \__kernel_tl_set:Nx #1 - { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} } - } -\cs_new_protected:Npn \str_gput_right:Nn #1#2 - { - \__kernel_tl_gset:Nx #1 - { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} } - } -\cs_generate_variant:Nn \str_set:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_gset:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_const:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_put_left:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_gput_left:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_put_right:Nn { NV , Ne , Nx , c , cV , ce , cx } -\cs_generate_variant:Nn \str_gput_right:Nn { NV , Ne , Nx , c , cV , ce , cx } -% \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}{\@@_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 -% simplified version of the token list code because neither the -% delimiter nor the replacement can contain macro parameters or -% braces. The delimiter \cs{s_@@_mark} cannot appear in the string to -% edit so it is used in all cases. Some |e|-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: \__kernel_tl_set:Nx } -\cs_new_protected:Npn \str_greplace_once:Nnn - { \@@_replace:NNNnn \prg_do_nothing: \__kernel_tl_gset:Nx } -\cs_new_protected:Npn \str_replace_all:Nnn - { \@@_replace:NNNnn \@@_replace_next:w \__kernel_tl_set:Nx } -\cs_new_protected:Npn \str_greplace_all:Nnn - { \@@_replace:NNNnn \@@_replace_next:w \__kernel_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_error:nne { kernel } { empty-search-pattern } {#5} - } - { - \use:e - { - \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_s_stop:w - #5 - \s_@@_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] -% { -% \str_if_empty:N, \str_if_empty:c, \str_if_empty:n, -% \str_if_exist:N, \str_if_exist:c -% } -% More copy-paste! -% \begin{macrocode} -\prg_new_eq_conditional:NNn \str_if_exist:N \tl_if_exist:N - { p , T , F , TF } -\prg_new_eq_conditional:NNn \str_if_exist:c \tl_if_exist:c - { p , T , F , TF } -\prg_new_eq_conditional:NNn \str_if_empty:N \tl_if_empty:N - { p , T , F , TF } -\prg_new_eq_conditional:NNn \str_if_empty:c \tl_if_empty:c - { p , T , F , TF } -\prg_new_eq_conditional:NNn \str_if_empty:n \tl_if_empty:n - { p , T , F , TF } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP]{\@@_if_eq:nn} -% String comparisons rely on the primitive \tn[index=pdfstrcmp]{(pdf)strcmp}, -% so we define a new name for it. -% \begin{macrocode} -\cs_new_eq:NN \@@_if_eq:nn \tex_strcmp:D -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[pTF, EXP]{\str_compare:nNn, \str_compare:eNe} -% Simply rely on \cs{@@_if_eq:nn}, which expands to |-1|, |0| -% or~|1|. The |ee| version is created directly because it is more efficient. -% \begin{macrocode} -\prg_new_conditional:Npnn \str_compare:nNn #1#2#3 { p , T , F , TF } - { - \if_int_compare:w - \@@_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#3} } - #2 \c_zero_int - \prg_return_true: \else: \prg_return_false: \fi: - } -\prg_new_conditional:Npnn \str_compare:eNe #1#2#3 { p , T , F , TF } - { - \if_int_compare:w \@@_if_eq:nn {#1} {#3} #2 \c_zero_int - \prg_return_true: \else: \prg_return_false: \fi: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[pTF, EXP] -% { -% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:nV, -% \str_if_eq:no, \str_if_eq:VV, -% \str_if_eq:ee -% } -% Modern engines provide a direct way of comparing two token lists, -% but returning a number. This set of conditionals therefore makes life -% a bit clearer. The \texttt{nn} and \texttt{ee} versions are created -% directly as this is most efficient. Since \cs{@@_if_eq:nn} will expand to -% |0| as an explicit character with category 12 if the two lists match (and -% either |-1| or |1| if they don't) we can use \cs{if:w} here which is faster -% than using \cs{if_int_compare:w}. -% \begin{macrocode} -\prg_new_conditional:Npnn \str_if_eq:nn #1#2 { p , T , F , TF } - { - \if:w 0 \@@_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#2} } - \prg_return_true: \else: \prg_return_false: \fi: - } -\prg_generate_conditional_variant:Nnn \str_if_eq:nn - { V , v , o , nV , no , VV , nv } { p , T , F , TF } -\prg_new_conditional:Npnn \str_if_eq:ee #1#2 { p , T , F , TF } - { - \if:w 0 \@@_if_eq:nn {#1} {#2} - \prg_return_true: \else: \prg_return_false: \fi: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP, pTF] -% {\str_if_eq:NN, \str_if_eq:Nc, \str_if_eq:cN, \str_if_eq:cc} -% Note that \cs{str_if_eq:NNTF} is different from -% \cs{tl_if_eq:NNTF} because it needs to ignore category codes. -% \begin{macrocode} -\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F } - { - \if:w 0 \@@_if_eq:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 } - \prg_return_true: \else: \prg_return_false: \fi: - } -\prg_generate_conditional_variant:Nnn \str_if_eq:NN - { c , Nc , cc } { T , F , TF , p } -% \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:e - { \tl_if_in:nnTF { \tl_to_str:N #1 } { \tl_to_str:n {#2} } } - { \prg_return_true: } { \prg_return_false: } - } -\prg_generate_conditional_variant:Nnn \str_if_in:Nn - { c } { T , F , TF } -\prg_new_protected_conditional:Npnn \str_if_in:nn #1#2 { T , F , TF } - { - \use:e - { \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:Vn, \str_case:Nn, \str_case:on, \str_case:en, \str_case:nV, \str_case:nv, -% \str_case_e:nn, \str_case_e:en -% } -% \begin{macro}[EXP]{\@@_case:nnTF, \@@_case_e:nnTF} -% \begin{macro}[EXP] -% {\@@_case:nw, \@@_case_e:nw, \@@_case_end:nw} -% The aim here is to allow the case statement to be evaluated -% using a known number of expansion steps (two), and without -% needing to use an explicit \enquote{end of recursion} marker. -% That is achieved by using the test input as the final case, -% as this is always true. The trick is then to tidy up -% the output such that the appropriate case code plus either -% the \texttt{true} or \texttt{false} branch code is inserted. -% \begin{macrocode} -\cs_new:Npn \str_case:nn #1#2 - { - \exp:w - \@@_case:nnTF {#1} {#2} { } { } - } -\cs_new:Npn \str_case:nnT #1#2#3 - { - \exp:w - \@@_case:nnTF {#1} {#2} {#3} { } - } -\cs_new:Npn \str_case:nnF #1#2 - { - \exp:w - \@@_case:nnTF {#1} {#2} { } - } -\cs_new:Npn \str_case:nnTF #1#2 - { - \exp:w - \@@_case:nnTF {#1} {#2} - } -\cs_new:Npn \@@_case:nnTF #1#2#3#4 - { \@@_case:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop } -\cs_generate_variant:Nn \str_case:nn { V , o , e , nV , nv } -\prg_generate_conditional_variant:Nnn \str_case:nn - { V , o , e , nV , nv } { T , F , TF } -\cs_new_eq:NN \str_case:Nn \str_case:Vn -\cs_new_eq:NN \str_case:NnT \str_case:VnT -\cs_new_eq:NN \str_case:NnF \str_case:VnF -\cs_new_eq:NN \str_case:NnTF \str_case:VnTF -\cs_new:Npn \@@_case:nw #1#2#3 - { - \str_if_eq:nnTF {#1} {#2} - { \@@_case_end:nw {#3} } - { \@@_case:nw {#1} } - } -\cs_new:Npn \str_case_e:nn #1#2 - { - \exp:w - \@@_case_e:nnTF {#1} {#2} { } { } - } -\cs_new:Npn \str_case_e:nnT #1#2#3 - { - \exp:w - \@@_case_e:nnTF {#1} {#2} {#3} { } - } -\cs_new:Npn \str_case_e:nnF #1#2 - { - \exp:w - \@@_case_e:nnTF {#1} {#2} { } - } -\cs_new:Npn \str_case_e:nnTF #1#2 - { - \exp:w - \@@_case_e:nnTF {#1} {#2} - } -\cs_new:Npn \@@_case_e:nnTF #1#2#3#4 - { \@@_case_e:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop } -\cs_generate_variant:Nn \str_case_e:nn { e } -\prg_generate_conditional_variant:Nnn \str_case_e:nn { e } { T , F , TF } -\cs_new:Npn \@@_case_e:nw #1#2#3 - { - \str_if_eq:eeTF {#1} {#2} - { \@@_case_end:nw {#3} } - { \@@_case_e:nw {#1} } - } -% \end{macrocode} -% To tidy up the recursion, there are two outcomes. If there was a hit to -% one of the cases searched for, then |#1| is the code to insert, -% |#2| is the \emph{next} case to check on and |#3| is all of -% the rest of the cases code. That means that |#4| is the \texttt{true} -% branch code, and |#5| tidies up the spare \cs{s_@@_mark} and the -% \texttt{false} branch. On the other hand, if none of the cases matched -% then we arrive here using the \enquote{termination} case of comparing -% the search with itself. That means that |#1| is empty, |#2| is -% the first \cs{s_@@_mark} and so |#4| is the \texttt{false} code (the -% \texttt{true} code is mopped up by |#3|). -% \begin{macrocode} -\cs_new:Npn \@@_case_end:nw #1#2#3 \s_@@_mark #4#5 \s_@@_stop - { \exp_end: #1 #4 } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsection{Mapping over strings} -% -% \begin{macro}[rEXP]{\str_map_function:NN, \str_map_function:cN} -% \begin{macro}[rEXP]{\str_map_function:nN} -% \begin{macro}{\str_map_inline:Nn, \str_map_inline:cn} -% \begin{macro}{\str_map_inline:nn} -% \begin{macro}{\str_map_variable:NNn, \str_map_variable:cNn} -% \begin{macro}{\str_map_variable:nNn} -% \begin{macro}{\str_map_break:} -% \begin{macro}{\str_map_break:n} -% \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{__kernel_str_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). -% -% For the \texttt{map_variable} functions we use a string assignment -% to store each character because spaces are made catcode~$12$ before -% the loop. -% \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 - \__kernel_tl_to_str:w {#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 - { - \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 - { - \int_gincr:N \g__kernel_prg_map_int - \cs_gset_protected:cpn - { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2} - \use:e - { - \exp_not:N \@@_map_inline:NN - \exp_not:c { @@_map_ \int_use:N \g__kernel_prg_map_int :w } - \__kernel_str_to_other_fast:n {#1} - } - \q_@@_recursion_tail - \prg_break_point:Nn \str_map_break: - { \int_gdecr:N \g__kernel_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 - { - \@@_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 - { - \use:e - { - \exp_not:n { \@@_map_variable:NnN #2 {#3} } - \__kernel_str_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 - { - \@@_if_recursion_tail_break:NN #3 \str_map_break: - \str_set:Nn #1 {#3} - \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} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[rEXP]{\str_map_tokens:Nn, \str_map_tokens:cn} -% \begin{macro}[rEXP]{\str_map_tokens:nn} -% Uses an auxiliary of \cs{str_map_function:NN}. -% \begin{macrocode} -\cs_new:Npn \str_map_tokens:nn #1#2 - { - \exp_args:Nno \use:nn - { \@@_map_function:w \@@_map_function:nn {#2} } - { \__kernel_tl_to_str:w {#1} } - \q_@@_recursion_tail ? ~ - \prg_break_point:Nn \str_map_break: { } - } -\cs_new:Npn \str_map_tokens:Nn { \exp_args:No \str_map_tokens:nn } -\cs_generate_variant:Nn \str_map_tokens:Nn { c } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsection{Accessing specific characters in a string} -% -% \begin{macro}[EXP]{\__kernel_str_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{s_@@_mark} and \cs{s_@@_stop} markers. The end -% is detected when \cs{@@_to_other_loop:w} finds one of the trailing -% |A|, distinguished from any contents of the initial token list by -% their category. Then \cs{@@_to_other_end:w} is called, and finds -% the result between \cs{s_@@_mark} and the first |A| (well, there is -% also the need to remove a space). -% \begin{macrocode} -\cs_new:Npn \__kernel_str_to_other:n #1 - { - \exp_after:wN \@@_to_other_loop:w - \tl_to_str:n {#1} ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s_@@_mark \s_@@_stop - } -\group_begin: -\tex_lccode:D `\* = `\ % -\tex_lccode:D `\A = `\A % -\tex_lowercase:D - { - \group_end: - \cs_new:Npn \@@_to_other_loop:w - #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \s_@@_stop - { - \if_meaning:w A #8 - \@@_to_other_end:w - \fi: - \@@_to_other_loop:w - #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \s_@@_stop - } - \cs_new:Npn \@@_to_other_end:w \fi: #1 \s_@@_mark #2 * A #3 \s_@@_stop - { \fi: #2 } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[rEXP]{\__kernel_str_to_other_fast:n} -% \begin{macro}[rEXP]{\__kernel_str_to_other_fast_loop:w, \@@_to_other_fast_end:w} -% The difference with \cs{__kernel_str_to_other:n} is that the converted part is -% left in the input stream, making these commands only -% restricted-expandable. -% \begin{macrocode} -\cs_new:Npn \__kernel_str_to_other_fast:n #1 - { - \exp_after:wN \@@_to_other_fast_loop:w \tl_to_str:n {#1} ~ - A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s_@@_stop - } -\group_begin: -\tex_lccode:D `\* = `\ % -\tex_lccode:D `\A = `\A % -\tex_lowercase:D - { - \group_end: - \cs_new:Npn \@@_to_other_fast_loop:w - #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 ~ - { - \if_meaning:w A #9 - \@@_to_other_fast_end:w - \fi: - #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * #9 - \@@_to_other_fast_loop:w * - } - \cs_new:Npn \@@_to_other_fast_end:w #1 * A #2 \s_@@_stop {#1} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP] -% {\str_item:Nn, \str_item:cn, \str_item:nn, \str_item_ignore_spaces:nn} -% \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 -% \cs{str_item_ignore_spaces:nn} function does not escape spaces, -% which are thus ignored by \cs{@@_item:nn} since -% everything else is done with undelimited arguments. -% Evaluate the \meta{index} argument~|#2| and count characters in -% the string, passing those two numbers to \cs{@@_item:w} for -% further analysis. If the \meta{index} is negative, shift it by -% the \meta{count} to know the how many character to discard, and if -% that is still negative give an empty result. If the \meta{index} -% is larger than the \meta{count}, give an empty result, and -% otherwise discard $\meta{index}-1$ characters before returning the -% following one. The shift by $-1$ is obtained by inserting an empty -% brace group before the string in that case: that brace group also -% covers the case where the \meta{index} is zero. -% \begin{macrocode} -\cs_new:Npn \str_item:Nn { \exp_args:No \str_item:nn } -\cs_generate_variant:Nn \str_item:Nn { c } -\cs_new:Npn \str_item:nn #1#2 - { - \exp_args:Nf \tl_to_str:n - { - \exp_args:Nf \@@_item:nn - { \__kernel_str_to_other:n {#1} } {#2} - } - } -\cs_new:Npn \str_item_ignore_spaces:nn #1 - { \exp_args:No \@@_item:nn { \tl_to_str:n {#1} } } -\cs_new:Npn \@@_item:nn #1#2 - { - \exp_after:wN \@@_item:w - \int_value:w \int_eval:n {#2} \exp_after:wN ; - \int_value:w \@@_count:n {#1} ; - #1 \s_@@_stop - } -\cs_new:Npn \@@_item:w #1; #2; - { - \int_compare:nNnTF {#1} < 0 - { - \int_compare:nNnTF {#1} < {-#2} - { \@@_use_none_delimit_by_s_stop:w } - { - \exp_after:wN \@@_use_i_delimit_by_s_stop:nw - \exp:w \exp_after:wN \@@_skip_exp_end:w - \int_value:w \int_eval:n { #1 + #2 } ; - } - } - { - \int_compare:nNnTF {#1} > {#2} - { \@@_use_none_delimit_by_s_stop:w } - { - \exp_after:wN \@@_use_i_delimit_by_s_stop:nw - \exp:w \@@_skip_exp_end:w #1 ; { } - } - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \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 -% \cs{exp:w}. We remove characters $8$ at a time until -% there are at most $8$ to remove. Then we do a dirty trick: the -% \cs{if_case:w} construction leaves between $0$ and $8$ times the -% \cs{or:} control sequence, and those \cs{or:} become arguments of -% \cs{@@_skip_end:NNNNNNNN}. If the number of characters to remove -% is $6$, say, then there are two \cs{or:} left, and the $8$ arguments -% of \cs{@@_skip_end:NNNNNNNN} are the two \cs{or:}, and $6$ -% characters from the input stream, exactly what we wanted to -% remove. Then close the \cs{if_case:w} conditional with \cs{fi:}, and -% stop the initial expansion with \cs{exp_end:} (see places where -% \cs{@@_skip_exp_end:w} is called). -% \begin{macrocode} -\cs_new:Npn \@@_skip_exp_end:w #1; - { - \if_int_compare:w #1 > 8 \exp_stop_f: - \exp_after:wN \@@_skip_loop:wNNNNNNNN - \else: - \exp_after:wN \@@_skip_end:w - \int_value:w \int_eval:w - \fi: - #1 ; - } -\cs_new:Npn \@@_skip_loop:wNNNNNNNN #1; #2#3#4#5#6#7#8#9 - { - \exp_after:wN \@@_skip_exp_end:w - \int_value:w \int_eval:n { #1 - 8 } ; - } -\cs_new:Npn \@@_skip_end:w #1 ; - { - \exp_after:wN \@@_skip_end:NNNNNNNN - \if_case:w #1 \exp_stop_f: \or: \or: \or: \or: \or: \or: \or: \or: - } -\cs_new:Npn \@@_skip_end:NNNNNNNN #1#2#3#4#5#6#7#8 { \fi: \exp_end: } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP] -% {\str_range:Nnn, \str_range:nnn, \str_range_ignore_spaces:nnn} -% \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 -% non-negative and at most the length of the string (this avoids -% needing to check for the end of the string when grabbing -% characters), shifting negative numbers by the appropriate amount. -% Afterwards, skip characters, then keep some more, and finally drop -% the end of the string. -% \begin{macrocode} -\cs_new:Npn \str_range:Nnn { \exp_args:No \str_range:nnn } -\cs_generate_variant:Nn \str_range:Nnn { c } -\cs_new:Npn \str_range:nnn #1#2#3 - { - \exp_args:Nf \tl_to_str:n - { - \exp_args:Nf \@@_range:nnn - { \__kernel_str_to_other:n {#1} } {#2} {#3} - } - } -\cs_new:Npn \str_range_ignore_spaces:nnn #1 - { \exp_args:No \@@_range:nnn { \tl_to_str:n {#1} } } -\cs_new:Npn \@@_range:nnn #1#2#3 - { - \exp_after:wN \@@_range:w - \int_value:w \@@_count:n {#1} \exp_after:wN ; - \int_value:w \int_eval:n { (#2) - 1 } \exp_after:wN ; - \int_value:w \int_eval:n {#3} ; - #1 \s_@@_stop - } -\cs_new:Npn \@@_range:w #1; #2; #3; - { - \exp_args:Nf \@@_range:nnw - { \@@_range_normalize:nn {#2} {#1} } - { \@@_range_normalize:nn {#3} {#1} } - } -\cs_new:Npn \@@_range:nnw #1#2 - { - \exp_after:wN \@@_collect_delimit_by_q_stop:w - \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ; - \exp:w \@@_skip_exp_end:w #1 ; - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \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 -% \meta{index} |#1| and the string count~|#2|. If |#1| is negative, -% replace it by $|#1| + |#2| + 1$, then limit to the range $[0, -% |#2|]$. -% \begin{macrocode} -\cs_new:Npn \@@_range_normalize:nn #1#2 - { - \int_eval:n - { - \if_int_compare:w #1 < \c_zero_int - \if_int_compare:w #1 < -#2 \exp_stop_f: - 0 - \else: - #1 + #2 + 1 - \fi: - \else: - \if_int_compare:w #1 < #2 \exp_stop_f: - #1 - \else: - #2 - \fi: - \fi: - } - } -% \end{macrocode} -% \end{macro} -% \begin{macro}[EXP]{\@@_collect_delimit_by_q_stop:w} -% \begin{macro}[EXP] -% { -% \@@_collect_loop:wn, \@@_collect_loop:wnNNNNNNN, -% \@@_collect_end:wn, \@@_collect_end:nnnnnnnnw -% } -% Collects |max(#1,0)| characters, and removes everything else until -% \cs{s_@@_stop}. This is somewhat similar to \cs{@@_skip_exp_end:w}, but -% accepts integer expression arguments. This time we can only grab -% $7$ characters at a time. At the end, we use an \cs{if_case:w} -% trick again, so that the $8$ first arguments of -% \cs{@@_collect_end:nnnnnnnnw} are some \cs{or:}, followed by an -% \cs{fi:}, followed by |#1| characters from the input stream. Simply -% leaving this in the input stream closes the conditional properly -% and the \cs{or:} disappear. -% \begin{macrocode} -\cs_new:Npn \@@_collect_delimit_by_q_stop:w #1; - { \@@_collect_loop:wn #1 ; { } } -\cs_new:Npn \@@_collect_loop:wn #1 ; - { - \if_int_compare:w #1 > 7 \exp_stop_f: - \exp_after:wN \@@_collect_loop:wnNNNNNNN - \else: - \exp_after:wN \@@_collect_end:wn - \fi: - #1 ; - } -\cs_new:Npn \@@_collect_loop:wnNNNNNNN #1; #2 #3#4#5#6#7#8#9 - { - \exp_after:wN \@@_collect_loop:wn - \int_value:w \int_eval:n { #1 - 7 } ; - { #2 #3#4#5#6#7#8#9 } - } -\cs_new:Npn \@@_collect_end:wn #1 ; - { - \exp_after:wN \@@_collect_end:nnnnnnnnw - \if_case:w \if_int_compare:w #1 > \c_zero_int - #1 \else: 0 \fi: \exp_stop_f: - \or: \or: \or: \or: \or: \or: \fi: - } -\cs_new:Npn \@@_collect_end:nnnnnnnnw #1#2#3#4#5#6#7#8 #9 \s_@@_stop - { #1#2#3#4#5#6#7#8 } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsection{Counting characters} -% -% \begin{macro}[EXP] -% {\str_count_spaces:N, \str_count_spaces:c, \str_count_spaces:n} -% \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 -% \meta{number} is added to the sum of $9$ that precedes, to adjust -% the result. -% \begin{macrocode} -\cs_new:Npn \str_count_spaces:N - { \exp_args:No \str_count_spaces:n } -\cs_generate_variant:Nn \str_count_spaces:N { c } -\cs_new:Npn \str_count_spaces:n #1 - { - \int_eval:n - { - \exp_after:wN \@@_count_spaces_loop:w - \tl_to_str:n {#1} ~ - X 7 ~ X 6 ~ X 5 ~ X 4 ~ X 3 ~ X 2 ~ X 1 ~ X 0 ~ X -1 ~ - \s_@@_stop - } - } -\cs_new:Npn \@@_count_spaces_loop:w #1~#2~#3~#4~#5~#6~#7~#8~#9~ - { - \if_meaning:w X #9 - \@@_use_i_delimit_by_s_stop:nw - \fi: - 9 + \@@_count_spaces_loop:w - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP] -% {\str_count:N, \str_count:c, \str_count:n, \str_count_ignore_spaces:n} -% \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{__kernel_str_to_other:n}, then pass the result to \cs{tl_count:n}. -% However, the escaping step would be quadratic in the number of -% characters in the string, and we can do better. Namely, sum the -% number of spaces (\cs{str_count_spaces:n}) and the result of -% \cs{tl_count:n}, which ignores spaces. Since strings tend to be -% longer than token lists, we use specialized functions to count -% characters ignoring spaces. Namely, loop, grabbing $9$ non-space -% characters at each step, and end as soon as we reach one of the $9$ -% trailing items. The internal function \cs{@@_count:n}, used in -% \cs{str_item:nn} and \cs{str_range:nnn}, is similar to -% \cs{str_count_ignore_spaces:n} but expects its argument to already -% be a string or a string with spaces escaped. -% \begin{macrocode} -\cs_new:Npn \str_count:N { \exp_args:No \str_count:n } -\cs_generate_variant:Nn \str_count:N { c } -\cs_new:Npn \str_count:n #1 - { - \@@_count_aux:n - { - \str_count_spaces:n {#1} - + \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1} - } - } -\cs_new:Npn \@@_count:n #1 - { - \@@_count_aux:n - { \@@_count_loop:NNNNNNNNN #1 } - } -\cs_new:Npn \str_count_ignore_spaces:n #1 - { - \@@_count_aux:n - { \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1} } - } -\cs_new:Npn \@@_count_aux:n #1 - { - \int_eval:n - { - #1 - { X 8 } { X 7 } { X 6 } - { X 5 } { X 4 } { X 3 } - { X 2 } { X 1 } { X 0 } - \s_@@_stop - } - } -\cs_new:Npn \@@_count_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9 - { - \if_meaning:w X #9 - \exp_after:wN \@@_use_none_delimit_by_s_stop:w - \fi: - 9 + \@@_count_loop:NNNNNNNNN - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsection{The first character in a string} -% -% \begin{macro}[EXP] -% {\str_head:N, \str_head:c, \str_head:n, \str_head_ignore_spaces:n} -% \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 -% hands it to \cs{str_head:n}. To circumvent the fact that \TeX{} -% skips spaces when grabbing undelimited macro parameters, -% \cs{@@_head:w} takes an argument delimited by a space. If |#1| -% starts with a non-space character, \cs{@@_use_i_delimit_by_s_stop:nw} -% leaves that in the input stream. On the other hand, if |#1| starts -% with a space, the \cs{@@_head:w} takes an empty argument, and the -% single (initially braced) space in the definition of \cs{@@_head:w} -% makes its way to the output. Finally, for an empty argument, the -% (braced) empty brace group in the definition of \cs{str_head:n} -% gives an empty result after passing through -% \cs{@@_use_i_delimit_by_s_stop:nw}. -% \begin{macrocode} -\cs_new:Npn \str_head:N { \exp_args:No \str_head:n } -\cs_generate_variant:Nn \str_head:N { c } -\cs_new:Npn \str_head:n #1 - { - \exp_after:wN \@@_head:w - \tl_to_str:n {#1} - { { } } ~ \s_@@_stop - } -\cs_new:Npn \@@_head:w #1 ~ % - { \@@_use_i_delimit_by_s_stop:nw #1 { ~ } } -\cs_new:Npn \str_head_ignore_spaces:n #1 - { - \exp_after:wN \@@_use_i_delimit_by_s_stop:nw - \tl_to_str:n {#1} { } \s_@@_stop - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP] -% {\str_tail:N, \str_tail:c, \str_tail:n, \str_tail_ignore_spaces:n} -% \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 -% character, and necessarily makes the test true, since the character -% cannot match \cs{scan_stop:}. The auxiliary function then inserts -% the required \cs{fi:} to close the conditional, and leaves the tail -% of the string in the input stream. The details are such that an -% empty string has an empty tail (this requires in particular that the -% end-marker |X| be unexpandable and not a control sequence). The -% \texttt{_ignore_spaces} is rather simpler: after converting the -% input to a string, \cs{@@_tail_auxii:w} removes one undelimited -% argument and leaves everything else until an end-marker \cs{s_@@_mark}. -% One can check that an empty (or blank) string yields an empty -% tail. -% \begin{macrocode} -\cs_new:Npn \str_tail:N { \exp_args:No \str_tail:n } -\cs_generate_variant:Nn \str_tail:N { c } -\cs_new:Npn \str_tail:n #1 - { - \exp_after:wN \@@_tail_auxi:w - \reverse_if:N \if_charcode:w - \scan_stop: \tl_to_str:n {#1} X X \s_@@_stop - } -\cs_new:Npn \@@_tail_auxi:w #1 X #2 \s_@@_stop { \fi: #1 } -\cs_new:Npn \str_tail_ignore_spaces:n #1 - { - \exp_after:wN \@@_tail_auxii:w - \tl_to_str:n {#1} \s_@@_mark \s_@@_mark \s_@@_stop - } -\cs_new:Npn \@@_tail_auxii:w #1 #2 \s_@@_mark #3 \s_@@_stop { #2 } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsection{String manipulation} -% -% \begin{macro}[EXP] -% { -% \str_casefold:n, \str_casefold:V, -% \str_lowercase:n, \str_lowercase:f, -% \str_uppercase:n, \str_uppercase:f -% } -% \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, \@@_change_case_char_auxi:nN, \@@_change_case_char_auxii:nN} -% \begin{macro}[EXP]{\@@_change_case_codepoint:nN} -% \begin{macro}[EXP]{\@@_change_case_codepoint:nNN} -% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNN} -% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNNN} -% \begin{macro}[EXP]{\@@_change_case_char:nnn, \@@_change_case_char_aux:nnn} -% \begin{macro}[EXP]{\@@_change_case_char:nnnnn} -% Case changing for programmatic reasons is done by first detokenizing -% input then doing a simple loop that only has to worry about spaces -% and everything else. The output is detokenized to allow data sharing -% with text-based case changing. Similarly, for $8$-bit engines the -% multi-byte information is shared. -% \begin{macrocode} -\cs_new:Npn \str_casefold:n #1 { \@@_change_case:nn {#1} { casefold } } -\cs_new:Npn \str_lowercase:n #1 { \@@_change_case:nn {#1} { lowercase } } -\cs_new:Npn \str_uppercase:n #1 { \@@_change_case:nn {#1} { uppercase } } -\cs_generate_variant:Nn \str_casefold:n { V } -\cs_generate_variant:Nn \str_lowercase:n { f } -\cs_generate_variant:Nn \str_uppercase:n { f } -\cs_new:Npn \@@_change_case:nn #1 - { - \exp_after:wN \@@_change_case_aux:nn \exp_after:wN - { \tl_to_str:n {#1} } - } -\cs_new:Npn \@@_change_case_aux:nn #1#2 - { - \@@_change_case_loop:nw {#2} #1 \q_@@_recursion_tail \q_@@_recursion_stop - \@@_change_case_result:n { } - } -\cs_new:Npn \@@_change_case_output:nw #1#2 \@@_change_case_result:n #3 - { #2 \@@_change_case_result:n { #3 #1 } } -\cs_generate_variant:Nn \@@_change_case_output:nw { f } -\cs_new:Npn \@@_change_case_end:wn #1 \@@_change_case_result:n #2 - { \tl_to_str:n {#2} } -\cs_new:Npn \@@_change_case_loop:nw #1#2 \q_@@_recursion_stop - { - \tl_if_head_is_space:nTF {#2} - { \@@_change_case_space:n } - { \@@_change_case_char:nN } - {#1} #2 \q_@@_recursion_stop - } -\exp_last_unbraced:NNNNo - \cs_new:Npn \@@_change_case_space:n #1 \c_space_tl - { - \@@_change_case_output:nw { ~ } - \@@_change_case_loop:nw {#1} - } -\cs_new:Npn \@@_change_case_char:nN #1#2 - { - \@@_if_recursion_tail_stop_do:Nn #2 - { \@@_change_case_end:wn } - \@@_change_case_codepoint:nN {#1} #2 - } -\if_int_compare:w 0 - \cs_if_exist:NT \tex_XeTeXversion:D { 1 } - \cs_if_exist:NT \tex_luatexversion:D { 1 } - > 0 \exp_stop_f: - \cs_new:Npn \@@_change_case_codepoint:nN #1#2 - { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} } -\else: - \cs_new:Npe \@@_change_case_codepoint:nN #1#2 - { - \exp_not:N \int_compare:nNnTF {`#2} > { "80 } - { - \cs_if_exist:NTF \tex_pdftexversion:D - { \exp_not:N \@@_change_case_char_auxi:nN } - { - \exp_not:N \int_compare:nNnTF {`#2} > { "FF } - { \exp_not:N \@@_change_case_char_auxii:nN } - { \exp_not:N \@@_change_case_char_auxi:nN } - } - } - { \exp_not:N \@@_change_case_char_auxii:nN } - {#1} #2 - } - \cs_new:Npn \@@_change_case_char_auxi:nN #1#2 - { - \int_compare:nNnTF {`#2} < { "E0 } - { \@@_change_case_codepoint:nNN } - { - \int_compare:nNnTF {`#2} < { "F0 } - { \@@_change_case_codepoint:nNNN } - { \@@_change_case_codepoint:nNNNNN } - } - {#1} #2 - } - \cs_new:Npn \@@_change_case_char_auxii:nN #1#2 - { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} } - \cs_new:Npn \@@_change_case_codepoint:nNN #1#2#3 - { - \@@_change_case_char:fnn - { \int_eval:n { (`#2 - "C0) * "40 + `#3 - "80 } } - {#1} {#2#3} - } - \cs_new:Npn \@@_change_case_codepoint:nNNN #1#2#3#4 - { - \@@_change_case_char:fnn - { - \int_eval:n - { (`#2 - "E0) * "1000 + (`#3 - "80) * "40 + `#4 - "80 } - } - {#1} {#2#3#4} - } - \cs_new:Npn \@@_change_case_codepoint:nNNNN #1#2#3#4#5 - { - \@@_change_case_char:fnn - { - \int_eval:n - { - (`#2 - "F0) * "40000 - + (`#3 - "80) * "1000 - + (`#4 - "80) * "40 - + `#5 - "80 - } - } - {#1} {#2#3#4#5} - } -\fi: -\cs_new:Npn \@@_change_case_char:nnn #1#2#3 - { - \@@_change_case_output:fw - { - \exp_args:Ne \@@_change_case_char_aux:nnn - { \__kernel_codepoint_case:nn {#2} {#1} } {#1} {#3} - } - \@@_change_case_loop:nw {#2} - } -\cs_generate_variant:Nn \@@_change_case_char:nnn { f } -\cs_new:Npn \@@_change_case_char_aux:nnn #1#2#3 - { - \use:e { \@@_change_case_char:nnnnn #1 {#2} {#3} } - } -\cs_new:Npn \@@_change_case_char:nnnnn #1#2#3#4#5 - { - \int_compare:nNnTF {#1} = {#4} - { \tl_to_str:n {#5} } - { - \codepoint_str_generate:n {#1} - \tl_if_blank:nF {#2} - { - \codepoint_str_generate:n {#2} - \tl_if_blank:nF {#3} - { \codepoint_str_generate:n {#3} } - } - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\str_mdfive_hash:n, \str_mdfive_hash:e} -% \begin{macrocode} -\cs_new:Npn \str_mdfive_hash:n #1 { \tex_mdfivesum:D { \tl_to_str:n {#1} } } -\cs_new:Npn \str_mdfive_hash:e #1 { \tex_mdfivesum:D {#1} } -% \end{macrocode} -% \end{macro} -% -% \begin{variable} -% { -% \c_ampersand_str, -% \c_atsign_str, -% \c_backslash_str, -% \c_left_brace_str, -% \c_right_brace_str, -% \c_circumflex_str, -% \c_colon_str, -% \c_dollar_str, -% \c_hash_str, -% \c_percent_str, -% \c_tilde_str, -% \c_underscore_str, -% \c_zero_str -% } -% For all of those strings, use \cs{cs_to_str:N} to get characters with -% the correct category code without worries -% \begin{macrocode} -\str_const:Ne \c_ampersand_str { \cs_to_str:N \& } -\str_const:Ne \c_atsign_str { \cs_to_str:N \@ } -\str_const:Ne \c_backslash_str { \cs_to_str:N \\ } -\str_const:Ne \c_left_brace_str { \cs_to_str:N \{ } -\str_const:Ne \c_right_brace_str { \cs_to_str:N \} } -\str_const:Ne \c_circumflex_str { \cs_to_str:N \^ } -\str_const:Ne \c_colon_str { \cs_to_str:N \: } -\str_const:Ne \c_dollar_str { \cs_to_str:N \$ } -\str_const:Ne \c_hash_str { \cs_to_str:N \# } -\str_const:Ne \c_percent_str { \cs_to_str:N \% } -\str_const:Ne \c_tilde_str { \cs_to_str:N \~ } -\str_const:Ne \c_underscore_str { \cs_to_str:N \_ } -\str_const:Ne \c_zero_str { 0 } -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\c_empty_str} -% An empty string is simply an empty token list. -% \begin{macrocode} -\cs_new_eq:NN \c_empty_str \c_empty_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_tmpa_str, \l_tmpb_str, \g_tmpa_str, \g_tmpb_str} -% Scratch strings. -% \begin{macrocode} -\str_new:N \l_tmpa_str -\str_new:N \l_tmpb_str -\str_new:N \g_tmpa_str -\str_new:N \g_tmpb_str -% \end{macrocode} -% \end{variable} -% -% \subsection{Viewing strings} -% -% \begin{macro}{\str_show:n, \str_show:N, \str_show:c} -% \begin{macro}{\str_log:n, \str_log:N, \str_log:c} -% Displays a string on the terminal. -% \begin{macrocode} -\cs_new_eq:NN \str_show:n \tl_show:n -\cs_new_protected:Npn \str_show:N #1 - { - \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 } - { \tl_show:N #1 } - } -\cs_generate_variant:Nn \str_show:N { c } -\cs_new_eq:NN \str_log:n \tl_log:n -\cs_new_protected:Npn \str_log:N #1 - { - \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 } - { \tl_log:N #1 } - } -\cs_generate_variant:Nn \str_log:N { c } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macrocode} -% -% \end{macrocode} -% -% \end{implementation} -% -% \PrintIndex -- cgit v1.2.3