diff options
Diffstat (limited to 'Master')
16 files changed, 3208 insertions, 2226 deletions
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf Binary files differindex 9ee837a7c92..0ddd7169a49 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf +++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf Binary files differindex f4b06528802..d82842dca5f 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf +++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf Binary files differindex b20f638d514..796242d76b6 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf +++ b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf Binary files differindex 3cad13a681f..c238f89d86d 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf +++ b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf Binary files differindex 38d903ad9fb..8e77fd9b96f 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf +++ b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx index e2977e2b249..cb0476ac898 100644 --- a/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx @@ -35,7 +35,7 @@ % %<*driver|package> \RequirePackage{expl3} -\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: l3regex.dtx 2943 2011-10-30 15:59:43Z bruno $ {L3 Experimental Regular Expressions} %</driver|package> %<*driver> @@ -68,7 +68,7 @@ % % \begin{documentation} % \newenvironment{l3regex-syntax} -% {\begin{itemize}\def\makelabel##1{\hss\llap{\ttfamily\string##1}}} +% {\begin{itemize}\def\\{\char`\\}\def\makelabel##1{\hss\llap{\ttfamily##1}}} % {\end{itemize}} % % \section{\pkg{l3regex} documentation} @@ -91,15 +91,13 @@ % by \enquote{\texttt{is}}. A more complicated example is % a pattern to add a comma at the end of each word: % \begin{verbatim} -% \regex_replace_all:nnN { (\w+) } { \1 , } \l_my_str +% \regex_replace_all:nnN { \w+ } { \0 , } \l_my_str % \end{verbatim} % The |\w| sequence represents any \enquote{word} character, -% and |*| indicates that the |\w| sequence should be repeated -% as many times as possible, hence matching a word in the -% input string. The parentheses \enquote{capture} what their -% contents matched in the input string, and this can be used -% in the replacement text as |\1| (and higher numbers if several -% groups are used in the regular expression). +% and |+| indicates that the |\w| sequence should be repeated +% as many times as possible (at least once), hence matching a word in the +% input string. In the replacement text, |\0| denotes the full match +% (here, a word). % % \subsection{Syntax of regular expressions} % @@ -111,13 +109,11 @@ % \begin{itemize} % \item every alphanumeric character (\texttt{A}--\texttt{Z}, % \texttt{a}--\texttt{z}, \texttt{0}--\texttt{9}) matches -% exactly itself, none of them should be escaped, because -% most of those escape sequences have special meanings; -% \item non-alphanumeric printable ascii characters can always -% be safely escaped, and for highest portability, they should -% always be escaped (this avoids problems if some currently -% \enquote{normal} characters is given a meaning in later -% releases); +% exactly itself, and should not be escaped, because +% |\A|, |\B|, \ldots{} have special meanings; +% \item non-alphanumeric printable ascii characters can (and should) +% always be escaped: many of them have special meanings (\emph{e.g.}, +% |(|, |)|, |?|, |.|); % \item spaces should always be escaped (even in character % classes); % \item any other character may be escaped or not, without any @@ -138,88 +134,97 @@ % % Characters. % \begin{l3regex-syntax} -% \item[\x\{hh\ldots{}\}] -% Character with hex code \texttt{hh\ldots{}} -% \item[\xhh] -% Character with hex code \texttt{hh}. -% \item[\a] Alarm (hex 07). -% \item[\e] Escape (hex 1B). -% \item[\f] Form-feed (hex 0C). -% \item[\n] New line (hex 0A). -% \item[\r] Carriage return (hex 0D). -% \item[\t] Horizontal tab (hex 09). +% \item[\\x\{hh\ldots{}\}] Character with hex code \texttt{hh\ldots{}} +% \item[\\xhh] Character with hex code \texttt{hh}. +% \item[\\a] Alarm (hex 07). +% \item[\\e] Escape (hex 1B). +% \item[\\f] Form-feed (hex 0C). +% \item[\\n] New line (hex 0A). +% \item[\\r] Carriage return (hex 0D). +% \item[\\t] Horizontal tab (hex 09). % \end{l3regex-syntax} % % Character types. % \begin{l3regex-syntax} -% \item[.] A single period matches any character, -% including newlines.\footnote{Should that be changed?} -% \item[\N] A character that is not -% the |\n| character (hex 0A).\footnote{Is that right?} -% \item[\d] Any decimal digit. -% \item[\h] Any horizontal space character, -% equivalent to |[\^^I\ ]|: space and tab. -% \item[\s] Any space character, -% equivalent to |[\^^I\^^J\^^L\^^M\ ]|. -% \item[\v] Any vertical space character, -% equivalent to |[\^^I-\^^M]|. Note that |\^^K| is a vertical space, -% but not a space, for compatibility with perl. -% \item[\w] Any word character, \emph{i.e.}, -% alpha-numerics and underscore, equivalent to |[A-Za-z0-9\_]|. -% \item[\D] Any character not matched by |\d|. -% \item[\H] Any character not matched by |\h|. -% \item[\S] Any character not matched by |\s|. -% \item[\V] Any character not matched by |\v|. -% \item[\W] Any character not matched by |\w|. +% \item[.] A single period matches any character, including newlines. +% \item[\\d] Any decimal digit. +% \item[\\h] Any horizontal space character, +% equivalent to |[\ \^^I]|: space and tab. +% \item[\\s] Any space character, +% equivalent to |[\ \^^I\^^J\^^L\^^M]|. +% \item[\\v] Any vertical space character, +% equivalent to |[\^^J\^^K\^^L\^^M]|. Note that |\^^K| is a vertical space, +% but not a space, for compatibility with Perl. +% \item[\\w] Any word character, \emph{i.e.}, +% alpha-numerics and underscore, equivalent to |[A-Za-z0-9\_]|. +% \item[\\D] Any character not matched by |\d|. +% \item[\\H] Any character not matched by |\h|. +% \item[\\N] Any character other than the |\n| character (hex 0A). +% \item[\\S] Any character not matched by |\s|. +% \item[\\V] Any character not matched by |\v|. +% \item[\\W] Any character not matched by |\w|. % \end{l3regex-syntax} % % Character classes match exactly one character in the subject string. % \begin{l3regex-syntax} -% \item[{[...]}] Positive character class. -% \item[{[\string^...]}] Negative character class. -% \item[{[x-y]}] Range (can be used with escaped characters). +% \item[{[\ldots{}]}] Positive character class. +% Matches any of the specified characters. +% \item[{[\char`\^\ldots{}]}] Negative character class. +% Matches any character other than the specified characters. +% \item[{[x-y]}] Range (can be used with escaped characters). % \end{l3regex-syntax} % -% Quantifiers. +% Quantifiers (repetition). % \begin{l3regex-syntax} -% \item[?] $0$ or $1$, greedy. -% \item[??] $0$ or $1$, lazy. -% \item[*] $0$ or more, greedy. -% \item[*?] $0$ or more, lazy. -% \item[+] $1$ or more, greedy. -% \item[+?] $1$ or more, lazy. -% \end{l3regex-syntax} -% Not implemented yet: ^^A ! -% \begin{l3regex-syntax}\def\makelabel#1{\hss\llap{\ttfamily#1}} -% \item[\{$n$\}] Exactly $n$. -% \item[\{$n,$\}] $n$ or more, greedy. -% \item[\{$n,$\}?] $n$ or more, lazy. -% \item[\{$n,m$\}] At least $n$, no more than $m$, greedy. -% \item[\{$n,m$\}?] At least $n$, no more than $m$, lazy. +% \item[?] $0$ or $1$, greedy. +% \item[??] $0$ or $1$, lazy. +% \item[*] $0$ or more, greedy. +% \item[*?] $0$ or more, lazy. +% \item[+] $1$ or more, greedy. +% \item[+?] $1$ or more, lazy. +% \item[\{$n$\}] Exactly $n$. +% \item[\{$n,$\}] $n$ or more, greedy. +% \item[\{$n,$\}?] $n$ or more, lazy. +% \item[\{$n,m$\}] At least $n$, no more than $m$, greedy. +% \item[\{$n,m$\}?] At least $n$, no more than $m$, lazy. % \end{l3regex-syntax} % % Anchors and simple assertions. % \begin{l3regex-syntax} -% \item[\b] Word boundary. -% \item[\B] Not a word boundary. -% \item[^\string\A] Start of the subject string.\footnote{The -% multiline mode is not implemented yet, so those are currently -% identical.} -% \item[$\string\Z\string\z] End of the subject -% string.\footnotemark[\thefootnote] -% \item[\G] Start of the current match. This is only different from |^| -% in the case of multiple matches: for instance -% |\regex_count:nnN { \G a } { aaba } \l_tmpa_int| yields $2$, but -% replacing |\G| by |^| would result in \cs{l_tmpa_int} holding the -% value $1$. +% \item[\\b] Word boundary. +% \item[\\B] Not a word boundary. +% \item[\char`^ \textrm{or} \\A] +% Start of the subject string. +% \item[\char`$\textrm{,} \\Z \textrm{or} \\z] +% End of the subject string. +% \item[\\G] Start of the current match. This is only different from |^| +% in the case of multiple matches: for instance +% |\regex_count:nnN { \G a } { aaba } \l_tmpa_int| yields $2$, but +% replacing |\G| by |^| would result in \cs{l_tmpa_int} holding the +% value $1$. % \end{l3regex-syntax} % % Alternation and capturing groups. % \begin{l3regex-syntax} -% \item[A\string|B\string|C] Either one of \texttt{A}, \texttt{B}, -% or \texttt{C}. -% \item[(\ldots{})] Capturing group. -% \item[(?:\ldots{})] Non-capturing group. +% \item[A\char`|B\char`|C] Either one of \texttt{A}, \texttt{B}, +% or \texttt{C}. +% \item[(\ldots{})] Capturing group. +% \item[(?:\ldots{})] Non-capturing group. +% \end{l3regex-syntax} +% +% Options can be set with |(?|\meta{option}|)| and +% unset with |(?-|\meta{option}|)|. Options are local +% to the group in which they are set, and revert to their +% previous setting upon reaching the closing parenthesis. +% For instance, in \verb"(?i)a(b(?-i)c|d)e", the |i| option +% applies to the letters |a|, |b| and |e|. +% \begin{l3regex-syntax} +% \item[(?i) \textrm{and} (?-i)] Toggle to a case +% insensitive/sensitive mode. This only applies to ascii letters +% (mapping \texttt{A}--\texttt{Z} to \texttt{a}--\texttt{z}). +% For instance, |(?i)[Y-\\]| matches the characters |Y|, |Z|, |[|, +% |\|, and the lower case letters |y| and |z|, while |(?i)[^aeiou]| +% matches any character which is not a vowel. % \end{l3regex-syntax} % % In character classes, only |^|, |-|, |]|, |\| and spaces are special, @@ -235,29 +240,23 @@ % opening parenthesis, starting at $1$. The contents of those groups % corresponding to the \enquote{best} match (leftmost longest) % can be extracted and stored in a sequence of strings using for -% instance \cs{regex_extract:nnNTF}. +% instance \cs{regex_extract_once:nnNTF}. % % \subsection{Syntax of in the replacement text} % % Most of the features described in regular expressions do not make sense % within the replacement text. Escaped characters are supported as inside -% regular expressions. The various submatches are accessed with |\1|, |\2|, -% \emph{etc.}, and the whole match is accessed using |\0|. +% regular expressions. The whole match is accessed as |\0|, and the first +% $9$ submatches are accessed as |\1|, \ldots{}, |\9|. Submatches with +% numbers higher than $9$ are accessed as |\g{|\meta{number}|}| instead. % % For instance, % \begin{verbatim} -% \str_set:Nn \l_my_str { Hello,~world! } -% \regex_replace_all:nnN { ([er]?l|o) . } { \(\0\-\-\1\) } \l_my_str +% \str_set:Nn \l_my_str { Hello,~world! } +% \regex_replace_all:nnN { ([er]?l|o) . } { \(\0\-\-\1\) } \l_my_str % \end{verbatim} % results in \cs{l_my_str} holding |H(ell--el)(o,--o) w(or--o)(ld--l)!| % -% Submatches with numbers higher than $10$ are accessed in the same way, -% namely |\10|, |\11|, \emph{etc}. To insert in the replacement text -% a submatch followed by a digit, the digit must be entered using the -% |\x| escape sequence: for instance, to get the first submatch followed -% by the digit $7$, use |\1\x37|, because $7$ has character code |37| -% (in hexadecimal). -% % \subsection{Precompiling regular expressions} % % If a regular expression is to be used several times, @@ -268,8 +267,7 @@ % regular expression argument either as an explicit string % or as a precompiled regular expression. % -% \begin{function}{\regex_set:Nn} -% \begin{function}{\regex_gset:Nn} +% \begin{function}{\regex_set:Nn, \regex_gset:Nn} % \begin{syntax} % \cs{regex_set:Nn} \meta{tl var} \Arg{regex} % \end{syntax} @@ -280,20 +278,23 @@ % \tl_new:N \l_my_regex_tl % \regex_set:Nn \l_my_regex_tl { my\ (simple\ )? reg(ex|ular\ expression) } % \end{verbatim} -% The assignment is local for \cs{regex_set:Nn} -% and global for \cs{regex_gset:Nn}. +% The assignment is local for \cs{regex_set:Nn} and global for +% \cs{regex_gset:Nn}. % \begin{texnote} % Precompiled regular expressions can be safely written to a file % and read when the \LaTeX3 syntax is active (as triggered by % \cs{ExplSyntaxOn}). % \end{texnote} % \end{function} -% \end{function} % % \subsection{String matching} % -% \begin{function}[TF]{\regex_match:nn} -% \begin{function}[TF]{\regex_match:Nn} +% All regular expression functions are available in both |:n| and |:N| +% variants. The former require a \enquote{standard} regular expression, +% while the later require a precompiled expression as generated by +% \cs{regex_(g)set:Nn}. +% +% \begin{function}[TF]{\regex_match:nn, \regex_match:Nn} % \begin{syntax} % \cs{regex_match:nnTF} \Arg{regex} \Arg{string} \Arg{true code} \Arg{false code} % \end{syntax} @@ -303,16 +304,15 @@ % \regex_match:nnTF { b [cde]* } { abecdcx } { TRUE } { FALSE } % \regex_match:nnTF { [b-dq-w] } { example } { TRUE } { FALSE } % \end{verbatim} -% leaves \texttt{TRUE FALSE} in the input stream. -% \end{function} +% leaves \texttt{TRUE} then \texttt{FALSE} in the input stream. % \end{function} % -% \begin{function}{\regex_count:nnN} -% \begin{function}{\regex_count:NnN} +% \begin{function}{\regex_count:nnN, \regex_count:NnN} % \begin{syntax} % \cs{regex_count:nnN} \Arg{regex} \Arg{string} \meta{int var} % \end{syntax} -% Sets \meta{int var} equal to the number of times +% Sets \meta{int var} within the current \TeX{} group level +% equal to the number of times % \meta{regular expression} appears in \meta{string}. % The search starts by finding the left-most longest match, % respecting greedy and ungreedy operators. Then the search @@ -325,29 +325,26 @@ % \end{verbatim} % results in \cs{l_foo_int} taking the value $5$. % \end{function} -% \end{function} % % \subsection{Submatch extraction} % -% \begin{function}{\regex_extract:nnN} -% \begin{function}{\regex_extract:NnN} -% \begin{function}[TF]{\regex_extract:nnN} -% \begin{function}[TF]{\regex_extract:NnN} +% \begin{function}[TF]{\regex_extract_once:nnN, \regex_extract_once:NnN} % \begin{syntax} -% \cs{regex_extract:nnNTF} \Arg{regex} \Arg{string} \meta{seq~var} \Arg{true code} \Arg{false code} +% \cs{regex_extract_once:nnN} \Arg{regex} \Arg{string} \meta{seq~var} +% \cs{regex_extract_once:nnNTF} \Arg{regex} \Arg{string} \meta{seq~var} \Arg{true code} \Arg{false code} % \end{syntax} % Finds the first match of the \meta{regular expression} % in the \meta{string}. If it exists, the match is stored % as the zeroeth item of the \meta{seq~var}, and further % items are the contents of capturing groups, in the order -% of their opening left parenthesis. The \meta{seq~var} +% of their opening parenthesis. The \meta{seq~var} % is assigned locally. If there is no match, % the \meta{seq~var} is not altered. -% The testing versions return \meta{true} if a match was found, -% and \meta{false} otherwise. +% The testing versions insert the \meta{true code} into the input +% stream if a match was found, and the \meta{false code} otherwise. % For instance, assume that you type % \begin{verbatim} -% \regex_extract:nnNTF { ^(La)?TeX(!*)$ } { LaTeX!!! } +% \regex_extract_once:nnNTF { ^(La)?TeX(!*)$ } { LaTeX!!! } % \l_foo_seq { true } { false } % \end{verbatim} % Then the regular expression (anchored at the start with |^| and @@ -357,14 +354,26 @@ % the items |{LaTeX!!!}|, |{La}|, and |{!!!}|, and the \texttt{true} % branch is left in the input stream. % \end{function} -% \end{function} -% \end{function} +% +% \begin{function}[TF]{\regex_extract_all:nnN, \regex_extract_all:NnN} +% \begin{syntax} +% \cs{regex_extract_all:nnNTF} \Arg{regex} \Arg{string} \meta{seq~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Finds all matches of the \meta{regular expression} +% in the \meta{string}, and stores all the submatch information +% in a single sequence (concatenating the results of +% multiple \cs{regex_extract_once:nnN} calls). +% For instance, assume that you type +% \begin{verbatim} +% \regex_extract_all:nnN { \w+ } { Hello,~world! } \l_foo_seq +% \end{verbatim} +% Then the regular expression will match twice, and the resulting +% sequence contains the two items \texttt{Hello} and \texttt{world}. % \end{function} % % \subsection{String splitting} % -% \begin{function}{\regex_split:nnN} -% \begin{function}{\regex_split:NnN} +% \begin{function}{\regex_split:nnN, \regex_split:NnN} % \begin{syntax} % \cs{regex_split:nnN} \Arg{regular expression} \meta{string} \meta{seq~var} % \end{syntax} @@ -375,15 +384,18 @@ % If no match is found the resulting \meta{seq~var} has the \meta{string} as % its sole item. If the \meta{regular expression} matches the empty string, % then the \meta{string} is split into single character substrings. -% \end{function} +% For example, after +% \begin{verbatim} +% \seq_new:N \l_path_seq +% \regex_split:nnN { / } { the/path/for/this/file.tex } \l_path_seq +% \end{verbatim} +% the sequence |\l_path_seq| contains the items |{the}|, |{path}|, +% |{for}|, |{this}|, and |{file.tex}|. % \end{function} % % \subsection{String replacement} % -% \begin{function}{\regex_replace_once:nnN} -% \begin{function}{\regex_replace_once:NnN} -% \begin{function}[TF]{\regex_replace_once:nnN} -% \begin{function}[TF]{\regex_replace_once:NnN} +% \begin{function}[TF]{\regex_replace_once:nnN,\regex_replace_once:NnN} % \begin{syntax} % \cs{regex_replace_once:nnN} \Arg{regular expression} \Arg{replacement} \meta{str~var} % \end{syntax} @@ -393,12 +405,8 @@ % |\0| represents the full match, |\1| represent the contents % of the first capturing group, |\2| of the second, \emph{etc.} % \end{function} -% \end{function} -% \end{function} -% \end{function} % -% \begin{function}{\regex_replace_all:nnN} -% \begin{function}{\regex_replace_all:NnN} +% \begin{function}{\regex_replace_all:nnN, \regex_replace_all:NnN} % \begin{syntax} % \cs{regex_replace_all:nnN} \Arg{regular expression} \Arg{replacement} \meta{str~var} % \end{syntax} @@ -407,53 +415,42 @@ % |\0| represents the full match, |\1| % represent the contents of the first capturing group, % |\2| of the second, \emph{etc.} Every match -% is treated independently. The result is assigned -% locally to \meta{str~var}. -% \end{function} +% is treated independently, and matches cannot overlap. +% The result is assigned locally to \meta{str~var}. % \end{function} % % \subsection{Bugs, misfeatures, future work, and other possibilities} % -% The \texttt{\{\}} quantifiers are not implemented. -% -% The following need discussion. +% The following need to be done now. % \begin{itemize} +% \item The \texttt{\{\}} quantifiers are only partially implemented. +% \item Space's catcode wrong in the result of a replacement! % \item Newline conventions are not done. % In particular, |.| should not match newlines. % Also, |\A| should differ from |^|, and |\Z|, |\z| and |$| should % differ. -% \item Caseless matching and more generally -% |(*..)| and |(?..)| sequences to set some options. -% \item General look-ahead/behind assertions. -% \item Idea of |#| as a synonym of |.*?|. -% \item Do we need a facility for balanced groups? (That's non-regular.) % \end{itemize} % % The following features are likely to be implemented at some point % in the future. % \begin{itemize} +% \item General look-ahead/behind assertions. +% \item Regex matching on external files. +% \item Conditional subpatterns with look ahead/behind: \enquote{if +% what follows is [\ldots{}], then [\ldots{}]}. +% \item \verb"(?|..|..)" to reset the capturing group number at the start +% of each alternative. +% \item |(*..)| and |(?..)| sequences to set some options. +% \item Reduce the number of epsilon-transitions in alternatives. % \item Optimize simple strings: use less states % (|abcade| should give two states, for |abc| and |ade|). % \item Optimize groups with no alternative. % \item Optimize the use of \cs{prg_stepwise_...} functions. -% \item Implement regex matching on external files. -% \item Conditional subpatterns with look ahead/behind: \enquote{if -% what follows is [\ldots{}], then [\ldots{}]}. -% \item "(?|..|..)" to reset the capturing group number at the start -% of each alternative. -% \item Todo: check space's catcode in the result of a replacement. +% \item |\K| for resetting the beginning of the match. +% \item UTF-8 mode for pdf\TeX{}. % \end{itemize} % -% The following features not present in PCRE nor perl (because they can -% be done in other ways in those languages) might be added. -% \begin{itemize} -% \item |#| as a synonym of |.*?|, in a way similar to macro parameters. -% \item Facility to match balanced groups: this is non-regular, but quite -% useful, and it can be implemented without too much performance loss. -% (\emph{cf.} callout?) -% \end{itemize} -% -% The following features of PCRE or perl will probably not be implemented. +% The following features of PCRE or Perl will probably not be implemented. % \begin{itemize} % \item Other forms of escapes, and character classes. % |\cx|$\simeq$|\^^x|, |\ddd| (octal \texttt{ddd}). @@ -465,7 +462,7 @@ % or look-behind condition): this is non-regular, isn't it? % \end{itemize} % -% The following features of PCRE or perl will not be implemented. +% The following features of PCRE or perl will definitely not be implemented. % \begin{itemize} % \item Comments: \TeX{} already has its own system for comments. % \item Named subpatterns: \TeX{} programmers have lived so far without @@ -482,9 +479,9 @@ % \item Back-references: non-regular feature, this requires backtracking, % which is prohibitively slow. % \item Backtracking control verbs: intrinsically tied to backtracking. -% \item |\K| for resetting the beginning of the match: tied to backtracking. -% \item |\C| single byte in UTF-8 mode: well, UTF-8 mode is not -% implemented, and that seems like a very rarely useful feature anyways. +% \item |\C| single byte in UTF-8 mode: Xe\TeX{} and Lua\TeX{} serve +% us characters directly, and splitting those into bytes is tricky, +% encoding dependent, and most likely not useful anyways. % \end{itemize} % % \end{documentation} @@ -521,9 +518,28 @@ % % \subsection{Constants and variables} % +% \begin{variable} +% { +% \c_forty_eight, \c_fifty_eight, \c_sixty_five, \c_ninety_one, +% \c_ninety_seven, \c_one_hundred_twenty_three, +% \c_one_hundred_twenty_seven +% } +% We declare here some integer values which delimit ranges of +% ASCII characters of various types. +% \begin{macrocode} +\int_const:Nn \c_forty_eight { 48 } +\int_const:Nn \c_fifty_eight { 58 } +\int_const:Nn \c_sixty_five { 65 } +\int_const:Nn \c_ninety_one { 91 } +\int_const:Nn \c_ninety_seven { 97 } +\int_const:Nn \c_one_hundred_twenty_three { 123 } +\int_const:Nn \c_one_hundred_twenty_seven { 127 } +% \end{macrocode} +% \end{variable} +% % \begin{macro}{\regex_tmp:w} -% \begin{variable}{\g_regex_tmpa_tl,\l_regex_tmpa_tl,\l_regex_tmpb_tl} -% \begin{variable}{\l_regex_tmpa_int} +% \begin{variable}{\g_regex_tmpa_tl, \l_regex_tmpa_tl, \l_regex_tmpb_tl} +% \begin{variable}{\l_regex_tmpa_int, \l_regex_tmpb_int} % Temporary variables. % \begin{macrocode} \cs_new:Npn \regex_tmp:w { } @@ -531,6 +547,7 @@ \tl_new:N \l_regex_tmpa_tl \tl_new:N \l_regex_tmpb_tl \int_new:N \l_regex_tmpa_int +\int_new:N \l_regex_tmpb_int % \end{macrocode} % \end{variable} % \end{variable} @@ -538,16 +555,8 @@ % % \subsubsection{Variables used while building} % -% \begin{variable}{\l_regex_pattern_str} -% The \meta{pattern} is stored in a string variable. -% \begin{macrocode} -\tl_new:N \l_regex_pattern_str -% \end{macrocode} -% \end{variable} -% % \begin{variable}{\l_regex_max_state_int} -% \begin{variable}{\l_regex_left_state_int,\l_regex_right_state_int} -% \begin{variable}{\l_regex_left_state_seq,\l_regex_right_state_seq} +% \begin{variable}{\l_regex_left_state_int, \l_regex_right_state_int} % The last state that was allocated is stored in \cs{l_regex_max_state_int}, % and \cs{l_regex_left/right_state_int} point to both end-points of the % last group (which any quantifier would repeat). For simple strings of @@ -556,21 +565,29 @@ \int_new:N \l_regex_max_state_int \int_new:N \l_regex_left_state_int \int_new:N \l_regex_right_state_int -\seq_new:N \l_regex_left_state_seq -\seq_new:N \l_regex_right_state_seq % \end{macrocode} % \end{variable} % \end{variable} +% +% \begin{variable}{\l_regex_group_begin_seq, \l_regex_group_end_seq} +% Alternatives are implemented by branching from a state into the +% various choices, then merging those into another state. We store +% information about those states in two sequences (several can be +% active at the same time when nesting groups). +% \begin{macrocode} +\seq_new:N \l_regex_group_begin_seq +\seq_new:N \l_regex_group_end_seq +% \end{macrocode} % \end{variable} % % \begin{variable}{\l_regex_capturing_group_int} % \begin{variable}{\l_regex_capturing_group_seq} -% \cs{l_regex_capturing_group_int} is the id number of the current +% \cs{l_regex_capturing_group_int} is the ID number of the current % capturing group, starting at $0$ for a group enclosing the full % regular expression, and counting in the order of their left parenthesis. % This number is used when a branch of the alternation ends. % Capturing groups can be arbitrarily nested, and we keep track of -% the stack of id numbers in \cs{l_regex_capturing_group_seq}. +% the stack of ID numbers in \cs{l_regex_capturing_group_seq}. % \begin{macrocode} \int_new:N \l_regex_capturing_group_int \seq_new:N \l_regex_capturing_group_seq @@ -588,33 +605,17 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{l_regex_repetition_int} -% Used when to distinguish threads at the same state but with -% different repetitions of some quantifiers. -% \begin{macrocode} -\int_new:N \l_regex_repetition_int -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_regex_look_behind_bool} -% \begin{variable}{\l_regex_look_behind_str} -% The boolean \cs{l_regex_look_behind_bool} indicates whether -% a look-behind assertion appears within the regular expression -% (currently only |\b| or |\B|). When matching, we will keep -% track of the part of the string before the current character -% in \cs{l_regex_look_behind_str}, stored backwards. -% \begin{macrocode} -\bool_new:N \l_regex_look_behind_bool -\tl_new:N \l_regex_look_behind_str -% \end{macrocode} -% \end{variable} -% \end{variable} -% % \subsubsection{Character classes} % % \begin{macro}{\regex_build_tmp_class:n} % \begin{variable}{\l_regex_class_bool,\l_regex_class_tl} -% Used when building character classes. +% \cs{l_regex_class_bool} is false for negative character classes. +% \cs{l_regex_class_tl} holds the tests which should be performed +% to decide whether the \cs{l_regex_current_char_int} matches that +% character class. Once the class is read completely, the full +% instructions are stored in \cs{regex_build_tmp_class:n}, whose +% argument is the target state if the test succeeds. It is also used +% for all other single-character situations (\emph{e.g.}, |\d|, or |.|). % \begin{macrocode} \cs_new_eq:NN \regex_build_tmp_class:n \use_none:n \bool_new:N \l_regex_class_bool @@ -633,73 +634,73 @@ % are recognized by |\d|, |\D|, |\w|, \emph{etc.} % in regular expressions. Namely, |\d=[0-9]|, % |\w=[0-9A-Z_a-z]|, |\s=[\ \^^I\^^J\^^L\^^M]|, -% |\h=[\ \^^I]|, |\v=[\^^J-\^^M]|, and the upper-case -% counterparts match anything that the lowercase +% |\h=[\ \^^I]|, |\v=[\^^J-\^^M]|, and the upper case +% counterparts match anything that the lower case % does not match. % The order in which the various ranges appear is -% optimized for usual mostly lowercase letter text. +% optimized for usual mostly lower case letter text. % \begin{macrocode} \tl_const:Nn \c_regex_d_tl { - \regex_item_range:nn {48} {57} % 0--9 + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 } \tl_const:Nn \c_regex_D_tl { - \regex_item_more:n {57} % 9 - \regex_item_range:nn {0} {47} % 0 + \regex_item_geq:n { \c_fifty_eight } % > `9 + \regex_item_range:nn { \c_zero } { 47 } % 0 } \tl_const:Nn \c_regex_h_tl { - \regex_item_equal:n {32} % space - \regex_item_equal:n {9} % tab + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_equal:n { \c_nine } % tab } \tl_const:Nn \c_regex_H_tl { - \regex_item_neq:n {32} % space - \regex_item_neq:n {9} % tab - \regex_break_true:w + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_ten } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab } \tl_const:Nn \c_regex_s_tl { - \regex_item_equal:n {32} % space - \regex_item_neq:n {11} % vtab - \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_range:nn { \c_nine } { \c_ten } % tab, lf + \regex_item_range:nn { \c_twelve } { \c_thirteen } % ff, cr } \tl_const:Nn \c_regex_S_tl { - \regex_item_more:n {32} % > space - \regex_item_range:nn {14} {31} % tab < ... < space - \regex_item_range:nn {0} {8} % < tab - \regex_item_equal:n {11} % vtab + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_fourteen } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab + \regex_item_equal:n { \c_eleven } % vtab } \tl_const:Nn \c_regex_v_tl { - \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + \regex_item_range:nn { \c_ten } { \c_thirteen } % lf, vtab, ff, cr } \tl_const:Nn \c_regex_V_tl { - \regex_item_more:n {13} % cr - \regex_item_range:nn {0} {9} % < lf + \regex_item_geq:n { \c_fourteen } % >cr + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } \tl_const:Nn \c_regex_w_tl { - \regex_item_range:nn {97} {122} % a--z - \regex_item_range:nn {65} {90} % A--Z - \regex_item_range:nn {48} {57} % 0--9 - \regex_item_equal:n {95} % _ + \regex_item_range:nn { \c_ninety_seven } { 122 } % a--z + \regex_item_range:nn { \c_sixty_five } { 90 } % A--Z + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 + \regex_item_equal:n { 95 } % _ } \tl_const:Nn \c_regex_W_tl { - \regex_item_range:nn {0} {47} % <`0 - \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) - \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) - \regex_item_equal:n {96} % ` - \regex_item_more:n {122} % z + \regex_item_range:nn { \c_zero } { 47 } % <`0 + \regex_item_range:nn { \c_fifty_eight } { 64 } % (`9+1)--(`A-1) + \regex_item_range:nn { \c_ninety_one } { 94 } % (`Z+1)--(`_-1) + \regex_item_equal:n { 96 } % ` + \regex_item_geq:n { \c_one_hundred_twenty_three } % z } \tl_const:Nn \c_regex_N_tl { - \regex_item_neq:n {10} % lf - \regex_break_true:w + \regex_item_geq:n { \c_eleven } % > lf + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } % \end{macrocode} % \end{variable} @@ -711,43 +712,69 @@ % % \subsubsection{Variables used when matching} % -% \begin{variable}{\l_regex_query_str} +% \begin{variable}{\l_regex_query_other_str} % The string that is being matched. +% In that string, spaces have category code \enquote{other}. % \begin{macrocode} -\tl_new:N \l_regex_query_str +\tl_new:N \l_regex_query_other_str % \end{macrocode} % \end{variable} % +% \begin{variable}{\l_regex_current_step_int} % \begin{variable}{\l_regex_start_step_int} -% In the case of multiple matches, \cs{l_regex_start_step_int} -% is equal to the position where the current match -% attempt began. +% \begin{variable}{\l_regex_success_step_int} +% While reading through the query string, \cs{l_regex_current_step_int} +% is the position in the string. Each match begins at the position +% given by \cs{l_regex_start_step_int}. Whenever an execution thread +% succeeds, the corresponding step is stored into +% \cs{l_regex_success_step_int}, which will be the next starting step +% (except in the case of empty matches). % \begin{macrocode} +\int_new:N \l_regex_current_step_int \int_new:N \l_regex_start_step_int +\int_new:N \l_regex_success_step_int +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_regex_unique_step_int} +% In the case of repeated matches, \cs{l_regex_current_step_int} +% is reset to the end-position of the previous match. In contrast, +% \cs{l_regex_unique_step_int} is simply incremented to provide +% a unique number for each iteration of the matching loop. This +% is handy to attach each set of submatch information to a given +% iteration (and automatically discard it when it corresponds to +% a past iteration). +% \begin{macrocode} +\int_new:N \l_regex_unique_step_int % \end{macrocode} % \end{variable} % % \begin{variable}{\l_regex_current_char_int} -% \begin{variable}{\l_regex_current_step_int} -% The character at the current position in the string, -% and the current position. +% \begin{variable}{\l_regex_last_char_int} +% \begin{variable}{\l_regex_case_changed_char_int} +% The character codes of the character at the current position +% in the string, and at the previous position, and the current +% character with its case changed (|A-Z|$\leftrightarrow$|a-z|). +% The \cs{l_regex_last_char_int} is used to test for word boundaries +% (|\b| and |\B|). The \cs{l_regex_case_changed_char_int} is +% only computed if the \enquote{case insensitive} option |(?i)| +% is used in the regex. % \begin{macrocode} \int_new:N \l_regex_current_char_int -\int_new:N \l_regex_current_step_int +\int_new:N \l_regex_last_char_int +\int_new:N \l_regex_case_changed_char_int % \end{macrocode} % \end{variable} % \end{variable} +% \end{variable} % -% \begin{variable}{\l_regex_unique_step_int} -% This gives a unique identifier to every step in the loop over -% characters in the string. In the case of a single match, it is -% equal to \cs{l_regex_current_step_int}, but for multiple matches, -% it is not reset to the start of the next match, but incremented. -% A unique identifier is handy when considering whether a state of -% the automaton has been visited at that step or not, and when -% tracking submatch information. +% \begin{variable}{\l_regex_caseless_bool} +% True if caseless matching is used within the regular expression. +% This controls whether \cs{l_regex_case_changed_char_int} is computed. % \begin{macrocode} -\int_new:N \l_regex_unique_step_int +\bool_new:N \l_regex_caseless_bool % \end{macrocode} % \end{variable} % @@ -755,17 +782,37 @@ % For every character in the string, each of the active states is % considered in turn. % The variable \cs{l_regex_current_state_int} holds the state -% of the NFA which is currently considered. +% of the NFA which is currently considered: transitions are then +% given as shifts relative to the current state. +% In the case of groups with quantifiers, +% \cs{l_regex_current_state_int} is shifted to a fake value for +% transitions to point to the correct states. % \begin{macrocode} \int_new:N \l_regex_current_state_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_current_submatches_prop} +% \begin{variable}{\l_regex_success_submatches_prop} +% The submatches for the thread which lies at the +% \cs{l_regex_current_state_int} are stored in a property +% list variable. This property list is stored by +% \cs{regex_action_cost:n} into the \tn{toks} register +% for the target state of the transition. When a thread +% succeeds, this property list is copied to +% \cs{l_regex_success_submatches_prop} and only the last +% sucessful thread will remain there. +% \begin{macrocode} \prop_new:N \l_regex_current_submatches_prop +\prop_new:N \l_regex_success_submatches_prop % \end{macrocode} % \end{variable} +% \end{variable} % % \begin{variable}{\l_regex_max_index_int} % All the currently active states are kept in order of precedence % in the \tn{skip} registers, which for our purpose serve as an array: -% the $i$-th item of the array is \tn{skip}$i$. The largest index used +% the $i$th item of the array is \tn{skip}$i$. The largest index used % after treating the previous character is \cs{l_regex_max_index_int}. % At the start of every step, the whole array is unpacked, so that the % space can immediately be reused, and \cs{l_regex_max_index_int} reset @@ -785,11 +832,25 @@ % \end{macrocode} % \end{macro} % +% \begin{variable}{\l_regex_success_bool} +% The boolean \cs{l_regex_success_bool} is true if the current match +% attempt was successful. +% \begin{macrocode} +\bool_new:N \l_regex_success_bool +% \end{macrocode} +% \end{variable} +% % \begin{macro}{\regex_last_match_empty:F} -% This function is most often \cs{use:n}, unless the current -% regular expression can match an empty string at the current -% position, and has already done so at the previous match attempt. -% The goal is to break infinite loops. +% \begin{macro}[aux]{\regex_last_match_empty_no:F} +% \begin{macro}[aux]{\regex_last_match_empty_yes:F} +% When doing multiple matches, we need to avoid infinite loops where +% each iteration matches the same empty string. When we detect such +% a situation, the next match attempt is shifted by one character. +% Namely, an empty match is discarded if it follows an empty match +% at the same position. If the previous match was non-empty, +% \cs{regex_last_match_empty:F} is simply \cs{use:n}, and keeps +% the match. If it was empty, then we test whether the new match +% has moved or not: if it has not, then the success is discarded. % \begin{macrocode} \cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} \cs_new_protected:Npn \regex_last_match_empty_yes:F @@ -797,37 +858,25 @@ \cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} % -% \begin{variable}{\l_regex_success_bool} -% \begin{variable}{\l_regex_success_step_int} -% \begin{variable}{\l_regex_success_submatches_prop} % \begin{variable}{\l_regex_success_empty_bool} -% The booleans \cs{l_regex_success_bool} and -% \cs{l_regex_success_empty_bool} are true if the current match -% attempt was successful, and if the match was empty, respectively. -% The other two variables hold the step number at which the match -% was successful and a property list of submatches. -% \begin{macrocode} -\bool_new:N \l_regex_success_bool -\bool_new:N \l_regex_success_empty_bool -\int_new:N \l_regex_success_step_int -\prop_new:N \l_regex_success_submatches_prop -% \end{macrocode} -% \end{variable} -% \end{variable} -% \end{variable} -% \end{variable} -% % \begin{variable}{\l_regex_fresh_thread_bool} -% This boolean marks when the current thread has started from -% the beginning of the regular expression at this character, -% in other words, it is true if the current thread has matched -% an empty string so far (and we only care about this boolean -% when a thread succeeds, hence matching an empty string overall). +% When a match succeeds, \cs{l_regex_success_empty_bool} +% records whether it is empty. This information is used +% to initialize \cs{regex_last_match_empty:F} before +% starting the next match attempt. +% The boolean \cs{l_regex_fresh_thread_bool} is true +% when the current thread has started from the beginning of the +% regular expression at this character. +% This is probably suboptimal. Improvements welcome. % \begin{macrocode} +\bool_new:N \l_regex_success_empty_bool \bool_new:N \l_regex_fresh_thread_bool % \end{macrocode} % \end{variable} +% \end{variable} % % \subsubsection{Variables used for user functions} % @@ -848,6 +897,15 @@ % \end{macrocode} % \end{variable} % +% \begin{variable}{\g_regex_extract_all_seq} +% The \cs{regex_extract_all:nnN} function stores its result +% in that sequence variable before assigning it to the +% variable provided by the user. +% \begin{macrocode} +\seq_new:N \g_regex_extract_all_seq +% \end{macrocode} +% \end{variable} +% % \begin{variable}{\g_regex_split_seq} % The \cs{regex_split:nnN} function stores its result % in that sequence variable before assigning it to the @@ -909,59 +967,66 @@ % \end{macro} % \end{macro} % -% \subsubsection{Interrupting recursions} +% \subsubsection{Unsafe substring extraction} % -% \begin{macro}[int]{\regex_if_tail_stop:N} -% \begin{macro}[int]{\regex_if_tail_error:Nn} -% Test for the end of the \meta{string}, and either stop -% or cause an error if it is reached. +% \begin{macro}[int]{\regex_query_substr:NN} +% \begin{macro}[aux]{\regex_query_substr_aux:nnn} +% Extracting submatches from the query string is done +% in a way similar to \cs{str_substr:Nnn}, but omitting +% most checks, hence faster. % \begin{macrocode} -\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N -\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 - { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } +\cs_new_nopar:Npn \regex_query_substr:NN #1#2 + { + \exp_after:wN \regex_query_substr_aux:NN + \exp_after:wN #1 + \exp_after:wN #2 + \l_regex_query_other_str + \q_stop + } +\cs_new:Npn \regex_query_substr_aux:NN #1#2 + { + \str_skip_do:nn {#1} + { \str_collect_do:nn { #2 - #1 } { \use_i_delimit_by_q_stop:nw } } + } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[int]{\regex_build_error:n} -% This macro is called if anything goes wrong when building -% the NFA corresponding to a given regular expression -% Negative codes are specific to the \LaTeX3 implementation. -% Other error codes match with the PCRE codes (not all of -% the PCRE errors can occur, since some constructions are -% not supported). +% \subsubsection{Sequences} +% +% \begin{macro}[int]{\regex_seq_pop:N} +% \begin{macro}[int]{\regex_seq_get:N} +% When building the regular expression, some settings are kept +% local to capturing groups without any help from \TeX{}'s grouping. +% This is done \enquote{by hand}, in sequences whose items contain +% instructions such as |\int_set:Nn| \meta{int~var} \Arg{value}, +% which should be run immediately. % \begin{macrocode} -\cs_new_protected_nopar:Npn \regex_build_error:n #1 +\cs_new_protected_nopar:Npn \regex_seq_pop:N #1 { - \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} - { - \prg_case_int:nnn {#1} - { - {-999} {File~not~found} - {-998} {Unsupported~construct} - {-997} {The~regular~expression~is~too~large~(32768~states).} - {1} {\iow_char:N\\~at~end~of~pattern } -% {2} {\iow_char:N\\c~at~end~of~pattern } - {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} - {6} {Missing~terminating~\iow_char:N\]~for~character~class } - {7} {Invalid~escape~sequence~in~character~class} - {8} {Range~out~of~order~in~character~class} - {22} {Mismatched~parentheses} - {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} -% {44} {Invalid~UTF-8~string} -% {46} {Malformed~\iow_char:N\\P~or\iow_char:N\\p~sequence} -% {47} {Unknown~property~after~\iow_char:N\\P~or\iow_char:N\\p} -% {68} {\iow_char:N\\c~must~be~followed~by~an~ASCII~character} - } - { Internal~bug. } - } + \seq_pop:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl + } +\cs_new_protected_nopar:Npn \regex_seq_get:N #1 + { + \seq_get:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl } -\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } % \end{macrocode} % \end{macro} +% \end{macro} % % \subsubsection{Testing characters} % +% \begin{macro}{\regex_item_dot:T} +% The dot meta-character matches any character, +% except the end-of-string marker. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_item_dot:T + { \int_compare:nNnF \l_regex_current_char_int = \c_minus_one } +% \end{macrocode} +% \end{macro} +% % \begin{macro}[int]{\regex_break_point:TF} % \begin{macro}[int]{\regex_break_true:w,\regex_break_false:w} % When testing whether a character of the query string matches @@ -977,25 +1042,27 @@ % Otherwise, \cs{regex_break_point:TF} leaves the \meta{false code} % in the input stream. % \begin{macrocode} -\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} -\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} -\cs_new_eq:NN \regex_break_point:TF \use_ii:nn +\cs_new_protected_nopar:Npn \regex_break_true:w + #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_protected_nopar:Npn \regex_break_false:w + #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_protected_nopar:Npn \regex_break_point:TF #1 #2 { #2 } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[int]{\regex_item_equal:n,\regex_item_range:nn} -% \begin{macro}[int]{\regex_item_less:n,\regex_item_more:n} -% \begin{macro}[int]{\regex_item_neq:n} +% \begin{macro}[int]{\regex_item_caseful_equal:n} +% \begin{macro}[int]{\regex_item_caseful_range:nn} +% \begin{macro}[int]{\regex_item_caseful_geq:n} % Simple comparisons triggering \cs{regex_break_true:w} when true. % \begin{macrocode} -\cs_new_nopar:Npn \regex_item_equal:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseful_equal:n #1 { \if_num:w #1 = \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_range:nn #1 #2 +\cs_new_protected_nopar:Npn \regex_item_caseful_range:nn #1 #2 { \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \reverse_if:N \if_num:w #2 < \l_regex_current_char_int @@ -1003,29 +1070,97 @@ \fi: \fi: } -\cs_new_nopar:Npn \regex_item_less:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseful_geq:n #1 { - \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_more:n #1 +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\regex_item_caseless_equal:n} +% \begin{macro}[int]{\regex_item_caseless_range:nn} +% \begin{macro}[int]{\regex_item_caseless_geq:n} +% For caseless matching, we perform the test both on +% \cs{l_regex_current_char_int} and on +% \cs{l_regex_case_changed_char_int}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_item_caseless_equal:n #1 { - \if_num:w #1 < \l_regex_current_char_int + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \if_num:w #1 = \l_regex_case_changed_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_neq:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseless_range:nn #1 #2 { - \if_num:w #1 = \l_regex_current_char_int - \exp_after:wN \regex_break_false:w + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \reverse_if:N \if_num:w #2 < \l_regex_case_changed_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: \fi: } +\cs_new_protected_nopar:Npn \regex_item_caseless_geq:n #1 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\regex_item_equal:n} +% \begin{macro}[int]{\regex_item_range:nn} +% \begin{macro}[int]{\regex_item_geq:n} +% By default, matching takes the letter case into account. +% Note that those functions are not protected: +% they will expand at the building step, hard-coding which +% states take care of caseless versus caseful matching. +% \begin{macrocode} +\cs_new:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } +\cs_new:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } +\cs_new:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % +% \begin{macro}[int]{\regex_build_caseless:,\regex_build_caseful:} +% Switch between caseful and caseless matching. +% This is only done during the building step. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_caseless: + { + \bool_set_true:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseless_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseless_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseless_geq:n } + } +\cs_new_protected_nopar:Npn \regex_build_caseful: + { + \bool_set_false:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Grabbing digits} % % \begin{macro}[int]{\regex_get_digits:nw} @@ -1042,23 +1177,27 @@ { \exp_not:n {#1} { \if_false: } } \fi: - \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_aux:N #1 +\cs_new_nopar:Npn \regex_get_digits_aux:NN #1#2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \else: - \if_charcode:w \c_space_token \exp_not:N #1 + \if_meaning:w \regex_build_raw:N #1 + \if_charcode:w \c_space_token \exp_not:N #2 \else: - \regex_get_digits_end:w + \if_num:w 9 < 1 \exp_not:N #2 \exp_stop_f: + #2 + \else: + \regex_get_digits_end:w #1 #2 + \fi: \fi: + \else: + \regex_get_digits_end:w #1 #2 \fi: - #1 \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_end:w - \fi: \fi: #1 \regex_get_digits_aux:N +\cs_new_nopar:Npn \regex_get_digits_end:w #1 \fi: #2 \regex_get_digits_aux:NN { - \fi: \fi: + \fi: #2 \if_false: { { \fi: } } #1 } @@ -1066,7 +1205,20 @@ % \end{macro} % \end{macro} % -% \subsubsection{More char testing} +% \subsubsection{More character testing} +% +% \begin{macro}[EXP,pTF]{\regex_token_if_other_digit:N} +% In the replacement text, |\g{|\meta{int}|}| denotes the \meta{int}-th +% submatch. Parsing this construction robustly requires a test of whether +% a token is a digit or not. +% \begin{macrocode} +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w \c_nine < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: \else: \prg_return_false: \fi: + } +% \end{macrocode} +% \end{macro} % % \begin{macro}[EXP,aux]{\regex_aux_char_if_alphanumeric:NTF} % \begin{macro}[EXP,aux]{\regex_aux_char_if_special:NTF} @@ -1076,11 +1228,11 @@ % ones have special meanings and which should be interpreted % as \enquote{raw} characters. Namely, % \begin{itemize} -% \item alphanumerics are \enquote{raw} if they are not escaped, -% and may have a special meaning when escaped; -% \item non-alphanumeric printable ascii characters are \enquote{raw} -% if they are escaped, and may have a special meaning when not escaped; -% \item characters other than printable ascii are always \enquote{raw}. +% \item alphanumerics are \enquote{raw} if they are not escaped, +% and may have a special meaning when escaped; +% \item non-alphanumeric printable ascii characters are \enquote{raw} +% if they are escaped, and may have a special meaning when not escaped; +% \item characters other than printable ascii are always \enquote{raw}. % \end{itemize} % The code is ugly, and highly based on magic numbers and the ascii % codes of characters. This is mostly unavoidable for performance @@ -1095,43 +1247,40 @@ % \begin{macrocode} \prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } { - \if_num:w `#1 < 97 \exp_stop_f: - \if_num:w `#1 < 58 \exp_stop_f: - \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \if_num:w `#1 < \c_thirty_two + \prg_return_false: \else: \prg_return_true: \fi: + \else: \prg_return_false: \fi: \else: - \if_num:w \int_eval:w `#1 / 26 = \c_three - \prg_return_false: - \else: - \prg_return_true: - \fi: + \if_num:w `#1 < \c_sixty_five + \prg_return_true: \else: \prg_return_false: \fi: \fi: \else: - \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_true: \else: \prg_return_false: \fi: \else: - \prg_return_false: + \if_num:w `#1 < \c_one_hundred_twenty_seven + \prg_return_true: \else: \prg_return_false: \fi: \fi: \fi: } \prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } { - \if_num:w `#1 < 91 \exp_stop_f: - \if_num:w `#1 < 65 \exp_stop_f: - \if_num:w \c_nine < 1 #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \prg_return_false: \else: \prg_return_true: \fi: \else: - \prg_return_true: + \if_num:w `#1 < \c_sixty_five + \prg_return_false: \else: \prg_return_true: \fi: \fi: \else: - \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_false: \else: \prg_return_true: \fi: \else: \prg_return_false: \fi: @@ -1153,8 +1302,8 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_new_state: { - \int_compare:nNnTF \l_regex_max_state_int > {32766} - { \regex_build_error:n {-997} } + \int_compare:nNnTF \l_regex_max_state_int > { 32766 } + { \msg_error:nn { regex } { -997 } } { \int_incr:N \l_regex_max_state_int \tex_toks:D \l_regex_max_state_int { \s_regex_toks } @@ -1174,15 +1323,15 @@ { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { #1 { \int_use:N #2 } } + { #1 { \int_eval:n { #2 - \l_regex_left_state_int } } } } \cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { - #1 { \int_use:N #2 } - #3 { \int_use:N #4 } + #1 { \int_eval:n { #2 - \l_regex_left_state_int } } + #3 { \int_eval:n { #4 - \l_regex_left_state_int } } } } % \end{macrocode} @@ -1191,8 +1340,10 @@ % % \subsubsection{From regex to NFA: framework} % -% In order for the construction \texttt{ab\string|cd} to work, we enclose -% the whole pattern within parentheses. These have the added benefit +% In order for the construction \verb"ab|cd" to work, we enclose +% the whole pattern within parentheses (in the code below, +% \cs{regex_build_open_aux:} and \cs{regex_build_close_aux:}). +% These have the added benefit % to form a capturing group: hence we get the data of the whole match % for free. % @@ -1270,28 +1421,20 @@ % % \begin{macro}[int]{\regex_build_setup:} % Hopefully, we didn't forget to initialize anything here. -% The search is not anchored: to acheive that, we insert state(s) +% The search is not anchored: to achieve that, we insert state(s) % responsible for repeating the match attempt on every character -% of the string: the first state has a free transition to the second -% state, where the regular expression really begins, and a costly -% transition to itself, to try again at the next character. +% of the string. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_setup: { \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N \int_set_eq:NN \l_regex_capturing_group_int \c_zero - \int_zero:N \l_regex_left_state_int - \int_zero:N \l_regex_right_state_int \int_zero:N \l_regex_max_state_int \regex_build_new_state: \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_action_start_wildcard:nn - { \int_use:N \l_regex_left_state_int } - { \int_use:N \l_regex_right_state_int } - } + { \regex_action_start_wildcard:nn {0} {1} } } % \end{macrocode} % \end{macro} @@ -1305,9 +1448,9 @@ { \seq_if_empty:NF \l_regex_capturing_group_seq { - \regex_build_error:n {22} + \msg_error:nn { regex } {22} \prg_replicate:nn - { \seq_length:N \l_regex_capturing_group_seq } % ( + { \seq_length:N \l_regex_capturing_group_seq } { \regex_build_close_aux: \regex_build_group_: } } \regex_toks_put_right:Nx \l_regex_right_state_int @@ -1339,7 +1482,13 @@ \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } % \end{macrocode} @@ -1360,7 +1509,13 @@ { \exp_not:N \int_compare:nNnT \c_minus_one = \l_regex_current_char_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ @@ -1374,52 +1529,50 @@ % \begin{macro}[int]{\regex_build_B:} % \begin{macro}[aux]{\regex_if_word_boundary:TF} % Contrarily to |^| and |$|, which could be implemented without -% really knowing what preceeds in the string, this requires -% more information. We request it by setting -% \cs{l_regex_look_behind_bool}. Then the matching code will -% keep store the characters that were already read, backwards, -% in \cs{l_regex_look_behind_str}, and we can analyse the first -% character of that string. +% really knowing what precedes in the string, this requires +% more information, namely, the knowledge of the last character +% code. Case sensitivity does not change word boundaries. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_b: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } { } } } \cs_new_protected_nopar:Npn \regex_build_B: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF { } - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_protected_nopar:Npn \regex_if_word_boundary:TF { - \tl_if_empty:NTF \l_regex_look_behind_str - { \c_regex_w_tl } - { - \group_begin: - \cs_set_nopar:Npx \regex_tmp:w - { - \int_set:Nn \l_regex_current_char_int - { ` \str_head:N \l_regex_look_behind_str } - } - \regex_tmp:w - \c_regex_w_tl - \regex_break_point:TF - { \group_end: \c_regex_W_tl } - { \group_end: \c_regex_w_tl } - } + \group_begin: + \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl \regex_item_equal:n { -1 } } + { \group_end: \c_regex_w_tl } \regex_break_point:TF } % \end{macrocode} @@ -1434,18 +1587,16 @@ % (actually, any unknown combination) will match itself % and the thread will fail otherwise. We prepare % \cs{regex_build_tmp_class:n} with the relevant test and -% commands. The program steps to be inserted in those -% commands will come as |##1| and |##2|: we don't know -% yet what those will be before checking for quantifiers. +% commands. The state shift to be inserted in those +% commands will come as |##1|: we don't know +% yet what this will be before checking for quantifiers. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 { \cs_set:Npx \regex_build_tmp_class:n ##1 { - \exp_not:n { \exp_not:N \if_num:w } - \int_value:w `#1 = \l_regex_current_char_int - \regex_action_cost:n { ##1 } - \exp_not:n { \exp_not:N \fi: } + \regex_item_equal:n { \int_value:w `#1 } + \regex_break_point:TF { \regex_action_cost:n { ##1 } } { } } \regex_build_one_quantifier: } @@ -1459,11 +1610,7 @@ \cs_new_protected_nopar:cpn { regex_build_.: } { \cs_set:Npn \regex_build_tmp_class:n ##1 - { - \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int - \regex_action_cost:n {##1} - \exp_not:N \fi: - } + { \regex_item_dot:T { \regex_action_cost:n {##1} } } \regex_build_one_quantifier: } % \end{macrocode} @@ -1486,9 +1633,7 @@ \cs_set:Npn \regex_build_tmp_class:n ##1 { \exp_not:N #1 - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } + \regex_break_point:TF { \regex_action_cost:n {##1} } { } } \regex_build_one_quantifier: } @@ -1557,16 +1702,8 @@ { \exp_not:o \l_regex_class_tl \bool_if:NTF \l_regex_class_bool - { - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } - } - { - \exp_not:N \regex_break_point:TF - { } - { \regex_action_cost:n {##1} } - } + { \regex_break_point:TF { \regex_action_cost:n {##1} } { } } + { \regex_break_point:TF { } { \regex_action_cost:n {##1} } } } \regex_build_one_quantifier: } @@ -1607,7 +1744,7 @@ \cs_new_protected_nopar:Npn \regex_class_single:N #1 { \tl_put_right:Nx \l_regex_class_tl - { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + { \regex_item_equal:n { \int_value:w `#1 } } } % \end{macrocode} % \end{macro} @@ -1641,14 +1778,14 @@ \cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 { \if_num:w `#1 > `#2 \exp_stop_f: - \regex_build_error:n {8} + \msg_error:nn { regex } {8} \else: \tl_put_right:Nx \l_regex_class_tl { \if_num:w `#1 = `#2 \exp_stop_f: - \exp_not:N \regex_item_equal:n + \regex_item_equal:n \else: - \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \regex_item_range:nn { \int_value:w `#1 } \fi: { \int_value:w `#2 } } @@ -1781,26 +1918,29 @@ \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 } } -\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3#4 { - \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + \str_if_eq:xxTF + { \exp_not:n {#3#4} } + { \exp_not:N \regex_build_control:N \c_rbrace_str } { \tl_if_empty:nTF {#2} - { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { \regex_build_quantifier_aux:nnNN { {#1} } { n* } } { \int_compare:nNnT {#1} > {#2} - { \regex_build_error:n {4} } - \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + { \msg_error:nn { regex } {4} } + \regex_build_quantifier_aux:nnNN { {#1} {#2} } { nn } } } { \regex_build_quantifier_end:n { } \use:x { - \exp_not:n { \exp_args:No \tl_map_function:nN } - { \c_lbrace_str #1 #2 , } - \regex_build_raw:N + \exp_args:No \tl_map_function:nN + { \c_lbrace_str #1 , #2 } + \regex_build_raw:N } + #3 #4 } } % \end{macrocode} @@ -1923,83 +2063,49 @@ % This function is called in case the syntax is % \texttt{\{\meta{int}\}}. Greedy and lazy operators % are identical, since the number of repetitions is fixed. +% Simply repeat |#1| times the effect of \cs{regex_build_one_:}. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_one_n: #1 - { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { %^^A safe \if nesting? - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } - } + { \prg_replicate:nn {#1} { \regex_build_one_: } } \cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\regex_build_one_nn:} -% \begin{macro}[aux]{\regex_build_one_nn?:} -% \begin{macro}[aux]{\regex_build_one_nn_aux:nn} -% This function is called when the syntax is either -% \texttt{\{\meta{int},\}} or \texttt{\{\meta{int},\meta{int}\}}. +% \begin{macro}[aux]{\regex_build_one_n*:} +% \begin{macro}[aux]{\regex_build_one_n*?:} +% This function is called in case the syntax is +% \texttt{\{\meta{int},\}}. % \begin{macrocode} -\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_copy:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*: } } -\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*?: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*?: } } -\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_nn:} +% \begin{macro}[aux]{\regex_build_one_nn?:} +% \begin{macro}[aux]{\regex_build_one_nn_aux:Nnn} +% This function is called when the syntax is +% \texttt{\{\meta{int},\meta{int}\}}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:Nnn #1#2#3 { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - #2 - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } + \prg_replicate:nn {#2} { \regex_build_one_: } + \prg_replicate:nn {#3-#2} {#1} } +\cs_new_protected_nopar:Npx \regex_build_one_nn: + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_?: } } +\cs_new_protected_nopar:cpx { regex_build_one_nn?: } + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_??: } } % \end{macrocode} % \end{macro} % \end{macro} @@ -2007,8 +2113,8 @@ % % \subsubsection{Groups and alternation} % -% We support the syntax \texttt{(\meta{expr1}|\ldots{}%^^A -% |\meta{expr$\sb{n}$})\meta{quantifier}} for alternations. +% We support the syntax \texttt{\meta{expr1}|\ldots{}%^^A +% |\meta{expr$\sb{n}$}} for alternations. % % \begin{macro}[aux]{\regex_build_(:, \regex_build_):} % \begin{macro}[aux]{\regex_build_open_aux:} @@ -2017,14 +2123,14 @@ % \regex_build_end_alternation:} % Grouping and alternation go together. % \begin{itemize} -% \item Allocate the next available number for the end vertex -% of the alternation/group and store it on a stack (so that nested -% alternations work). -% \item Put free transitions to separate all cases of the alternation. -% \item Build each branch separately, and merge them to the common -% end-node. -% \item Test for a quantifier, and if needed, transfer the initial -% vertex to a new vertex. +% \item Allocate the next available number for the end vertex +% of the alternation/group and store it on a stack (so that nested +% alternations work). +% \item Put free transitions to separate all cases of the alternation. +% \item Build each branch separately, and merge them to the common +% end-node. +% \item Test for a quantifier, and if needed, transfer the initial +% vertex to a new vertex. % \end{itemize} % \begin{macrocode} \cs_new_protected_nopar:cpn { regex_build_(: } #1#2 @@ -2042,10 +2148,19 @@ \cs_new_protected_nopar:Npn \regex_build_open_aux: { \regex_build_new_state: - \seq_push:Nx \l_regex_left_state_seq - { \int_use:N \l_regex_left_state_int } - \seq_push:Nx \l_regex_right_state_seq - { \int_use:N \l_regex_right_state_int } + \seq_push:Nx \l_regex_group_begin_seq + { + \int_set:Nn \l_regex_left_state_int + { \int_use:N \l_regex_left_state_int } + } + \bool_if:NTF \l_regex_caseless_bool + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseless: } } + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseful: } } + \seq_push:Nx \l_regex_group_end_seq + { + \int_set:Nn \l_regex_right_state_int + { \int_use:N \l_regex_right_state_int } + } \regex_build_begin_alternation: } \cs_new_protected_nopar:cpn { regex_build_|: } @@ -2056,7 +2171,7 @@ \cs_new_protected_nopar:cpn { regex_build_): } { \seq_if_empty:NTF \l_regex_capturing_group_seq - { \regex_build_error:n {22} } + { \msg_error:nn { regex } { 22 } } { \regex_build_close_aux: \regex_build_group_quantifier: @@ -2065,10 +2180,9 @@ \cs_new_protected_nopar:Npn \regex_build_close_aux: { \regex_build_end_alternation: - \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl - \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + \regex_seq_pop:N \l_regex_group_begin_seq + \regex_seq_pop:N \l_regex_group_end_seq + \regex_seq_pop:N \l_regex_group_end_seq } % \end{macrocode} % Building each branch. @@ -2076,16 +2190,28 @@ \cs_new_protected_nopar:Npn \regex_build_begin_alternation: { \regex_build_new_state: - \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_seq_get:N \l_regex_group_begin_seq \regex_toks_put_right:Nx \l_regex_left_state_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } \cs_new_protected_nopar:Npn \regex_build_end_alternation: { - \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \regex_toks_put_right:Nx \l_regex_right_state_int - { \regex_action_free:n { \l_regex_tmpa_tl } } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \regex_seq_get:N \l_regex_group_end_seq + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } % \end{macrocode} % \end{macro} @@ -2102,7 +2228,7 @@ { \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } { - \regex_build_error:n { -998 } + \msg_error:nn { regex } { -998 } \regex_build_control:N ( % ) \regex_build_raw:N ? #1 #2 @@ -2125,6 +2251,55 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\regex_build_special_group_i:} +% \begin{macro}{\regex_build_special_group_-:} +% \begin{macro}[aux]{\regex_build_options:NNN} +% \begin{macro}[aux]{\regex_build_option_+i:} +% \begin{macro}[aux]{\regex_build_option_-i:} +% The match can be made case-insensitive by setting the option +% with \texttt{(?i)}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_special_group_i: + { + \regex_build_options:NNN + + \regex_build_raw:N i + } +\cs_new_protected_nopar:cpn { regex_build_special_group_-: } + { + \regex_build_options:NNN - + } +\cs_new_protected_nopar:Npn \regex_build_options:NNN #1#2#3 + { + \token_if_eq_meaning:NNTF \regex_build_raw:N #2 + { + \cs_if_exist_use:cF { regex_build_option_#1#3: } + { \msg_error:nnx { regex } { unknown-option } { #3 } } + \regex_build_options:NNN #1 + } + { + \prg_case_str:nnn { #3 } + { % ( + { ) } { } + { - } { \regex_build_options:NNN - } + } + { \msg_error:nnx { regex } { invalid-in-option } { #3 } } + } + } +\cs_new_protected_nopar:cpn { regex_build_option_+i: } + { + \regex_build_caseless: + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } +\cs_new_protected_nopar:cpn { regex_build_option_-i: } + { \regex_build_caseful: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% % \subsubsection{Quantifiers for groups} % % \begin{macro}[aux]{\regex_build_group_quantifier:} @@ -2162,7 +2337,7 @@ % % \begin{macro}[aux]{\regex_build_group_:} % When there is no quantifier, the group is simply inserted as is, -% and we only need to track submatches. +% and we only need to track submatches, and move to a new state. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_group_: { @@ -2184,14 +2359,22 @@ \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int \regex_build_new_state: \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int - \use:x + \regex_toks_put_left:Nx \l_regex_right_state_int + { + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_tmpa_int } % ^^A here we lie! + } + \cs_set:Npx \regex_tmp:w { \tex_toks:D \l_regex_tmpa_int - { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + { + \s_regex_toks + #1 { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } + \regex_tmp:w \regex_build_group_submatches:NN \l_regex_right_state_int \l_regex_left_state_int - \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int } % \end{macrocode} % \end{macro} @@ -2216,9 +2399,13 @@ \cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 { \regex_build_group_shift:N \regex_action_free:n + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int \regex_build_transition_aux:NN \regex_action_free:n #1 #2 \l_regex_tmpa_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } \cs_new_protected_nopar:cpn { regex_build_group_?: } { @@ -2275,84 +2462,108 @@ % \end{macro} % \end{macro} % +% \begin{macro}[aux]{\regex_build_group_n_aux:n} +% The braced quantifiers rely on replicating the states +% corresponding to the group that has just been built, +% and joining the right state of each copy to the left state +% of the next copy. Once this function has been run, +% \cs{l_regex_tmpa_int} points to the last copy of the initial +% left-most state, \cs{l_regex_left_state_int} has its initial +% value, and \cs{l_regex_right_state_int} points to the last +% copy of the initial right-most state. Furthermore, +% \cs{l_regex_max_state_int} is set appropriately to the largest +% allocated \tn{toks} register. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_n_aux:n #1 + { + \regex_toks_put_right:Nx \l_regex_right_state_int + { + \regex_action_free:n + { \int_eval:n { \l_regex_max_state_int - \l_regex_left_state_int } } + } + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \int_set:Nn \l_regex_tmpb_int { \l_regex_max_state_int + \c_one } + \int_set:Nn \l_regex_max_state_int + { + \l_regex_left_state_int - \c_one + + #1 * ( \l_regex_max_state_int - \l_regex_left_state_int + \c_one ) + } + \int_until_do:nNnn \l_regex_tmpb_int > \l_regex_max_state_int + { + \tex_toks:D \l_regex_tmpb_int = \tex_toks:D \l_regex_tmpa_int + \int_incr:N \l_regex_tmpa_int + \int_incr:N \l_regex_tmpb_int + } + } +% \end{macrocode} +% \end{macro} +% % \begin{macro}[aux]{\regex_build_group_n:} % \begin{macro}[aux]{\regex_build_group_n?:} % These functions are called in case the syntax is % \texttt{\{\meta{int}\}}. Greedy and lazy operators % are identical, since the number of repetitions is fixed. +% We only record the submatch information at the last repetition. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_group_n: #1 - { - \regex_build_group_shift:N \regex_action_repeat_move:n + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x - { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } - } + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int } \cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: % \end{macrocode} % \end{macro} % \end{macro} % +% \begin{macro}[aux]{\regex_build_group_n*:} +% \begin{macro}[aux]{\regex_build_group_n*?:} +% These functions are called in case the syntax is +% \texttt{\{\meta{int},\}}. They are somewhat hybrid between +% the \texttt{\{\meta{int}\}} and the \texttt{*} quantifiers. +% Contrarily to the \texttt{*} quantifier, for which we had +% to be careful not to overwrite the submatch information in +% case no iteration was made, here, we know that the submatch +% information is overwritten in any case. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_group_n*: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +\cs_new_protected_nopar:cpn { regex_build_group_n*?: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \begin{macro}[aux]{\regex_build_group_nn:} % \begin{macro}[aux]{\regex_build_group_nn?:} % These functions are called when the syntax is either % \texttt{\{\meta{int},\}} or \texttt{\{\meta{int},\meta{int}\}}. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x - { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } - } + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*: } {#1} } \cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x - { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \exp_not:N \fi: - } - } + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*?: } {#1} } % \end{macrocode} % \end{macro} @@ -2435,22 +2646,6 @@ % % \subsubsection{Helpers for running the NFA} % -% \begin{macro}[aux]{\regex_if_state_free:nT} -% A state is free if it is not marker as taken, namely -% if the corresponding \tn{dimen} register is $0\mathtt{sp}$ -% rather than $1\mathtt{sp}$. -% \begin{macrocode} -\cs_new_protected:Npn \regex_if_state_free:nT #1 - { - \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int - \exp_after:wN \use_none:n - \else: - \exp_after:wN \use:n - \fi: - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}[aux]{\regex_store_state:n} % Put the given state in the array of \tn{skip} registers. % This is done by increasing the pointer @@ -2468,27 +2663,33 @@ % \end{macro} % % \begin{macro}[int]{\regex_state_use:} -% \begin{macro}[int]{\regex_state_use_submatches:} -% \begin{macro}[aux]{\regex_state_use_aux:n} +% \begin{macro}[int]{\regex_state_use_with_submatches:} % \begin{macro}[aux]{\regex_state_use_aux_ii:w} +% \begin{macro}[aux]{\regex_state_use_aux:n} % Use a given program instruction, unless it has already been % executed at this step. The \tn{toks} registers begin with % some submatch information, ignored by \cs{regex_state_use:}, -% but not by \cs{regex_state_use_submatches:}. +% but not by \cs{regex_state_use_with_submatches:}. +% A state is free if it is not marker as taken, namely +% if the corresponding \tn{dimen} register is not +% \cs{l_regex_unique_step_int} in \texttt{sp}. +% The primitive conditional is ended before unpacking +% the \tn{toks} register. % \begin{macrocode} -\cs_new_protected_nopar:Npn \regex_state_use_submatches: +\cs_new_protected_nopar:Npn \regex_state_use_with_submatches: { \regex_state_use_aux:n { } } \cs_new_protected_nopar:Npn \regex_state_use: { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } -\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } -\cs_new_protected:Npn \regex_state_use_aux:n #1 - { - \regex_if_state_free:nT { \l_regex_current_state_int } - { - \tex_dimen:D \l_regex_current_state_int - = \l_regex_unique_step_int sp \scan_stop: - #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: - } +\cs_new_nopar:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected_nopar:Npn \regex_state_use_aux:n #1 + { + \if_num:w \tex_dimen:D \l_regex_current_state_int + < \l_regex_unique_step_int + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \exp_after:wN \l_regex_current_state_int + \fi: + \scan_stop: } % \end{macrocode} % \end{macro} @@ -2506,7 +2707,7 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_disable_submatches: { - \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_state_use_with_submatches: \regex_state_use: \cs_set_eq:NN \regex_store_submatches:n \regex_protected_use_none:n \cs_set_eq:NN \regex_action_submatches:n @@ -2517,37 +2718,42 @@ % \end{macro} % % \begin{macro}[int]{\regex_store_submatches:n} -% \begin{macro}[aux]{\regex_store_submatches_aux:Nw} -% \begin{macro}[aux]{\regex_store_submatches_aux_ii:wnnwwn} +% \begin{macro}[aux]{\regex_store_submatches_aux:w} +% \begin{macro}[aux]{\regex_store_submatches_aux_ii:Nnnw} % The submatch information pertaining to one given thread is moved % from state to state as we execute the NFA. +% We make sure that most of the \tn{toks} register is not read +% before being assigned again to that same register. % \begin{macrocode} \cs_new_protected:Npn \regex_store_submatches:n #1 { - \exp_after:wN \regex_store_submatches_aux:Nw - \exp_after:wN \l_regex_current_submatches_prop - \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop - { #1 } + \tex_toks:D #1 \exp_after:wN + { + \tex_romannumeral:D + \exp_after:wN \regex_store_submatches_aux:w + \tex_the:D \tex_toks:D #1 + } } -\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks +\cs_new_protected:Npn \regex_store_submatches_aux:w #1 \s_regex_toks { - \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } - #2 - \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \regex_store_submatches_aux_ii:Nnnw + #1 + \regex_state_submatches:nn \c_minus_one \q_prop \s_regex_toks } -\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn - #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 +\cs_new_protected:Npn \regex_store_submatches_aux_ii:Nnnw + \regex_state_submatches:nn #1 #2 #3 \s_regex_toks { - \tex_toks:D #6 \exp_after:wN + \exp_after:wN \c_zero + \exp_after:wN \regex_state_submatches:nn \exp_after:wN { - \exp_after:wN \regex_state_submatches:nn \exp_after:wN - { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } - { #1 } - \regex_state_submatches:nn {#2} {#3} - \s_regex_toks - #5 + \int_value:w \int_eval:w + \l_regex_unique_step_int + \c_one + \exp_after:wN } + \exp_after:wN { \l_regex_current_submatches_prop } + \regex_state_submatches:nn {#1} {#2} + \s_regex_toks } % \end{macrocode} % \end{macro} @@ -2562,7 +2768,7 @@ \cs_new_protected:Npn \regex_state_submatches:nn #1#2 { \if_num:w #1 = \l_regex_unique_step_int - \tl_set:Nn \l_regex_current_submatches_prop {#2} + \tl_set:Nn \l_regex_current_submatches_prop { #2 } \fi: } % \end{macrocode} @@ -2571,7 +2777,7 @@ % \subsubsection{Matching: framework} % % \begin{macro}[int]{\regex_match:n} -% Store the query string in \cs{l_regex_query_str}. +% Store the query string in \cs{l_regex_query_other_str}. % Then reset a few variables which should be set only once, % before the first match, even in the case of multiple matches. % Then run the NFA (\cs{regex_match_once:} matches multiple times @@ -2579,7 +2785,7 @@ % \begin{macrocode} \cs_new_protected:Npn \regex_match:n #1 { - \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \tl_set:Nx \l_regex_query_other_str { \tl_to_other_str:n {#1} } \regex_match_initial_setup: \regex_match_once: } @@ -2588,6 +2794,7 @@ % % \begin{macro}[int]{\regex_match_once:} % \begin{macro}[aux]{\regex_match_once_aux:} +% \begin{macro}[aux]{\regex_match_once_aux_ii:N} % After setting up more variables in \cs{regex_match_setup:}, % skip the \cs{l_regex_start_step_int} first characters of the % query string, and loop over it. @@ -2597,15 +2804,32 @@ \cs_new_protected_nopar:Npn \regex_match_once: { \regex_match_setup: - \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \exp_after:wN \regex_match_once_aux: \l_regex_query_other_str \q_recursion_tail \q_recursion_stop \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } } \cs_new_protected_nopar:Npn \regex_match_once_aux: - { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } + { + \int_compare:nNnTF \l_regex_start_step_int = \c_zero + { + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \regex_match_loop:N + } + { + \str_skip_do:nn + { \l_regex_start_step_int - \c_one } + { \regex_match_once_aux_ii:N } + } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux_ii:N #1 + { + \int_set:Nn \l_regex_current_char_int { `#1 } + \regex_match_loop:N + } % \end{macrocode} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}[aux]{\regex_match_initial_setup:} % This function holds the setup that should be done @@ -2613,12 +2837,11 @@ % string. It is called only once for the whole string. % On the other hand, \cs{regex_match_setup:} % is called for every match in the string in case of -% repeated matches, and \cs{regex_match_loop_setup:} +% repeated matches, and \cs{regex_match_loop_setup:N} % is called at every step. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_match_initial_setup: { - \tl_clear:N \l_regex_look_behind_str \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } \int_set_eq:NN \l_regex_unique_step_int \c_minus_one @@ -2650,18 +2873,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\regex_match_loop_setup:} -% This is called for every character in the string. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \regex_match_loop_setup: - { - \int_incr:N \l_regex_current_step_int - \int_incr:N \l_regex_unique_step_int - \bool_set_false:N \l_regex_fresh_thread_bool - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}[int]{\s_regex_step} % \begin{macro}[aux]{\regex_break_step:w} % When a thread succeeds, all threads with lower precedence @@ -2677,7 +2888,7 @@ % % \begin{macro}[aux]{\regex_match_loop:N} % \begin{macro}[aux]{\regex_match_one_index:n} -% \begin{macro}[aux]{\regex_match_one_index_aux:nn} +% \begin{macro}[aux]{\regex_match_one_index_aux:n} % Setup what needs to be reset at every character, % then set \cs{l_regex_current_char_int} to the % character code of the character that is read @@ -2688,10 +2899,7 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_match_loop:N #1 { - \regex_match_loop_setup: - \token_if_eq_meaning:NNTF #1 \q_recursion_tail - { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } - { \int_set:Nn \l_regex_current_char_int {`#1} } + \regex_match_loop_setup:N #1 \cs_set_nopar:Npx \regex_tmp:w { \int_zero:N \l_regex_max_index_int @@ -2705,28 +2913,80 @@ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \quark_if_recursion_tail_stop:N #1 - \bool_if:NT \l_regex_look_behind_bool - { \tl_put_left:Nx \l_regex_look_behind_str {#1} } \regex_match_loop:N } \cs_new_nopar:Npn \regex_match_one_index:n #1 { - \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + \regex_match_one_index_aux:n + { \int_value:w \tex_skip:D #1 } } \cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 { \int_set:Nn \l_regex_current_state_int {#1} \prop_clear:N \l_regex_current_submatches_prop - \regex_state_use_submatches: + \regex_state_use_with_submatches: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % +% \begin{macro}[aux]{\regex_match_loop_setup:N} +% This is called for every character in the string. +% At every step in reading the query string, we store the character +% code of the current character in \cs{l_regex_current_char_int}, +% unless the end was reached: then we store $-1$. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_loop_setup:N #1 + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int + \if_meaning:w #1 \q_recursion_tail + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \else: + \int_set:Nn \l_regex_current_char_int {`#1} + \fi: + \regex_match_loop_case_hook: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\regex_match_loop_case_hook:} +% \begin{macro}{\regex_match_loop_caseless_hook:} +% In the case where the regular expression contains caseless matching, +% the \cs{regex_match_loop_case_hook:} (normally empty) is redefined +% to set \cs{l_regex_case_changed_char_int} properly. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_loop_case_hook: { } +\cs_new_protected_nopar:Npn \regex_match_loop_caseless_hook: + { + \int_set_eq:NN \l_regex_case_changed_char_int \l_regex_current_char_int + \if_num:w \l_regex_current_char_int < \c_ninety_one + \if_num:w \l_regex_current_char_int < \c_sixty_five + \else: + \int_add:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \else: + \if_num:w \l_regex_current_char_int < \c_one_hundred_twenty_three + \if_num:w \l_regex_current_char_int < \c_ninety_seven + \else: + \int_sub:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \fi: + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \subsubsection{Actions when matching} % % \begin{macro}[aux]{\regex_action_start_wildcard:nn} +%: the first state has a free transition to the second +% state, where the regular expression really begins, and a costly +% transition to itself, to try again at the next character. ^^A ?? % The search is made unanchored at the start by putting % a free transition to the real start of the NFA, and a % costly transition to the same state, waiting for the @@ -2751,7 +3011,10 @@ % to state |#1|. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_action_cost:n #1 - { \regex_store_state:n {#1} } + { + \exp_args:Nf \regex_store_state:n %^^A optimize! + { \int_eval:n { \l_regex_current_state_int + #1 } } + } % \end{macrocode} % \end{macro} % @@ -2790,41 +3053,32 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_action_free:n #1 { - \regex_if_state_free:nT {#1} + \cs_set_nopar:Npx \regex_tmp:w { - \use:x - { - \int_set:Nn \l_regex_current_state_int {#1} - \regex_store_submatches:n { \l_regex_current_state_int } - \regex_state_use: - \int_set:Nn \l_regex_current_state_int - { \int_use:N \l_regex_current_state_int } - \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop - { \exp_not:o \l_regex_current_submatches_prop } - } + \int_add:Nn \l_regex_current_state_int {#1} + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } } + \regex_tmp:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux]{\regex_action_submatch:n} +% Update the current submatches with the information +% from the current step. % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_action_submatch:n #1 { \prop_put:Nno \l_regex_current_submatches_prop {#1} { \int_use:N \l_regex_current_step_int } - \regex_store_submatches:n { \l_regex_current_state_int } } % \end{macrocode} % \end{macro} % -% \begin{macrocode} -\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } -% \end{macrocode} -% % \subsection{Submatches, once the correct match is found} % % \begin{macro}[int]{\regex_extract:} @@ -2839,11 +3093,7 @@ \regex_extract_aux:nTF { ##1 } { \seq_gput_right:Nx \g_regex_submatches_seq - { - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpa_tl } - { \l_regex_tmpb_tl } - } + { \regex_query_substr:NN \l_regex_tmpa_tl \l_regex_tmpb_tl } } { \seq_gput_right:Nn \g_regex_submatches_seq { } } } @@ -2918,9 +3168,13 @@ = \int_use:N \l_regex_max_state_int \l_regex_capturing_group_int = \int_use:N \l_regex_capturing_group_int - \bool_if:NTF \l_regex_look_behind_bool - { \bool_set_true:N \l_regex_look_behind_bool } - { \bool_set_false:N \l_regex_look_behind_bool } + \token_if_eq_meaning:NNT + \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + { + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } \prg_stepwise_function:nnnN {1} {1} {\l_regex_max_state_int} \regex_set_aux:n @@ -2946,7 +3200,7 @@ % If a token list variable starts with \cs{regex_nfa:Nw}, % then it most likely holds the data for a precompiled pattern. % \begin{macrocode} -\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { T , TF } { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } \cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop { @@ -2957,11 +3211,6 @@ \prg_return_false: \fi: } -\msg_new:nnn { regex } { not-nfa } - { - I~was~expecting~a~regular~expression~variable.\\ - Instead,~I~got~#1. - } % \end{macrocode} % \end{macro} % @@ -3001,7 +3250,7 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_return_after_group: { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \group_insert_after:N \prg_return_true: \else: \group_insert_after:N \prg_return_false: @@ -3016,7 +3265,7 @@ % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \regex_extract: \group_insert_after:N \seq_set_eq:NN \group_insert_after:N #1 @@ -3100,7 +3349,7 @@ } \cs_new_protected:Npn \regex_count:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -3116,7 +3365,6 @@ \regex_count_after_group:N #3 } } - { } } % \end{macrocode} % \end{macro} @@ -3124,12 +3372,12 @@ % % \subsubsection{Submatch extraction} % -% \begin{macro}{\regex_extract:nnN} -% \begin{macro}{\regex_extract:NnN} -% \begin{macro}[TF]{\regex_extract:nnN} -% \begin{macro}[TF]{\regex_extract:NnN} +% \begin{macro}{\regex_extract_once:nnN} +% \begin{macro}{\regex_extract_once:NnN} +% \begin{macro}[TF]{\regex_extract_once:nnN} +% \begin{macro}[TF]{\regex_extract_once:NnN} % \begin{macrocode} -\cs_new_protected:Npn \regex_extract:nnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:nnN #1#2#3 { \regex_user_aux:n { @@ -3138,7 +3386,8 @@ \regex_extract_after_group:N #3 } } -\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:nnN #1#2#3 + { T , F , TF } { \regex_user_aux:n { @@ -3148,9 +3397,9 @@ \regex_return_after_group: } } -\cs_new_protected:Npn \regex_extract:NnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -3159,9 +3408,9 @@ \regex_extract_after_group:N #3 } } - { } } -\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:NnN #1#2#3 + { T , F , TF } { \regex_check_nfa:NTF #1 { @@ -3181,6 +3430,66 @@ % \end{macro} % \end{macro} % +% \begin{macro}{\regex_extract_all:nnN} +% \begin{macro}{\regex_extract_all:NnN} +% \begin{macro}[aux]{\regex_extract_all_aux:} +% \begin{macro}[aux]{\regex_extract_all_after_group:N} +% Similarly to \cs{regex_count:nnN} functions, +% recurse through matches of the pattern. Then we do +% something slightly different, extracting submatches. +% Submatches are not extracted if the pattern matched +% an empty string at the start of the match attempt +% (to avoid adding spurious empty items to the resulting +% sequence). +% \begin{macrocode} +\cs_new_protected:Npn \regex_extract_all:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_extract_all:NnN #1#2#3 + { + \regex_check_nfa:NT #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } + } +\cs_new_protected_nopar:Npn \regex_extract_all_aux: + { + \regex_extract: + \seq_gconcat:NNN \g_regex_extract_all_seq + \g_regex_extract_all_seq \g_regex_submatches_seq + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_extract_all_after_group:N #1 + { + \seq_if_empty:NF \g_regex_extract_all_seq + { + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_extract_all_seq + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% % \subsubsection{Splitting a string by matches of a regex} % % \begin{macro}{\regex_split:nnN} @@ -3208,7 +3517,7 @@ } \cs_new_protected:Npn \regex_split:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -3219,7 +3528,6 @@ \regex_split_after_group:N #3 } } - { } } \cs_new_protected_nopar:Npn \regex_split_aux: { @@ -3231,9 +3539,8 @@ { \seq_gput_left:Nx \g_regex_submatches_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl } } { \msg_error:nn { regex } { internal } } @@ -3252,9 +3559,8 @@ { \seq_gput_right:Nx \g_regex_split_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \int_use:N \l_regex_current_step_int } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_current_step_int } } \group_insert_after:N \seq_set_eq:NN @@ -3270,55 +3576,77 @@ % \subsubsection{String replacement} % % \begin{macro}[int]{\regex_replacement:n} -% \begin{macro}[aux]{\regex_replacement_loop:N} -% \begin{macro}[aux]{\regex_replacement_normal:N} +% \begin{macro}[aux]{\regex_replacement_escaped:N} +% \begin{macro}[aux]{\regex_replacement_submatch:w} +% \begin{macro}[aux]{\regex_replacement_submatch_aux:nN} % \begin{macrocode} \cs_new_protected:Npn \regex_replacement:n #1 { \str_aux_escape:NNNn \prg_do_nothing: \regex_replacement_escaped:N - \regex_replacement_raw:N + \prg_do_nothing: {#1} \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl \tl_set:Nx \l_regex_replacement_tl { \l_regex_replacement_tl \prg_do_nothing: } } -\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } \cs_new_nopar:Npn \regex_replacement_escaped:N #1 { - \if_num:w 9 < 1 #1 \exp_stop_f: - \exp_not:N \regex_replacement_escaped_ii:nN {#1} - \else: - #1 - \fi: + \regex_token_if_other_digit:NTF #1 + { + \exp_not:n + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } {#1} + } + { + \token_if_eq_charcode:NNTF g #1 + { \exp_not:N \regex_replacement_submatch:w } + { #1 } + } } -\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 +\cs_new_nopar:Npn \regex_replacement_submatch:w #1 { - \regex_token_if_other_digit:NTF #2 - { \regex_replacement_escaped_ii:nN {#1#2} } - { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + \exp_after:wN \token_if_eq_meaning:NNTF \c_lbrace_str #1 + { \regex_replacement_submatch_aux:nN { } } + { + \regex_token_if_other_digit:NTF #1 + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } + { \msg_expandable_error:n { \g misused~in~replacement~text. } } + #1 + } } -\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } +\cs_new_nopar:Npn \regex_replacement_submatch_aux:nN #1 #2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_submatch_aux:nN {#1#2} } + { + \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } {#1} + \exp_after:wN \token_if_eq_meaning:NNF \c_rbrace_str #2 + { + \msg_expandable_error:n { \g misused~in~replacement~text. } + #2 + } + } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}[aux]{\regex_replace_after_group:N} +% I wonder why I implemented things with \tn{aftergroup} +% rather than simply placing that code after the explicit +% \cs{group_end:} that I have. (General remark.) % \begin{macrocode} \cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 { - \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N \tl_set:Nx \group_insert_after:N #1 - \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N { + \group_insert_after:N \tl_to_str:N + \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N } } % \end{macrocode} % \end{macro} @@ -3339,15 +3667,16 @@ \regex_extract: \regex_extract_aux:nTF {0} { + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_tmpb_tl } { } } \tl_gset:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \regex_query_substr:NN \c_zero \l_regex_tmpa_tl \l_regex_replacement_tl - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpb_tl } { \c_max_int } + \exp_after:wN \regex_tmp:w \l_regex_query_other_str } } - { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_other_str } \group_end: } \cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 @@ -3404,12 +3733,10 @@ \tl_gclear:N \g_regex_replaced_str #2 \exp_args:No \regex_match:n {#1} + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_start_step_int } { } } \tl_gput_right:Nx \g_regex_replaced_str - { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \c_max_int } - } + { \exp_after:wN \regex_tmp:w \l_regex_query_other_str } \group_end: } \cs_new_protected_nopar:Npn \regex_replace_all_aux: @@ -3419,9 +3746,8 @@ { \tl_gput_right:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl \l_regex_replacement_tl } } @@ -3450,6 +3776,47 @@ % \end{macro} % \end{macro} % +% \subsection{Messages} +% +% Negative codes are specific to the \LaTeX3 implementation. +% Other error codes match with the PCRE codes (not all of +% the PCRE errors can occur, since many constructions are +% not supported). +% \begin{macrocode} +% \msg_new:nnn { regex } { -999 } { File~not~found } +\msg_new:nnn { regex } { -998 } { Unsupported~construct } +\msg_new:nnn { regex } { -997 } + { The~regular~expression~is~too~large~(32768~states). } +\msg_new:nnn { regex } { 1 } { \iow_char:N\\~at~end~of~pattern } +% \msg_new:nnn { regex } { 2 } { \iow_char:N\\c~at~end~of~pattern } +\msg_new:nnn { regex } { 4 } + { Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier. } +\msg_new:nnn { regex } { 6 } + { Missing~terminating~\iow_char:N\]~for~character~class } +\msg_new:nnn { regex } { 7 } + { Invalid~escape~sequence~in~character~class } +\msg_new:nnn { regex } { 8 } + { Range~out~of~order~in~character~class } +\msg_new:nnn { regex } { 22 } { Mismatched~parentheses } +\msg_new:nnn { regex } { 34 } + { Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large } +% \msg_new:nnn { regex } { 44 } { Invalid~UTF-8~string } +% \msg_new:nnn { regex } { 46 } +% { Malformed~\iow_char:N\\P~or\iow_char:N\\p~sequence } +% \msg_new:nnn { regex } { 47 } +% { Unknown~property~after~\iow_char:N\\P~or\iow_char:N\\p } +% \msg_new:nnn { regex } { 68 } +% { \iow_char:N\\c~must~be~followed~by~an~ASCII~character } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnn { regex } { not-nfa } + { + LaTeX~was~expecting~a~regular~expression~variable.\\ + Instead,~LaTeX~found~'#1'. + } +% \end{macrocode} +% % \begin{macrocode} %</package> % \end{macrocode} diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx index a718f986ce7..b3648c57ede 100644 --- a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx @@ -35,7 +35,7 @@ % %<*driver|package> \RequirePackage{expl3} -\GetIdInfo$Id: l3str.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: l3str.dtx 2921 2011-10-17 13:23:36Z bruno $ {L3 Experimental Strings} %</driver|package> %<*driver> @@ -72,25 +72,23 @@ % \LaTeX3 provides a set of functions to manipulate token lists % as strings of characters, ignoring the category codes of those % characters. -% -% There is no separate string data type: string variables are simply -% specialized token lists, conventionally made out only of characters -% with category code $12$ (other), except spaces, which have category -% code $10$ (blank space). All the \enquote{safe} functions in this -% module first convert their argument to a string for internal processing, +% +% 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 \enquote{safe} functions in +% this module first convert their argument to a string for internal processing, % and will not treat a token list or the corresponding string representation -% differently. However, it is often better to name token list variables -% meant to contain string data with a name such as \cs{l_\ldots{}_str}. -% Those are then manipulated mostly with tools from \pkg{l3tl}. +% differently. % % Most functions in this module come in three flavours: % \begin{itemize} -% \item \cs{str_...:N...}, which expect a token list -% variable as their argument; -% \item \cs{str_...:n...}, taking any token list (or string) as an argument; -% \item \cs{str_..._ignore_spaces:n...}, which ignores any space encountered -% during the operation: these functions are slightly faster than those -% which take care of escaping spaces appropriately; +% \item \cs{str_...:N...}, which expect a token list +% variable as their argument; +% \item \cs{str_...:n...}, taking any token list (or string) as an argument; +% \item \cs{str_..._ignore_spaces:n...}, which ignores any space encountered +% during the operation: these functions are slightly faster than those +% which take care of escaping spaces appropriately; % \end{itemize} % When performance is important, the internal \cs{str_..._unsafe:n...} % functions, which expect a \enquote{safe} string in which spaces @@ -98,66 +96,119 @@ % % \subsection{Conversion and input of strings} % -% \begin{variable}{\c_backslash_str, -% \c_lbrace_str,\c_rbrace_str,\c_hash_str,\c_percent_str} +% \begin{variable} +% { +% \c_backslash_str, +% \c_lbrace_str, +% \c_rbrace_str, +% \c_hash_str, +% \c_percent_str +% } % Constant strings, containing a single character, with category code $12$. % \end{variable} % -% \begin{function}[EXP]{\tl_to_str:N,\tl_to_str:n} +% \begin{function}[EXP]{\tl_to_str:N, \tl_to_str:n} % \begin{syntax} % \cs{tl_to_str:N} \meta{tl var} % \cs{tl_to_str:n} \Arg{token list} % \end{syntax} -% Converts the \meta{token list} to a \meta{string}. +% Converts the \meta{token list} to a \meta{string}, leaving the resulting +% tokens in the input stream. +% \end{function} +% +% \begin{function}{\str_set:Nn, \str_set:Nx, \str_set:cn, \str_set:cx} +% \begin{syntax} +% \cs{str_set:Nn} \meta{str var} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, +% and saves the result in \meta{str var}. +% \end{function} +% +% \begin{function}{\str_gset:Nn, \str_gset:Nx, \str_gset:cn, \str_gset:cx} +% \begin{syntax} +% \cs{str_gset:Nn} \meta{str var} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, +% and saves the result in \meta{str var} globally. % \end{function} % % \begin{function} % { -% \str_set:Nn, \str_set:Nx, -% \str_set:cn, \str_set:cx, -% \str_gset:Nn, \str_gset:Nx, -% \str_gset:cn, \str_gset:cx, +% \str_put_left:Nn, \str_put_left:Nx, +% \str_put_left:cn, \str_put_left:cx % } +% \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} % { -% \str_put_left:Nn, \str_put_left:Nx, -% \str_put_left:cn, \str_put_left:cx, % \str_gput_left:Nn, \str_gput_left:Nx, -% \str_gput_left:cn, \str_gput_left:cx, +% \str_gput_left:cn, \str_gput_left:cx % } +% \begin{syntax} +% \cs{str_gput_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}, globally. +% The current contents of the \meta{str var} are not +% automatically converted to a string. +% \end{function} +% % \begin{function} % { % \str_put_right:Nn, \str_put_right:Nx, -% \str_put_right:cn, \str_put_right:cx, -% \str_gput_right:Nn, \str_gput_right:Nx, -% \str_gput_right:cn, \str_gput_right:cx, +% \str_put_right:cn, \str_put_right:cx % } % \begin{syntax} -% \cs{str_set:Nn} \meta{str var} \Arg{token list} +% \cs{str_put_right:Nn} \meta{str var} \Arg{token list} % \end{syntax} % Converts the \meta{token list} to a \meta{string}, -% and saves the result in \meta{str var}. -% \end{function} +% 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} +% +% \begin{function} +% { +% \str_gput_right:Nn, \str_gput_right:Nx, +% \str_gput_right:cn, \str_gput_right:cx +% } +% \begin{syntax} +% \cs{str_gput_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}, globally. +% The current contents of the \meta{str var} are not +% automatically converted to a string. % \end{function} % -% \begin{function}{\str_input:Nn,\str_ginput:Nn} +% \begin{function}{\str_input:Nn, \str_ginput:Nn} % \begin{syntax} % \cs{str_input:Nn} \meta{str var} \Arg{token list} % \end{syntax} % Converts the \meta{token list} into a \meta{string}, and stores -% it in the \meta{str var}. Special characters can be input by +% it in the \meta{str var}, within the current \TeX{} group level +% for the \texttt{input} variant and globally for the \texttt{ginput} +% version. +% Special characters can be input by % escaping them with a backslash. % \begin{itemize} -% \item Spaces are ignored unless escaped with a backslash. -% \item |\xhh| produces the character with code \texttt{hh} -% in hexadecimal: when |\x| is encountered, up to two hexadecimal digits -% (\texttt{0}--\texttt{9}, \texttt{a}--\texttt{f}, \texttt{A}--\texttt{F}) -% are read to give a number between $0$ and $255$. -% \item |\x{hh...}| produces the character with code \texttt{hh...} -% (an aribtrary number of hexadecimal digits are read): -% this is mostly useful for Lua\TeX{} and Xe\TeX{}. -% \item |\a|, |\e|, |\f|, |\n|, |\r|, |\t| stand for specific characters: +% \item Spaces are ignored unless escaped with a backslash. +% \item |\xhh| produces the character with code \texttt{hh} +% in hexadecimal: when |\x| is encountered, up to two hexadecimal digits +% (\texttt{0}--\texttt{9}, \texttt{a}--\texttt{f}, +% \texttt{A}--\texttt{F}) +% are read to give a number between $0$ and $255$. +% \item |\x{hh...}| produces the character with code \texttt{hh...} +% (an arbitrary number of hexadecimal digits are read): +% this is mostly useful for \LuaTeX{} and \XeTeX{}. +% \item |\a|, |\e|, |\f|, |\n|, |\r|, |\t| stand for specific characters: % \begin{center} % \begin{tabular}{cccc} % |\a| & |\^^G| & alarm & hex \texttt{07} \\ @@ -172,26 +223,27 @@ % For instance, % \begin{quote} % \cs{tl_new:N} \cs{l_my_str} \\ -% \cs{str_input:Nn} \cs{l_my_str} |{\x3C \\ \# abc\ def\^}| +% \cs{str_input:Nn} \cs{l_my_str} |{\x3C \\ \# abc\ def\^\n}| % \end{quote} -% results in \cs{l_my_str} containing the characters |\<#abc def^|, -% since |<| has ascii code \texttt{3C} (in hexadecimal). +% results in \cs{l_my_str} containing the characters |<\#abc def^|, +% followed by a newline character (hex \texttt{0A}) +% since |<| has \textsc{ascii} code \texttt{3C} (in hexadecimal). % \end{function} % % \subsection{Characters given by their position} % -% \begin{function}[EXP]{\str_length:N} -% \begin{function}[EXP]{\str_length:n} -% \begin{function}[EXP]{\str_length_ignore_spaces:n} +% \begin{function}[EXP] +% {\str_length:N, \str_length:n, \str_length_ignore_spaces:n} % \begin{syntax} % \cs{str_length:n} \Arg{token list} % \end{syntax} % Leaves the length of the string representation of \meta{token list} -% in the input stream. The functions differ in their treatment of spaces. +% in the input stream as an integer denotation. The functions differ in +% their treatment of spaces. % In the case of \cs{str_length:N} and \cs{str_length:n}, % all characters including spaces are counted. -% The \cs{str_length_ignore_spaces:n} returns the number of non-space -% characters. +% The \cs{str_length_ignore_spaces:n} leaves the number of non-space +% characters in the input stream. % \begin{texnote} % The \cs{str_length:n} of a given token list may depend % on the category codes in effect when it is measured, @@ -200,12 +252,8 @@ % on the escape character, and the category code of \texttt{a}. % \end{texnote} % \end{function} -% \end{function} -% \end{function} % -% \begin{function}[EXP]{\str_head:N} -% \begin{function}[EXP]{\str_head:n} -% \begin{function}[EXP]{\str_head_ignore_spaces:n} +% \begin{function}[EXP]{\str_head:N, \str_head:n, \str_head_ignore_spaces:n} % \begin{syntax} % \cs{str_head:n} \Arg{token list} % \end{syntax} @@ -215,18 +263,14 @@ % The functions differ in their treatment of spaces. % In the case of \cs{str_head:N} and \cs{str_head:n}, % a leading space is returned with category code $10$ (blank space). -% The \cs{str_head_ignore_spaces:n} function returns the first -% non-space character. +% The \cs{str_head_ignore_spaces:n} function leaves the first +% non-space character in the input stream. % 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} -% \end{function} -% \end{function} % -% \begin{function}[EXP]{\str_tail:N} -% \begin{function}[EXP]{\str_tail:n} -% \begin{function}[EXP]{\str_tail_ignore_spaces:n} +% \begin{function}[EXP]{\str_tail:N, \str_tail:n, \str_tail_ignore_spaces:n} % \begin{syntax} % \cs{str_tail:n} \Arg{token list} % \end{syntax} @@ -239,12 +283,8 @@ % only that space, while \cs{str_tail_ignore_spaces:n} trims % the first non-space character. % \end{function} -% \end{function} -% \end{function} % -% \begin{function}[EXP]{\str_item:Nn} -% \begin{function}[EXP]{\str_item:nn} -% \begin{function}[EXP]{\str_item_ignore_spaces:nn} +% \begin{function}[EXP]{\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} @@ -260,29 +300,25 @@ % the right-most character, \emph{etc.}, while $0$ is the first % (left-most) character. % \end{function} -% \end{function} -% \end{function} % -% \begin{function}[EXP]{\str_from_to:Nnn} -% \begin{function}[EXP]{\str_from_to:nnn} -% \begin{function}[EXP]{\str_from_to_ignore_spaces:nnn} +% \begin{function}[EXP] +% {\str_substr:Nnn, \str_substr:nnn, \str_substr_ignore_spaces:nnn} % \begin{syntax} -% \cs{str_from_to:nnn} \Arg{token list} \Arg{start index} \Arg{end index} +% \cs{str_substr: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 % between \meta{start index} (inclusive) and \meta{end index} (exclusive). % If either of \meta{start index} or \meta{end index} is negative, -% the it is incremented by the length of the list. -% \end{function} -% \end{function} +% the it is incremented by the length of the list. Both \meta{start index} +% and \meta{end index} count from $0$ for the first (left most) character. % \end{function} % % \subsection{String conditionals} % -% \begin{function}[EXP,pTF]{\str_if_eq:NN} -% \begin{function}[EXP,pTF] +% \begin{function}[EXP, pTF] % { +% \str_if_eq:NN, % \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:xx % } @@ -300,10 +336,9 @@ % expandable (including those involving an \texttt{x}-type % expansion). % \end{function} -% \end{function} % -% \begin{function}[EXP,pTF]{\str_if_contains_char:NN} -% \begin{function}[EXP,pTF]{\str_if_contains_char:nN} +% \begin{function}[EXP, pTF] +% {\str_if_contains_char:NN, \str_if_contains_char:nN} % \begin{syntax} % \cs{str_if_contains_char:nN} \Arg{token list} \meta{char} % \end{syntax} @@ -312,43 +347,43 @@ % The \meta{char} can be given either directly, or as a one % letter control sequence. % \end{function} -% \end{function} % -% \subsection{Bytes and encoding} +% \subsection{Byte conditionals} % -% \subsubsection{Conditionals} -% -% \begin{function}[EXP,pTF]{\str_if_bytes:N} +% \begin{function}[EXP, pTF]{\str_if_bytes:N} % \begin{syntax} % \cs{str_if_bytes:NTF} \meta{str var} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{str var} only contains characters -% in the range $0$--$255$. -% \end{function} -% -% \begin{function}[EXP,pTF]{\str_if_UTF_viii:N} -% \begin{syntax} -% \cs{str_if_UTF_viii:N} \meta{str var} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests whether the \meta{str var} only contains characters -% in the range $0$--$255$, and forms a valid \texttt{UTF8} string. -% \textbf{Missing!} ^^A Missing. +% in the range $0$ to $255$. % \end{function} % -% \subsubsection{Conversion} +% ^^A\begin{function}[EXP, pTF]{\str_if_UTF_viii:N} +% ^^A \begin{syntax} +% ^^A \cs{str_if_UTF_viii:N} \meta{str var} \Arg{true code} \Arg{false code} +% ^^A \end{syntax} +% ^^A Tests whether the \meta{str var} only contains characters +% ^^A in the range $0$--$255$, and forms a valid \texttt{UTF8} string. +% ^^A \textbf{Missing!} ^^A Missing. +% ^^A\end{function} +% +% \subsection{Byte conversion} +% +% Byte conversions are necessary where strings are to be used in PDF +% string objects or within URLs. In both cases, only a restricted subset +% of characters are permitted. % % \begin{function}{\str_native_from_UTF_viii:NN} % \begin{syntax} % \cs{str_native_from_UTF_viii:NN} \meta{str~var1} \meta{str~var2} % \end{syntax} -% Reads the contents of the \meta{str~var2} as an \texttt{UTF8}-encoded +% Reads the contents of the \meta{str~var2} as an \textsc{utf8}-encoded % sequence of bytes, and stores the resulting characters (which can % now have any character code) into \meta{str~var1}. % This function raises an error if the \meta{str~var2} is not -% a valid sequence of bytes in the \texttt{UTF8} encoding. +% a valid sequence of bytes in the \textsc{utf8} encoding. % In the \pdfTeX{} engine, this function raises an error if any of the -% resulting characters is outside the range $0$--$255$ (in other words, -% don't use this function with \pdfTeX{}). +% resulting characters is outside the range $0$ to $255$. % \end{function} % % \begin{function}{\str_UTF_viii_from_native:NN} @@ -356,58 +391,123 @@ % \cs{str_UTF_viii_from_native:NN} \meta{str~var1} \meta{str~var2} % \end{syntax} % Converts each character of the \meta{str~var2} into -% a sequence of bytes, as defined by the \texttt{UTF8} +% a sequence of bytes, as defined by the \textsc{utf8} % encoding, and stores the result in \meta{str~var1}. % In the \pdfTeX{} engine, this function is of course % of very little use, but can be used without harm: -% characters in the range $0$--$127$ are left unchanged, -% and characters in the range $128$--$255$ become two-byte +% characters in the range $0$ to $127$ are left unchanged, +% and characters in the range $128$ to $255$ become two-byte % sequences, as per the definition of \texttt{UTF8}. % \end{function} % -% \begin{function}{\str_bytes_escape_hexadecimal:NN} -% \begin{function}{\str_bytes_unescape_hexadecimal:NN} +% \begin{function} +% {\str_bytes_escape_hexadecimal:NN, \str_bytes_unescape_hexadecimal:NN} % \begin{syntax} % \cs{str_bytes_escape_hexadecimal:NN} \meta{str var1} \meta{str var2} % \cs{str_bytes_unescape_hexadecimal:NN} \meta{str var1} \meta{str var2} % \end{syntax} -% See \tn{pdfescapehex}. ^^A missing doc -% \end{function} +% The \texttt{escape} variant takes each character in \meta{str var1} +% and converts it into the hexadecimal representation. The resulting +% string is stored in \meta{str var2} within the current \TeX{} group +% level. The \texttt{unescape} variant reverses this process. +% Thus for example +% \begin{verbatim} +% \str_set:Nn \l_mya_str { Hello ( ) } +% \str_bytes_escape_hexadecimal:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |48656C6C6F2829|, +% while +% \begin{verbatim} +% \str_set:Nn \l_mya_str { 48656C6C6F2829 } +% \str_bytes_unescape_hexadecimal:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello()|. +% See also \tn{pdfescapehex}. % \end{function} % -% \begin{function}{\str_bytes_escape_name:NN} -% \begin{function}{\str_bytes_unescape_name:NN} +% \begin{function} +% {\str_bytes_escape_name:NN, \str_bytes_unescape_name:NN} % \begin{syntax} % \cs{str_bytes_escape_name:NN} \meta{str var1} \meta{str var2} % \cs{str_bytes_unescape_name:NN} \meta{str var1} \meta{str var2} % \end{syntax} -% See \tn{pdfescapename}. ^^A missing doc -% \end{function} +% The \texttt{escape} variant takes each character in \meta{str var1} +% and replaces any which cannot be used directly in a PDF string object +% with the appropriate hexadecimal representation preceded by the |#| +% character. The resulting +% string is stored in \meta{str var2} within the current \TeX{} group +% level. The \texttt{unescape} variant reverses this process. Thus for +% example +% \begin{verbatim} +% \str_set:Nn \l_mya_str { Hello ( ) } +% \str_bytes_escape_name:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello#28#29|, +% while +% \begin{verbatim} +% \char_set_catcode_other:N \# +% \str_set:Nn \l_mya_str { Hello#28#29 } +% \str_bytes_unescape_name:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello()|. +% See also \tn{pdfescapename}. % \end{function} % -% \begin{function}{\str_bytes_escape_string:NN} -% \begin{function}{\str_bytes_unescape_string:NN} +% \begin{function}{\str_bytes_escape_string:NN, \str_bytes_unescape_string:NN} % \begin{syntax} % \cs{str_bytes_escape_string:NN} \meta{str var1} \meta{str var2} % \cs{str_bytes_unescape_string:NN} \meta{str var1} \meta{str var2} % \end{syntax} -% See \tn{pdfescapestring}. ^^A missing doc -% \end{function} +% The \texttt{escape} variant takes each character in \meta{str var1} +% and prepends a |\| to any which cannot be used directly in a PDF string +% object. The resulting string is stored in \meta{str var2} within the +% current \TeX{} group level. The \texttt{unescape} variant reverses this +% process. Thus for example +% \begin{verbatim} +% \str_set:Nn \l_mya_str { Hello [ ] } +% \str_bytes_escape_string:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello\(\)|, +% while +% \begin{verbatim} +% \str_set:Nn \l_mya_str { Hello\(\) } +% \str_bytes_unescape_string:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello()|. +% Some characters (for example a space) are converted to a three-digit +% octal representation, so for example a space gives |\040|. +% See also \tn{pdfescapename}. % \end{function} % -% \begin{function}{\str_bytes_percent_encode:NN} -% \begin{function}{\str_bytes_percent_decode:NN} +% \begin{function}{\str_bytes_percent_encode:NN, \str_bytes_percent_decode:NN} % \begin{syntax} % \cs{str_bytes_percent_encode:NN} meta{str var1} \meta{str var2} % \cs{str_bytes_percent_decode:NN} meta{str var1} \meta{str var2} % \end{syntax} -% Used for urls. ^^A missing doc -% \end{function} +% The \texttt{escape} variant takes each character in \meta{str var1} +% and replaces any which cannot be used directly in a URL +% with the appropriate hexadecimal representation preceded by the |%| +% character. The resulting +% string is stored in \meta{str var2} within the current \TeX{} group +% level. The \texttt{unescape} variant reverses this process. Thus for +% example +% \begin{verbatim} +% \str_set:Nn \l_mya_str { Hello ( ) } +% \str_bytes_precent_encode:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello%28%29|, +% while +% \begin{verbatim} +% \char_set_catcode_other:N \% +% \str_set:Nn \l_mya_str { Hello%28%29 } +% \str_bytes_unescape_name:NN \l_mya_str \l_myb_str +% \end{verbatim} +% will result in \cs{l_myb_str} containing the string |Hello()|. % \end{function} % % \subsection{Internal string functions} % -% \begin{function}[EXP]{\tl_to_other_str:N,\tl_to_other_str:n} +% \begin{function}[EXP]{\tl_to_other_str:N, \tl_to_other_str:n} % \begin{syntax} % \cs{tl_to_other_str:n} \Arg{token list} % \end{syntax} @@ -421,7 +521,7 @@ % \end{texnote} % \end{function} % -% \begin{function}[EXP]{\str_sanitize_args:Nn,\str_sanitize_args:Nnn} +% \begin{function}[EXP]{\str_sanitize_args:Nn, \str_sanitize_args:Nnn} % \begin{syntax} % \cs{str_sanitize_args:Nnn} \meta{function} % ~~\Arg{token list1} \Arg{token list2} @@ -473,7 +573,6 @@ % Some functionalities of \pkg{stringstrings} and \pkg{xstring} as well. % %^^A Should "str_item" be "str_char"? -%^^A Should "str_from_to" be "str_substr"? % % \end{documentation} % @@ -482,7 +581,7 @@ % \section{\pkg{l3str} implementation} % % \begin{macrocode} -%<*package> +%<*initex|package> % \end{macrocode} % % \begin{macrocode} @@ -492,31 +591,18 @@ % % Those string-related functions are defined in \pkg{l3kernel}. % \begin{itemize} -% \item \cs{str_if_eq:nn}[pTF] and variants, -% \item \cs{str_if_eq_return:on}, -% \item \cs{tl_to_str:n}, \cs{tl_to_str:N}, \cs{tl_to_str:c}, -% \item \cs{token_to_str:N}, \cs{cs_to_str:N} -% \item \cs{str_head:n}, \cs{str_head_aux:w}, (changed behaviour slightly) -% \item \cs{str_tail:n}, \cs{str_tail_aux:w}, (changed behaviour slightly) -% \item \cs{str_length_skip_spaces} (deprecated) -% \item \cs{str_length_loop:NNNNNNNNN} (unchanged) +% \item \cs{str_if_eq:nn}[pTF] and variants, +% \item \cs{str_if_eq_return:on}, +% \item \cs{tl_to_str:n}, \cs{tl_to_str:N}, \cs{tl_to_str:c}, +% \item \cs{token_to_str:N}, \cs{cs_to_str:N} +% \item \cs{str_head:n}, \cs{str_head_aux:w}, (copied here) +% \item \cs{str_tail:n}, \cs{str_tail_aux:w}, (copied here) +% \item \cs{str_length_skip_spaces} (deprecated) +% \item \cs{str_length_loop:NNNNNNNNN} (unchanged) % \end{itemize} % % \subsection{General functions} % -% \begin{macro}[EXP,aux]{\cs_if_exist_use:cF} -% This function could be moved to a different module in \pkg{l3kernel}. -% If the control sequence exists, use it, -% otherwise fall back to a default behaviour. -% \begin{macrocode} -\cs_if_exist:NF \cs_if_exist_use:cF - { - \cs_new:Npn \cs_if_exist_use:cF #1 - { \cs_if_exist:cTF {#1} { \use:c {#1} } } - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}[EXP,aux]{\use_i:nnnnnnnn} % A function which may already be defined elsewhere. % \begin{macrocode} @@ -542,18 +628,20 @@ % } % Simply convert the token list inputs to \meta{strings}. % \begin{macrocode} -\cs_set:Npn \str_tmp:w #1 - { - \cs_new_protected:cpx { str_#1:Nn } ##1##2 - { \exp_not:c { tl_#1:Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } - \exp_args:Nc \cs_generate_variant:Nn { str_#1:Nn } { Nx , cn , cx } - } -\str_tmp:w {set} -\str_tmp:w {gset} -\str_tmp:w {put_left} -\str_tmp:w {gput_left} -\str_tmp:w {put_right} -\str_tmp:w {gput_right} +\group_begin: + \cs_set_protected_nopar:Npn \str_tmp:w #1 + { + \cs_new_protected:cpx { str_ #1 :Nn } ##1##2 + { \exp_not:c { tl_ #1 :Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } + \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :Nn } { Nx , cn , cx } + } + \str_tmp:w { set } + \str_tmp:w { gset } + \str_tmp:w { put_left } + \str_tmp:w { gput_left } + \str_tmp:w { put_right } + \str_tmp:w { gput_right } +\group_end: % \end{macrocode} % \end{macro} % @@ -563,7 +651,7 @@ % \begin{variable}{\g_str_tmpa_tl} % Internal scratch space for some functions. % \begin{macrocode} -\cs_set_protected_nopar:Npn \str_tmp:w { } +\cs_new_protected_nopar:Npn \str_tmp:w { } \tl_new:N \g_str_tmpa_tl % \end{macrocode} % \end{variable} @@ -579,11 +667,11 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_str_char_int,\l_str_bytes_int} +% \begin{variable}{\l_str_char_int, \l_str_bytes_int} % When converting from various escaped forms to raw characters, % we often need to read several digits (hexadecimal or octal depending % on the case) and keep track of the corresponding character code -% in \cs{l_str_char_int}. For UTF-8 support, the number of bytes +% in \cs{l_str_char_int}. For \textsc{utf8} support, the number of bytes % of for the current character is stored in \cs{l_str_bytes_int}. % \begin{macrocode} \int_new:N \l_str_char_int @@ -591,8 +679,14 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\c_backslash_str, -% \c_lbrace_str,\c_rbrace_str,\c_hash_str,\c_percent_str} +% \begin{variable} +% { +% \c_backslash_str, +% \c_lbrace_str, +% \c_rbrace_str, +% \c_hash_str, +% \c_percent_str +% } % For all of those strings, \cs{cs_to_str:N} produce characters % with the correct category code. % \begin{macrocode} @@ -606,48 +700,45 @@ % % \subsection{Escaping spaces} % -% \begin{macro}[EXP]{\tl_to_other_str:N,\tl_to_other_str:n} -% \begin{macro}[EXP,aux]{\tl_to_other_str_loop:w,\tl_to_other_str_end:w} +% \begin{macro}[EXP]{\tl_to_other_str:N, \tl_to_other_str:n} +% \begin{macro}[EXP,aux]{\tl_to_other_str_loop:w, \tl_to_other_str_end:w} % Replaces all spaces by \enquote{other} spaces, after converting % the token list to a string via \cs{tl_to_str:n}. % \begin{macrocode} \group_begin: -\char_set_lccode:nn {`\*}{`\ } -\char_set_lccode:nn {`\A}{`\A} -\tl_to_lowercase:n { - \group_end: - \cs_new:Npn \tl_to_other_str:n #1 - { - \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % - A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop - } - \cs_new_nopar:Npn \tl_to_other_str_loop:w +\char_set_lccode:nn { `\* } { `\ } +\char_set_lccode:nn { `\A } { `\A } +\tl_to_lowercase:n + { + \group_end: + \cs_new:Npn \tl_to_other_str:n #1 + { + \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % + A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_loop:w #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \q_stop - { - \if_meaning:w A #8 - \tl_to_other_str_end:w - \fi: - \tl_to_other_str_loop:w - #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop - } - \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop - { \fi: #2 } -} -\cs_new_nopar:Npn \tl_to_other_str:N - { \exp_args:No \tl_to_other_str:n } + { + \if_meaning:w A #8 + \tl_to_other_str_end:w + \fi: + \tl_to_other_str_loop:w + #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop + { \fi: #2 } + } +\cs_new_nopar:Npn \tl_to_other_str:N { \exp_args:No \tl_to_other_str:n } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[EXP]{\str_sanitize_args:Nn,\str_sanitize_args:Nnn} +% \begin{macro}[EXP]{\str_sanitize_args:Nn, \str_sanitize_args:Nnn} % Here, \texttt{f}-expansion does not lose leading spaces, % since they have catcode \enquote{other} after \cs{str_sanitize:n}. % \begin{macrocode} -\cs_new:Npn \str_sanitize_args:Nn #1 #2 - { - \exp_args:Nf #1 - { \tl_to_other_str:n {#2} } - } +\cs_new:Npn \str_sanitize_args:Nn #1#2 + { \exp_args:Nf #1 { \tl_to_other_str:n {#2} } } \cs_new:Npn \str_sanitize_args:Nnn #1#2#3 { \exp_args:Nff #1 @@ -726,15 +817,15 @@ % gives an empty result after passing through % \cs{use_i_delimit_by_q_stop:nw}. % \begin{macrocode} -\cs_set_nopar:Npn \str_head:N { \exp_args:No \str_head:n } +\cs_new_nopar:Npn \str_head:N { \exp_args:No \str_head:n } \cs_set:Npn \str_head:n #1 { \exp_after:wN \str_head_aux:w \tl_to_str:n {#1} { { } } ~ \q_stop } -\cs_set_nopar:Npx \str_head_aux:w #1 ~ % - { \exp_not:N \use_i_delimit_by_q_stop:nw #1 { ~ } } +\cs_set_nopar:Npn \str_head_aux:w #1 ~ % + { \use_i_delimit_by_q_stop:nw #1 { ~ } } \cs_new:Npn \str_head_ignore_spaces:n #1 { \exp_after:wN \use_i_delimit_by_q_stop:nw \tl_to_str:n {#1} { } \q_stop } \cs_new_nopar:Npn \str_head_unsafe:n #1 @@ -779,7 +870,7 @@ } \cs_new_nopar:Npn \str_tail_unsafe:n #1 { \str_tail_aux_ii:w #1 X { } X \q_stop } -\cs_set_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } +\cs_new_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } % \end{macrocode} % \end{macro} % \end{macro} @@ -800,12 +891,14 @@ % \begin{macrocode} \cs_new:Npn \str_skip_do:nn #1 { - \int_compare:nNnTF {#1} > \c_seven - { \str_skip_aux:nnnnnnnnn } - { \str_skip_end:n } + \if_num:w \int_eval:w #1 > \c_seven + \exp_after:wN \str_skip_aux:nnnnnnnnn + \else: + \exp_after:wN \str_skip_end:n + \fi: {#1} } -\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2 #3#4#5#6#7#8#9 +\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \exp_args:Nf \str_skip_do:nn { \int_eval:n { #1 - \c_seven } } {#2} } \cs_new:Npn \str_skip_end:n #1 { @@ -821,7 +914,7 @@ \else: \str_skip_end_ii:nwn { XXXXXXX } \fi: } -\cs_new:Npn \str_skip_end_ii:nwn #1 #2 \fi: #3 +\cs_new:Npn \str_skip_end_ii:nwn #1#2 \fi: #3 { \fi: \use_i:nnnnnnnn {#3} #1 } % \end{macrocode} % \end{macro} @@ -861,18 +954,18 @@ \cs_new:Npn \str_collect_end:n #1 { \if_case:w \int_eval:w #1 \int_eval_end: - \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{} } - \or: \str_collect_end_ii:nwn { {}{} } - \or: \str_collect_end_ii:nwn { {} } + \str_collect_end_ii:nwn { { } { } { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } } + \or: \str_collect_end_ii:nwn { { } } \or: \str_collect_end_ii:nwn { } - \else: \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \else: \str_collect_end_ii:nwn { { } { } { } { } { } { } { } } \fi: } -\cs_new:Npn \str_collect_end_ii:nwn #1 #2 \fi: #3 +\cs_new:Npn \str_collect_end_ii:nwn #1#2 \fi: #3 { \fi: #3 \q_stop #1 } \cs_new:Npn \str_collect_end_iii:nwNNNNNNN #1 #2 \q_stop #3#4#5#6#7#8#9 { #1 {#2#3#4#5#6#7#8#9} } @@ -938,12 +1031,12 @@ % \end{macro} % \end{macro} % -% \begin{macro}[EXP]{\str_from_to:Nnn} -% \begin{macro}[EXP]{\str_from_to:nnn} -% \begin{macro}[EXP]{\str_from_to_ignore_spaces:nnn} -% \begin{macro}[EXP]{\str_from_to_unsafe:nnn} -% \begin{macro}[EXP,aux]{\str_from_to_aux:nnnw} -% \begin{macro}[EXP,aux]{\str_from_to_aux_ii:nnw} +% \begin{macro}[EXP]{\str_substr:Nnn} +% \begin{macro}[EXP]{\str_substr:nnn} +% \begin{macro}[EXP]{\str_substr_ignore_spaces:nnn} +% \begin{macro}[EXP]{\str_substr_unsafe:nnn} +% \begin{macro}[EXP,aux]{\str_substr_aux:nnnw} +% \begin{macro}[EXP,aux]{\str_substr_aux_ii:nnw} % \begin{macro}[EXP,aux]{\str_aux_eval_args:Nnnn} % \begin{macro}[EXP,aux]{\str_aux_normalize_range:nn} % Sanitize the string, then limit the second and third arguments @@ -952,30 +1045,30 @@ % Afterwards, skip characters, then keep some more, and finally % drop the end of the string. % \begin{macrocode} -\cs_new_nopar:Npn \str_from_to:Nnn { \exp_args:No \str_from_to:nnn } -\cs_new:Npn \str_from_to:nnn #1#2#3 +\cs_new_nopar:Npn \str_substr:Nnn { \exp_args:No \str_substr:nnn } +\cs_new:Npn \str_substr:nnn #1#2#3 { \exp_args:Nf \tl_to_str:n - { \str_sanitize_args:Nn \str_from_to_unsafe:nnn {#1}{#2}{#3} } + { \str_sanitize_args:Nn \str_substr_unsafe:nnn {#1} {#2} {#3} } } -\cs_new:Npn \str_from_to_ignore_spaces:nnn #1 - { \exp_args:No \str_from_to_unsafe:nnn { \tl_to_str:n {#1} } } -\cs_new:Npn \str_from_to_unsafe:nnn #1#2#3 % <string> <from> <to> +\cs_new:Npn \str_substr_ignore_spaces:nnn #1 + { \exp_args:No \str_substr_unsafe:nnn { \tl_to_str:n {#1} } } +\cs_new:Npn \str_substr_unsafe:nnn #1#2#3 { - \str_aux_eval_args:Nnnn \str_from_to_aux:nnnw + \str_aux_eval_args:Nnnn \str_substr_aux:nnnw { \str_length_unsafe:n {#1} } {#2} {#3} #1 \q_stop } -\cs_new:Npn \str_from_to_aux:nnnw #1#2#3 % <len> <from> <to> +\cs_new:Npn \str_substr_aux:nnnw #1#2#3 { - \exp_args:Nf \str_from_to_aux_ii:nnw + \exp_args:Nf \str_substr_aux_ii:nnw { \str_aux_normalize_range:nn {#2} {#1} } { \str_aux_normalize_range:nn {#3} {#1} } } -\cs_new:Npn \str_from_to_aux_ii:nnw #1#2 +\cs_new:Npn \str_substr_aux_ii:nnw #1#2 { \str_skip_do:nn {#1} { @@ -1127,7 +1220,7 @@ % % \subsection{Internal conditionals} % -% \begin{macro}[aux]{\str_aux_hexadecimal_test:N} +% \begin{macro}[aux]{\str_aux_hexadecimal_test:NTF} % This test is used when reading hexadecimal digits, for the |\x| escape % sequence, and some conversion functions. It returns \meta{true} if the % token is a hexadecimal digit, and \meta{false} otherwise. It has the @@ -1137,7 +1230,7 @@ % \begin{macrocode} \prg_new_protected_conditional:Npnn \str_aux_hexadecimal_test:N #1 { TF } { - \tl_if_in:onTF { \tl_to_str:n {abcdef} } {#1} + \tl_if_in:onTF { \tl_to_str:n { abcdef } } {#1} { \int_set:Nn \l_str_char_int { \c_sixteen * \l_str_char_int + `#1 - 87 } @@ -1156,7 +1249,7 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\str_aux_octal_test:N} +% \begin{macro}[aux]{\str_aux_octal_test:NTF} % This test is used when reading octal digits, for % some conversion functions. It returns \meta{true} if the % token is an octal digit, and \meta{false} otherwise. @@ -1193,7 +1286,8 @@ % and in the regular expression module to go through the regular expression % once before actually parsing it. The goal in that case is to turn any % character with a meaning in regular expressions (\texttt{*}, \texttt{?}, -% \texttt{\{}, etc.) into a marker indicating that this was a special character, +% \texttt{\{}, etc.) into a marker indicating that this was a special +% character, % and replace any escaped character by the corresponding unescaped character, % so that the \pkg{l3regex} code can avoid caring about escaping issues % (those can become quite complex to handle in combination with ranges @@ -1201,7 +1295,7 @@ % % The idea is to feed unescaped characters to one function, % escaped characters to another, and feed |\a|, |\e|, |\f|, -% |\n|, |\r|, |\t| and |\x| converted to the approriate +% |\n|, |\r|, |\t| and |\x| converted to the appropriate % character to a third function. Spaces are ignored unless % escaped. % For the \cs{str_(g)input:Nn} application, all the functions are simply @@ -1245,7 +1339,7 @@ \cs_set_nopar:Npn \str_aux_escape_unescaped:N { #1 } \cs_set_nopar:Npn \str_aux_escape_escaped:N { #2 } \cs_set_nopar:Npn \str_aux_escape_raw:N { #3 } - \int_set:Nn \tex_escapechar:D {92} + \int_set:Nn \tex_escapechar:D { 92 } \tl_gset:Nx \g_str_result_tl { \tl_to_other_str:n {#4} } \tl_gset:Nx \g_str_result_tl { @@ -1278,9 +1372,11 @@ % \begin{macro}[aux]+\str_aux_escape_\q_recursion_tail:w+ % \begin{macro}[aux]+\str_aux_escape_/\q_recursion_tail:w+ % \begin{macro}[aux]+\str_aux_escape_ :w+ -% \begin{macro}[aux]{ +% \begin{macro}[aux] +% { % \str_aux_escape_/a:w, \str_aux_escape_/e:w, \str_aux_escape_/f:w, -% \str_aux_escape_/n:w, \str_aux_escape_/r:w, \str_aux_escape_/t:w} +% \str_aux_escape_/n:w, \str_aux_escape_/r:w, \str_aux_escape_/t:w +% } % The loop is ended upon seeing \cs{q_recursion_tail}. % Spaces are ignored, and |\a|, |\e|, |\f|, |\n|, |\r|, |\t| take % their meaning here. @@ -1396,12 +1492,6 @@ } } \group_end: -\msg_new:nnn { str } { x-missing-brace } - { - You~wrote~something~like~ - `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ - The~closing~brace~is~missing. - } % \end{macrocode} % \end{macro} % \end{macro} @@ -1411,21 +1501,21 @@ % \end{macro} % \end{macro} % -% \subsubsection{Escape and unescape strings for pdf use} +% \subsubsection{Escape and unescape strings for PDF use} % % \begin{macro}{\str_aux_convert_store:} % \begin{macro}{\str_aux_convert_store:NNn} % \begin{macrocode} \group_begin: - \char_set_catcode_other:n {`\^^@} + \char_set_catcode_other:n { `\^^@ } \cs_new_protected_nopar:Npn \str_aux_convert_store: { - \char_set_lccode:nn {\c_zero} {\l_str_char_int} + \char_set_lccode:nn { \c_zero } { \l_str_char_int } \tl_to_lowercase:n { \tl_gput_right:Nx \g_str_result_tl {^^@} } } \cs_new_protected_nopar:Npn \str_aux_convert_store:NNn #1#2#3 { - \char_set_lccode:nn {\c_zero} {#3} + \char_set_lccode:nn { \c_zero } {#3} \tl_to_lowercase:n { #1 #2 {^^@} } } \group_end: @@ -1438,7 +1528,7 @@ % \begin{macrocode} \cs_new_nopar:Npn \str_aux_byte_to_hexadecimal:N #1 { - \int_compare:nNnTF {`#1} < {256} + \int_compare:nNnTF {`#1} < { 256 } { \int_to_letter:n { \int_div_truncate:nn {`#1} \c_sixteen } \int_to_letter:n { \int_mod:nn {`#1} \c_sixteen } @@ -1447,14 +1537,14 @@ } \cs_new_nopar:Npn \str_aux_byte_to_octal:N #1 { - \int_compare:nNnTF {`#1} < {64} + \int_compare:nNnTF {`#1} < { 64 } { 0 \int_to_letter:n { \int_div_truncate:nn {`#1} \c_eight } \int_to_letter:n { \int_mod:nn {`#1} \c_eight } } { - \int_compare:nNnTF {`#1} < {256} + \int_compare:nNnTF {`#1} < { 256 } { \int_to_octal:n {`#1} } { \msg_expandable_error:n { Invalid~byte~`#1'. } } } @@ -1476,7 +1566,7 @@ % \begin{macro}{\str_bytes_escape_name:NN} % \begin{macrocode} \tl_const:Nx \c_str_bytes_escape_name_str - { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str () /<>[] } + { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str ()/<>[] } \cs_new_protected_nopar:Npn \str_bytes_escape_name:NN #1#2 { \str_set:Nx #1 @@ -1484,10 +1574,10 @@ } \cs_new_nopar:Npn \str_bytes_escape_name_aux:N #1 { - \int_compare:nNnTF {`#1} < {"21} + \int_compare:nNnTF {`#1} < { "21 } { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } { \str_if_contains_char:NNTF \c_str_bytes_escape_name_str #1 @@ -1501,7 +1591,7 @@ % % \begin{macro}{\str_bytes_escape_string:NN} % Any character below (and including) space, and any character -% above (anc including) \texttt{del}, are converted to octal. +% above (and including) \texttt{del}, are converted to octal. % One backslash is added before each parenthesis and backslash. % \begin{macrocode} \tl_const:Nx \c_str_bytes_escape_string_str { \c_backslash_str ( ) } @@ -1512,10 +1602,10 @@ } \cs_new_nopar:Npn \str_bytes_escape_string_aux:N #1 { - \int_compare:nNnTF {`#1} < {"21} + \int_compare:nNnTF {`#1} < { "21 } { \c_backslash_str \str_aux_byte_to_octal:N #1 } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_backslash_str \str_aux_byte_to_octal:N #1 } { \str_if_contains_char:NNT \c_str_bytes_escape_string_str #1 @@ -1639,7 +1729,7 @@ % If followed by an end-of-line character, the backslash and % the end-of-line are ignored. If followed by anything else, % the backslash is ignored. -% The pdf specification indicates that LF, CR, and CRLF should be +% The PDF specification indicates that LF, CR, and CRLF should be % converted to LF: \emph{this is not implemented here}. % \begin{macrocode} \group_begin: @@ -1671,16 +1761,16 @@ { \prg_case_str:xxn {#2} { - {n} {10} - {r} {13} - {t} {9} - {b} {8} - {f} {12} - {(} {40} - {)} {41} - {\c_backslash_str} {92} - {^^J} {-1} - {^^M} {-1} + { n } { 10 } + { r } { 13 } + { t } { 9 } + { b } { 8 } + { f } { 12 } + { ( } { 40 } + { ) } { 41 } + { \c_backslash_str } { 92 } + { ^^J } { -1 } + { ^^M } { -1 } } {`#2} } @@ -1727,14 +1817,14 @@ } \cs_new_nopar:Npn \str_bytes_percent_encode_aux:N #1 { - \int_compare:nNnTF {`#1} < {"41} + \int_compare:nNnTF {`#1} < { "41 } { \str_if_contains_char:NNTF \c_str_bytes_percent_encode_not_str #1 { #1 } { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } { \str_if_contains_char:NNTF \c_str_bytes_percent_encode_str #1 @@ -1798,6 +1888,14 @@ % \subsubsection{UTF-8 support} % % \begin{macro}{\str_native_from_UTF_viii:NN} +% \begin{macro}[aux] +% { +% \str_native_from_UTF_viii_aux_i:N, +% \str_native_from_UTF_viii_aux_ii:nN, +% \str_native_from_UTF_viii_aux_iii:N, +% \str_native_from_UTF_viii_aux_iv:, +% \str_native_from_UTF_viii_aux_v: +% } % \begin{macrocode} \cs_new_protected_nopar:Npn \str_native_from_UTF_viii:NN #1#2 { @@ -1812,19 +1910,18 @@ \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_i:N #1 { \quark_if_recursion_tail_stop:N #1 - \int_set:Nn \l_str_char_int { `#1 } - \str_native_from_UTF_viii_aux_ii:nN {128} \c_one - \str_native_from_UTF_viii_aux_ii:nN {64} \c_zero - \str_native_from_UTF_viii_aux_ii:nN {32} \c_two - \str_native_from_UTF_viii_aux_ii:nN {16} \c_three - \str_native_from_UTF_viii_aux_ii:nN {8} \c_four + \int_set:Nn \l_str_char_int {`#1} + \str_native_from_UTF_viii_aux_ii:nN { 128 } \c_one + \str_native_from_UTF_viii_aux_ii:nN { 64 } \c_zero + \str_native_from_UTF_viii_aux_ii:nN { 32 } \c_two + \str_native_from_UTF_viii_aux_ii:nN { 16 } \c_three + \str_native_from_UTF_viii_aux_ii:nN { 8 } \c_four \msg_error:nnx { str } { utf8-invalid-byte } {#1} \use_i:nnn \str_native_from_UTF_viii_aux_i:N \q_stop \str_native_from_UTF_viii_aux_iii:N } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_ii:nN #1#2 +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_ii:nN #1#2 { \int_compare:nNnTF \l_str_char_int < {#1} { @@ -1833,8 +1930,7 @@ } { \int_sub:Nn \l_str_char_int {#1} } } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_iii:N #1 +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_iii:N #1 { \int_compare:nNnTF \l_str_bytes_int < \c_two { \str_native_from_UTF_viii_aux_iv: #1 } @@ -1844,11 +1940,11 @@ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \quark_if_recursion_tail_stop:N #1 - \tex_multiply:D \l_str_char_int by 64 \scan_stop: % no interface?? + \tex_multiply:D \l_str_char_int by 64 \scan_stop: \int_decr:N \l_str_bytes_int - \int_compare:nNnTF {`#1} < {128} + \int_compare:nNnTF {`#1} < { 128 } { \use_ii:nn } - { \int_compare:nNnTF {`#1} < {192} } + { \int_compare:nNnTF {`#1} < { 192 } } { \int_add:Nn \l_str_char_int { `#1 - 128 } \str_native_from_UTF_viii_aux_iii:N @@ -1859,49 +1955,35 @@ } } } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_iv: +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_iv: { \int_compare:nNnTF \l_str_bytes_int = \c_zero { \msg_error:nnx { str } { utf8-extra-byte } { \int_eval:n { \l_str_char_int + 128 } } } + { \str_native_from_UTF_viii_aux_v: } + \str_native_from_UTF_viii_aux_i:N + } +\pdftex_if_engine:TF + { + \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_v: { - \pdftex_if_engine:TF + \int_compare:nNnTF { \l_str_char_int } < { 256 } + { \str_aux_convert_store: } { - \int_compare:nNnTF { \l_str_char_int } < { 256 } - { \str_aux_convert_store: } - { - \msg_error:nnx { str } { utf8-pdftex-overflow } - { \int_use:N \l_str_char_int } - } + \msg_error:nnx { str } { utf8-pdftex-overflow } + { \int_use:N \l_str_char_int } } - { \str_aux_convert_store: } } - \str_native_from_UTF_viii_aux_i:N - } -\msg_new:nnn { str } { utf8-invalid-byte } - { - \int_compare:nNnTF {`#1} < {256} - { Byte~number~\int_eval:n {`#1}~invalid~in~utf-8~encoding. } - { The~character~number~\int_eval:n {`#1}~is~not~a~byte. } } -\msg_new:nnn { str } { utf8-missing-byte } - { The~byte~number~\int_eval:n {`#1}~is~not~a~valid~continuation~byte. } -\msg_new:nnn { str } { utf8-extra-byte } - { The~byte~number~\int_eval:n {`#1}~is~only~valid~as~a~continuation~byte. } -\msg_new:nnnn { str } { utf8-premature-end } - { Incomplete~last~utf8~character. } { - The~sequence~of~byte~that~I~need~to~convert~to~utf8 \ - ended~before~the~last~character~was~complete.~Perhaps \ - it~was~mistakenly~truncated? + \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_v: + { \str_aux_convert_store: } } -\msg_new:nnn { str } { utf8-pdftex-overflow } - { The~character~number~#1~is~too~big~for~pdfTeX. } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}{\str_UTF_viii_from_native:NN} % \begin{macrocode} @@ -1916,14 +1998,14 @@ \cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_i:N #1 { \int_set:Nn \l_str_char_int {`#1} - \int_compare:nNnTF \l_str_char_int < {128} + \int_compare:nNnTF \l_str_char_int < { 128 } { \tl_gput_right:Nx \g_str_result_tl {#1} } { \tl_gclear:N \g_str_tmpa_tl \int_set:Nn \l_str_bytes_int { 64 } - \str_UTF_viii_from_native_aux_ii:n {32} - \str_UTF_viii_from_native_aux_ii:n {16} - \str_UTF_viii_from_native_aux_ii:n {8} + \str_UTF_viii_from_native_aux_ii:n { 32 } + \str_UTF_viii_from_native_aux_ii:n { 16 } + \str_UTF_viii_from_native_aux_ii:n { 8 } \ERROR % somehow the unicode char was > "1FFFFF > "10FFFF \tl_gclear:N \g_str_tmpa_tl \use_none:n \q_stop @@ -1936,29 +2018,60 @@ { \str_aux_convert_store:NNn \tl_gput_left:Nx \g_str_tmpa_tl - { 128 + \int_mod:nn { \l_str_char_int } {64} } - \tex_divide:D \l_str_char_int by 64 \scan_stop: % no interface?? + { 128 + \int_mod:nn { \l_str_char_int } { 64 } } + \tex_divide:D \l_str_char_int by 64 \scan_stop: \int_add:Nn \l_str_bytes_int {#1} \int_compare:nNnT \l_str_char_int < {#1} { \use_none_delimit_by_q_stop:w } } % \end{macrocode} % \end{macro} +% +% \subsection{Messages} +% +% \begin{macrocode} +\msg_new:nnnn { str } { x-missing-brace } + { Missing~closing~brace~in~ \token_to_str:N \x ~byte~sequence. } + { + You~wrote~something~like~ + `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ + The~closing~brace~is~missing. + } +\msg_new:nnn { str } { utf8-invalid-byte } + { + \int_compare:nNnTF {`#1} < { 256 } + { Byte~number~ \int_eval:n {`#1} ~invalid~in~utf-8~encoding. } + { The~character~number~ \int_eval:n {`#1} ~is~not~a~byte. } + } +\msg_new:nnn { str } { utf8-missing-byte } + { The~byte~number~ \int_eval:n {`#1} ~is~not~a~valid~continuation~byte. } +\msg_new:nnn { str } { utf8-extra-byte } + { The~byte~number~ \int_eval:n {`#1} ~is~only~valid~as~a~continuation~byte. } +\msg_new:nnnn { str } { utf8-premature-end } + { Incomplete~last~UTF8~character. } + { + The~sequence~of~byte~that~to~be~converted~to~UTF8~ + ended~before~the~last~character~was~complete.~Perhaps~ + it~was~mistakenly~truncated? + } +\msg_new:nnn { str } { utf8-pdftex-overflow } + { The~character~number~#1~is~too~big~for~pdfTeX. } +% \end{macrocode} % % \subsection{Deprecated string functions} % -% \begin{macro}{\str_length_skip_spaces:N,\str_length_skip_spaces:n} +% \begin{macro}{\str_length_skip_spaces:N, \str_length_skip_spaces:n} % The naming scheme is a little bit more consistent % with \enquote{ignore_spaces} instead of \enquote{skip_spaces}. % \begin{macrocode} -\cs_gset:Npn \str_length_skip_spaces:N +\cs_set:Npn \str_length_skip_spaces:N { \exp_args:No \str_length_skip_spaces:n } -\cs_gset_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n +\cs_set_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n % \end{macrocode} % \end{macro} % % \begin{macrocode} -%</package> +%</initex|package> % \end{macrocode} % % \end{implementation} diff --git a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx index 103934d5fe8..30a58c18817 100644 --- a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx @@ -36,7 +36,7 @@ % %<*driver|package> \RequirePackage{xparse} -\GetIdInfo$Id: xcoffins.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: xcoffins.dtx 2963 2011-11-15 22:06:41Z joseph $ {L3 Experimental design-level coffins} %</driver|package> %<*driver> @@ -583,6 +583,47 @@ % coffin is therefore analogous to carrying out an alignment where the % \enquote{parent} coffin is the current insertion point. % \end{function} +% +% \section{Measuring coffins} +% +% There are places in the design process where it is useful to be able to +% measure coffins outside of pole-setting procedures. +% +% \begin{function}{\CoffinDepth} +% \begin{syntax} +% \cs{CoffinDepth} \meta{coffin} +% \end{syntax} +% Calculates the depth (below the baseline) of the \meta{coffin} +% in a form suitable for use in a \meta{dimension expression}, for example +% |\setlength{\mylength}{\CoffinDepth\ExampleCoffin}|. +% \end{function} +% +% \begin{function}{\CoffinHeight} +% \begin{syntax} +% \cs{CoffinHeight} \meta{coffin} +% \end{syntax} +% Calculates the height (above the baseline) of the \meta{coffin} +% in a form suitable for use in a \meta{dimension expression}, for example +% |\setlength{\mylength}{\CoffinHeight\ExampleCoffin}|. +% \end{function} +% +% \begin{function}{\CoffinTotalHeight} +% \begin{syntax} +% \cs{CoffinTotalHeight} \meta{coffin} +% \end{syntax} +% Calculates the total height of the \meta{coffin} +% in a form suitable for use in a \meta{dimension expression}, for example +% |\setlength{\mylength}{\CoffinTotalHeight\ExampleCoffin}|. +% \end{function} +% +% \begin{function}{\CoffinWidth} +% \begin{syntax} +% \cs{CoffinHeight} \meta{coffin} +% \end{syntax} +% Calculates the width of the \meta{coffin} in a form +% suitable for use in a \meta{dimension expression}, for example +% |\setlength{\mylength}{\CoffinWidth\ExampleCoffin}|. +% \end{function} % % \section{Diagnostic functions} % @@ -839,6 +880,22 @@ % \end{macro} % \end{macro} % \end{macro} +% +% \begin{macro}{\CoffinDepth, \CoffinHeight, \CoffinTotalHeigth, \CoffinWidth} +% Nothing too complex, except that the total height is set up as an +% expression so that it will act correctly if prefixed with a negative +% sign, \emph{etc.} +% \begin{macrocode} +\NewDocumentCommand \CoffinDepth { m } + { \coffin_dp:N #1 } +\NewDocumentCommand \CoffinHeight { m } + { \coffin_ht:N #1 } +\NewDocumentCommand \CoffinTotalHeight { m } + { \dim_eval:w \coffin_ht:N #1 + \coffin_dp:N #1 \dim_eval_end: } +\NewDocumentCommand \CoffinWidth { m } + { \coffin_wd:N #1 } +% \end{macrocode} +% \end{macro} % % \begin{macro}{\DisplayCoffinHandles} % Displaying all of the handles is a bit easier, as there is no need diff --git a/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx b/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx index fa54ac37e59..57fbb9ecca4 100644 --- a/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx @@ -37,7 +37,7 @@ % %<*driver|package> \RequirePackage{l3names} -\GetIdInfo$Id: l3galley.dtx 2899 2011-10-09 21:22:06Z joseph $ +\GetIdInfo$Id: l3galley.dtx 2938 2011-10-25 18:41:20Z joseph $ {L3 Experimental galley code} %</driver|package> %<*driver> @@ -535,10 +535,22 @@ % paragraph started. % \end{variable} % -% \begin{variable}{\g_galley_whatsit_previous_hook_tl} +% \begin{variable}{\g_galley_whatsit_previous_tl} % Token list for whatsits to be inserted at the very end of the last % paragraph started. % \end{variable} +% +% \section{Additional effects} +% +% \begin{function}{\galley_end_par:n} +% \begin{syntax} +% \cs{galley_end_par:n} \Arg{tokens} +% \end{syntax} +% Adds the \meta{tokens} to the material collected for the last paragraph +% before finalising the last paragraph in the usual way. This function should +% therefore be the \emph{first} non-expandable entry used when a function +% needs to add tokens to the preceding paragraph. +% \end{function} % % \section{Internal variables} % @@ -641,6 +653,17 @@ \package_check_loaded_expl: %</package> % \end{macrocode} +% +% \subsection{TO BE MOVED} +% +% \begin{macro}{\scan_marker_new:N, \scan_marker_new:c} +% Temporary: needs to go somewhere else if this concept is retained. +% \begin{macrocode} +\cs_new_protected:Npn \scan_marker_new:N #1 + { \cs_new_eq:NN #1 \scan_stop: } +\cs_generate_variant:Nn \scan_marker_new:N { c } +% \end{macrocode} +% \end{macro} % % \subsection{Support items} % @@ -755,11 +778,11 @@ % \begin{macrocode} \bool_new:N \g_galley_begin_level_bool \bool_new:N \l_galley_begin_level_bool -\bool_gset_true:N \g_galley_begin_level_bool % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_galley_omit_next_indent_bool, \l_galley_omit_next_indent_bool} +% \begin{variable} +% {\g_galley_omit_next_indent_bool, \l_galley_omit_next_indent_bool} % A global flag is needed for suppressing indentation of the next % paragraph. This does not need a \enquote{running} version since that % should be handled using the \texttt{justification} object: the two @@ -907,9 +930,10 @@ \bool_gset_false:N \g_galley_parshape_set_bool \int_gzero:N \g_galley_previous_par_lines_int \tl_gclear:N \g_galley_restore_running_tl - \tl_gclear:N \g_galley_whatsits_previous_tl - \tl_gclear:N \g_galley_whatsits_next_tl + \tl_gclear:N \g_galley_whatsit_previous_tl + \tl_gclear:N \g_galley_whatsit_next_tl } +\galley_initialise_variables: % \end{macrocode} % \end{macro} % @@ -955,10 +979,10 @@ \g_galley_previous_par_lines_int \tl_set_eq:NN \l_galley_restore_running_tl \g_galley_restore_running_tl - \tl_set_eq:NN \l_galley_whatsits_previous_tl - \g_galley_whatsits_previous_tl - \tl_set_eq:NN \l_galley_whatsits_next_tl - \g_galley_whatsits_next_tl + \tl_set_eq:NN \l_galley_whatsit_previous_tl + \g_galley_whatsit_previous_tl + \tl_set_eq:NN \l_galley_whatsit_next_tl + \g_galley_whatsit_next_tl } \cs_new_protected_nopar:Npn \galley_restore_parameters: { @@ -984,10 +1008,10 @@ \l_galley_previous_par_lines_int \tl_gset_eq:NN \g_galley_restore_running_tl \l_galley_restore_running_tl - \tl_gset_eq:NN \g_galley_whatsits_previous_tl - \l_galley_whatsits_previous_tl - \tl_gset_eq:NN \g_galley_whatsits_next_tl - \l_galley_whatsits_next_tl + \tl_gset_eq:NN \g_galley_whatsit_previous_tl + \l_galley_whatsit_previous_tl + \tl_gset_eq:NN \g_galley_whatsit_next_tl + \l_galley_whatsit_next_tl } % \end{macrocode} % \end{macro} @@ -1025,13 +1049,46 @@ % \end{macro} % % \subsection{The \cs{par} token} +% +% \begin{variable}{\s_par_omit} +% Used to indicate that a paragraph should be omitted. +% \begin{macrocode} +\scan_marker_new:N \s_par_omit +% \end{macrocode} +% \end{variable} % % \begin{macro}{\galley_std_par:} -% In vertical mode, the standard paragraph insert is just the primitive -% \cs{tex_par:D}. +% \begin{macro}[aux]{\galley_std_par_aux_i:, \galley_std_par_aux_ii:} +% \begin{macro}[aux]{\galley_std_par_aux:N} +% The idea here is to expand the next token in exactly the same way as \TeX{} +% would do anyway. The \texttt{f}-type expansion will ignore any protection, +% but will stop at a scan marker. Thus the code can test for an +% \enquote{omit paragraph} marker. % \begin{macrocode} \cs_new_protected_nopar:Npn \galley_std_par: { + \s_par_omit + \exp_after:wN \galley_std_par_aux_i: \tex_romannumeral:D - `0 + } +\cs_new_protected:Npn \galley_std_par_aux_i: + { + \peek_meaning:NTF \s_par_omit + { \galley_std_par_aux:N } + { \galley_std_par_aux_ii: } + } +\cs_new_protected:Npn \galley_std_par_aux:N #1 + { + \str_if_eq:xxF {#1} { \s_par_omit } + { + \galley_std_par_aux_ii: + #1 + } + } +% \end{macrocode} +% No marker, so really insert a paragraph. In vertical mode, +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_std_par_aux_ii: + { \mode_if_vertical:TF { \tex_par:D } % \end{macrocode} @@ -1064,6 +1121,24 @@ } % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\galley_end_par:n} +% Inserts tokens such that they are appended to the end of the last +% paragraph, using the paragraph-omitting system. +% \begin{macrocode} +\cs_new_protected:Npn \galley_end_par:n #1 + { + \s_par_omit + \bool_if:nF \g_galley_begin_level_bool + { + #1 + \galley_std_par: + } + } +% \end{macrocode} +% \end{macro} % % \begin{macro}{\par} % The meaning of the token \cs{par} itself starts off as a standard @@ -1375,14 +1450,14 @@ { \g_galley_whatsit_next_tl \tl_gclear:N \g_galley_whatsit_next_tl - \bool_if:NT \g_galley_omit_next_indent_bool + \bool_if:NF \g_galley_omit_next_indent_bool { - \box_if_empty:NF \g_galley_last_box + \box_if_empty:NF \g_galley_last_box { \hbox_to_wd:nn \l_galley_par_indent_dim { } } } \skip_horizontal:N \l_galley_par_begin_skip \g_galley_par_begin_hook_tl - \bool_gset_true:N \g_galley_omit_next_indent_bool + \bool_gset_false:N \g_galley_omit_next_indent_bool } % \end{macrocode} % \end{macro} @@ -2066,16 +2141,57 @@ % \end{macro} % \end{macro} % \end{macro} +% % -% \subsection{\LaTeXe{} fixes} -% +% \subsection{\LaTeXe{} functions} +% % \begin{macrocode} %<*package> % \end{macrocode} % +% \begin{macro}{\nobreak} +% \begin{macro}{\noindent} +% \begin{macro}{\vspace} +% In package mode, some of \LaTeXe{}'s functions are re-implemented using +% the galley system. Not all of the optional arguments currently work! +% \begin{macrocode} +\cs_set_protected_nopar:Npn \nobreak + { \bool_gset_true:N \g_galley_no_break_next_bool } +% \end{macrocode} +% The \tn{noindent} primitive will causes problems, as it is used by +% \LaTeXe{} documents to implicitly leave vertical mode as well as to +% prevent indentation. Rather than patch \emph{every} place where we +% need leave vertical mode, at the moment we stick with the primitive as +% well as setting the galley flag. +% \begin{macrocode} +\cs_set_protected_nopar:Npn \noindent + { + \tex_noindent:D + \bool_gset_false:N \g_galley_omit_next_indent_bool + } +\cs_set_protected_nopar:Npn \vspace #1 + { + \@ifstar + { \galley_set_user_vspace:n {#1} } + { \galley_set_user_vspace:n {#1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{\LaTeXe{} fixes} +% % Purely for testing, some internal \LaTeXe{} functions are altered to work % with the mechanism here. This material is not comprehensive: additions are % made as-needed for test purposes. +% +% \begin{macro}{\@@par} +% The primitive is moved as otherwise the clever skipping code will fail. +% \begin{macrocode} +\cs_set_eq:NN \@@par \galley_std_par: +% \end{macrocode} +% \end{macro} % % \begin{macro}{\@afterheading} % Set some flags and hope for the best! @@ -2098,6 +2214,8 @@ % \begin{macrocode} \cs_set_protected:Npn \@hangfrom #1 { + \bool_gset_true:N \g_galley_omit_next_indent_bool + \leavevmode \setbox \@tempboxa = \hbox { {#1} } \galley_parshape_single_par:nnnN \c_one @@ -2105,28 +2223,12 @@ \c_zero_dim \c_false_bool \bool_gset_true:N \g_galley_no_break_next_bool - \bool_gset_true:N \galley_omit_next_indent_bool + \bool_gset_true:N \g_galley_omit_next_indent_bool \box \@tempboxa } % \end{macrocode} % \end{macro} % -% \begin{macro}{\@makefntext} -% Here, the paragraph indent is set up using the template system, and -% as in other places the code ensures that the label is set in horizontal -% mode. The flag used for omitting the indent is once again the galley -% one. -% \begin{macrocode} -\cs_set_protected:Npn \@makefntext #1 - { - \UseTemplate { justification } { std } { indent-width = 1 em } - \bool_gset_true:N \galley_omit_next_indent_bool - \galley_leave_vmode: - \hb@xt@ 1.8em { \hss \@makefnmark } #1 - } -% \end{macrocode} -% \end{macro} -% % \begin{macrocode} %</package> % \end{macrocode} diff --git a/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx index 0dc1c85b610..f573668fde8 100644 --- a/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx @@ -37,7 +37,7 @@ % %<*driver|package> \RequirePackage{l3names} -\GetIdInfo$Id: xgalley.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: xgalley.dtx 2922 2011-10-17 19:01:04Z joseph $ {L3 Experimental galley} %</driver|package> %<*driver> @@ -1575,24 +1575,6 @@ } % \end{macrocode} % -% \subsection{\LaTeXe{} functions} -% -% \begin{macro}{\nobreak} -% \begin{macro}{\noindent} -% \begin{macro}{\vspace} -% In package mode, some of \LaTeXe{}'s functions are re-implemented using -% the galley system. Not all of the optional arguments currently work! -% \begin{macrocode} -\RenewDocumentCommand \nobreak { } - { \bool_gset_true:N \g_galley_no_break_next_bool } -\RenewDocumentCommand \noindent { } - { \bool_gset_false:N \g_galley_omit_next_indent_bool } -\RenewDocumentCommand \vspace { s m } { \galley_set_vspace_user:n {#2} } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% % \begin{macrocode} %</package> % \end{macrocode} diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty index 29045fcdea9..07d568d94e9 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty @@ -46,113 +46,121 @@ %% %% ----------------------------------------------------------------------- \RequirePackage{expl3} -\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: l3regex.dtx 2943 2011-10-30 15:59:43Z bruno $ {L3 Experimental Regular Expressions} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} \RequirePackage{l3str} +\int_const:Nn \c_forty_eight { 48 } +\int_const:Nn \c_fifty_eight { 58 } +\int_const:Nn \c_sixty_five { 65 } +\int_const:Nn \c_ninety_one { 91 } +\int_const:Nn \c_ninety_seven { 97 } +\int_const:Nn \c_one_hundred_twenty_three { 123 } +\int_const:Nn \c_one_hundred_twenty_seven { 127 } \cs_new:Npn \regex_tmp:w { } \tl_new:N \g_regex_tmpa_tl \tl_new:N \l_regex_tmpa_tl \tl_new:N \l_regex_tmpb_tl \int_new:N \l_regex_tmpa_int -\tl_new:N \l_regex_pattern_str +\int_new:N \l_regex_tmpb_int \int_new:N \l_regex_max_state_int \int_new:N \l_regex_left_state_int \int_new:N \l_regex_right_state_int -\seq_new:N \l_regex_left_state_seq -\seq_new:N \l_regex_right_state_seq +\seq_new:N \l_regex_group_begin_seq +\seq_new:N \l_regex_group_end_seq \int_new:N \l_regex_capturing_group_int \seq_new:N \l_regex_capturing_group_seq \tl_new:N \l_regex_one_or_group_tl -\int_new:N \l_regex_repetition_int -\bool_new:N \l_regex_look_behind_bool -\tl_new:N \l_regex_look_behind_str \cs_new_eq:NN \regex_build_tmp_class:n \use_none:n \bool_new:N \l_regex_class_bool \tl_new:N \l_regex_class_tl \tl_const:Nn \c_regex_d_tl { - \regex_item_range:nn {48} {57} % 0--9 + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 } \tl_const:Nn \c_regex_D_tl { - \regex_item_more:n {57} % 9 - \regex_item_range:nn {0} {47} % 0 + \regex_item_geq:n { \c_fifty_eight } % > `9 + \regex_item_range:nn { \c_zero } { 47 } % 0 } \tl_const:Nn \c_regex_h_tl { - \regex_item_equal:n {32} % space - \regex_item_equal:n {9} % tab + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_equal:n { \c_nine } % tab } \tl_const:Nn \c_regex_H_tl { - \regex_item_neq:n {32} % space - \regex_item_neq:n {9} % tab - \regex_break_true:w + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_ten } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab } \tl_const:Nn \c_regex_s_tl { - \regex_item_equal:n {32} % space - \regex_item_neq:n {11} % vtab - \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_range:nn { \c_nine } { \c_ten } % tab, lf + \regex_item_range:nn { \c_twelve } { \c_thirteen } % ff, cr } \tl_const:Nn \c_regex_S_tl { - \regex_item_more:n {32} % > space - \regex_item_range:nn {14} {31} % tab < ... < space - \regex_item_range:nn {0} {8} % < tab - \regex_item_equal:n {11} % vtab + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_fourteen } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab + \regex_item_equal:n { \c_eleven } % vtab } \tl_const:Nn \c_regex_v_tl { - \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + \regex_item_range:nn { \c_ten } { \c_thirteen } % lf, vtab, ff, cr } \tl_const:Nn \c_regex_V_tl { - \regex_item_more:n {13} % cr - \regex_item_range:nn {0} {9} % < lf + \regex_item_geq:n { \c_fourteen } % >cr + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } \tl_const:Nn \c_regex_w_tl { - \regex_item_range:nn {97} {122} % a--z - \regex_item_range:nn {65} {90} % A--Z - \regex_item_range:nn {48} {57} % 0--9 - \regex_item_equal:n {95} % _ + \regex_item_range:nn { \c_ninety_seven } { 122 } % a--z + \regex_item_range:nn { \c_sixty_five } { 90 } % A--Z + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 + \regex_item_equal:n { 95 } % _ } \tl_const:Nn \c_regex_W_tl { - \regex_item_range:nn {0} {47} % <`0 - \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) - \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) - \regex_item_equal:n {96} % ` - \regex_item_more:n {122} % z + \regex_item_range:nn { \c_zero } { 47 } % <`0 + \regex_item_range:nn { \c_fifty_eight } { 64 } % (`9+1)--(`A-1) + \regex_item_range:nn { \c_ninety_one } { 94 } % (`Z+1)--(`_-1) + \regex_item_equal:n { 96 } % ` + \regex_item_geq:n { \c_one_hundred_twenty_three } % z } \tl_const:Nn \c_regex_N_tl { - \regex_item_neq:n {10} % lf - \regex_break_true:w + \regex_item_geq:n { \c_eleven } % > lf + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } -\tl_new:N \l_regex_query_str -\int_new:N \l_regex_start_step_int -\int_new:N \l_regex_current_char_int +\tl_new:N \l_regex_query_other_str \int_new:N \l_regex_current_step_int +\int_new:N \l_regex_start_step_int +\int_new:N \l_regex_success_step_int \int_new:N \l_regex_unique_step_int +\int_new:N \l_regex_current_char_int +\int_new:N \l_regex_last_char_int +\int_new:N \l_regex_case_changed_char_int +\bool_new:N \l_regex_caseless_bool \int_new:N \l_regex_current_state_int \prop_new:N \l_regex_current_submatches_prop +\prop_new:N \l_regex_success_submatches_prop \int_new:N \l_regex_max_index_int \tl_new:N \l_regex_every_match_tl +\bool_new:N \l_regex_success_bool \cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} \cs_new_protected:Npn \regex_last_match_empty_yes:F { \int_compare:nNnF \l_regex_start_step_int = \l_regex_current_step_int } \cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F -\bool_new:N \l_regex_success_bool \bool_new:N \l_regex_success_empty_bool -\int_new:N \l_regex_success_step_int -\prop_new:N \l_regex_success_submatches_prop \bool_new:N \l_regex_fresh_thread_bool \seq_new:N \g_regex_submatches_seq \int_new:N \g_regex_match_count_int +\seq_new:N \g_regex_extract_all_seq \seq_new:N \g_regex_split_seq \tl_new:N \l_regex_replacement_tl \tl_new:N \g_regex_replaced_str @@ -169,40 +177,43 @@ \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w } } -\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N -\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 - { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } -\cs_new_protected_nopar:Npn \regex_build_error:n #1 +\cs_new_nopar:Npn \regex_query_substr:NN #1#2 { - \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} - { - \prg_case_int:nnn {#1} - { - {-999} {File~not~found} - {-998} {Unsupported~construct} - {-997} {The~regular~expression~is~too~large~(32768~states).} - {1} {\iow_char:N\\~at~end~of~pattern } - {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} - {6} {Missing~terminating~\iow_char:N\]~for~character~class } - {7} {Invalid~escape~sequence~in~character~class} - {8} {Range~out~of~order~in~character~class} - {22} {Mismatched~parentheses} - {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} - } - { Internal~bug. } - } + \exp_after:wN \regex_query_substr_aux:NN + \exp_after:wN #1 + \exp_after:wN #2 + \l_regex_query_other_str + \q_stop + } +\cs_new:Npn \regex_query_substr_aux:NN #1#2 + { + \str_skip_do:nn {#1} + { \str_collect_do:nn { #2 - #1 } { \use_i_delimit_by_q_stop:nw } } + } +\cs_new_protected_nopar:Npn \regex_seq_pop:N #1 + { + \seq_pop:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl + } +\cs_new_protected_nopar:Npn \regex_seq_get:N #1 + { + \seq_get:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl } -\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } -\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} -\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} -\cs_new_eq:NN \regex_break_point:TF \use_ii:nn -\cs_new_nopar:Npn \regex_item_equal:n #1 +\cs_new_protected_nopar:Npn \regex_item_dot:T + { \int_compare:nNnF \l_regex_current_char_int = \c_minus_one } +\cs_new_protected_nopar:Npn \regex_break_true:w + #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_protected_nopar:Npn \regex_break_false:w + #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_protected_nopar:Npn \regex_break_point:TF #1 #2 { #2 } +\cs_new_protected_nopar:Npn \regex_item_caseful_equal:n #1 { \if_num:w #1 = \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_range:nn #1 #2 +\cs_new_protected_nopar:Npn \regex_item_caseful_range:nn #1 #2 { \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \reverse_if:N \if_num:w #2 < \l_regex_current_char_int @@ -210,24 +221,60 @@ \fi: \fi: } -\cs_new_nopar:Npn \regex_item_less:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseful_geq:n #1 { - \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_more:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseless_equal:n #1 { - \if_num:w #1 < \l_regex_current_char_int + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \if_num:w #1 = \l_regex_case_changed_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_neq:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseless_range:nn #1 #2 { - \if_num:w #1 = \l_regex_current_char_int - \exp_after:wN \regex_break_false:w + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \reverse_if:N \if_num:w #2 < \l_regex_case_changed_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: \fi: } +\cs_new_protected_nopar:Npn \regex_item_caseless_geq:n #1 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } +\cs_new:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } +\cs_new:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } +\cs_new_protected_nopar:Npn \regex_build_caseless: + { + \bool_set_true:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseless_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseless_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseless_geq:n } + } +\cs_new_protected_nopar:Npn \regex_build_caseful: + { + \bool_set_false:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } + } \cs_new_protected:Npn \regex_get_digits:nw #1 { \tex_afterassignment:D \regex_tmp:w @@ -235,65 +282,71 @@ { \exp_not:n {#1} { \if_false: } } \fi: - \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_aux:N #1 +\cs_new_nopar:Npn \regex_get_digits_aux:NN #1#2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \else: - \if_charcode:w \c_space_token \exp_not:N #1 + \if_meaning:w \regex_build_raw:N #1 + \if_charcode:w \c_space_token \exp_not:N #2 \else: - \regex_get_digits_end:w + \if_num:w 9 < 1 \exp_not:N #2 \exp_stop_f: + #2 + \else: + \regex_get_digits_end:w #1 #2 + \fi: \fi: + \else: + \regex_get_digits_end:w #1 #2 \fi: - #1 \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_end:w - \fi: \fi: #1 \regex_get_digits_aux:N +\cs_new_nopar:Npn \regex_get_digits_end:w #1 \fi: #2 \regex_get_digits_aux:NN { - \fi: \fi: + \fi: #2 \if_false: { { \fi: } } #1 } +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w \c_nine < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: \else: \prg_return_false: \fi: + } \prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } { - \if_num:w `#1 < 97 \exp_stop_f: - \if_num:w `#1 < 58 \exp_stop_f: - \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \if_num:w `#1 < \c_thirty_two + \prg_return_false: \else: \prg_return_true: \fi: + \else: \prg_return_false: \fi: \else: - \if_num:w \int_eval:w `#1 / 26 = \c_three - \prg_return_false: - \else: - \prg_return_true: - \fi: + \if_num:w `#1 < \c_sixty_five + \prg_return_true: \else: \prg_return_false: \fi: \fi: \else: - \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_true: \else: \prg_return_false: \fi: \else: - \prg_return_false: + \if_num:w `#1 < \c_one_hundred_twenty_seven + \prg_return_true: \else: \prg_return_false: \fi: \fi: \fi: } \prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } { - \if_num:w `#1 < 91 \exp_stop_f: - \if_num:w `#1 < 65 \exp_stop_f: - \if_num:w \c_nine < 1 #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \prg_return_false: \else: \prg_return_true: \fi: \else: - \prg_return_true: + \if_num:w `#1 < \c_sixty_five + \prg_return_false: \else: \prg_return_true: \fi: \fi: \else: - \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_false: \else: \prg_return_true: \fi: \else: \prg_return_false: \fi: @@ -301,8 +354,8 @@ } \cs_new_protected_nopar:Npn \regex_build_new_state: { - \int_compare:nNnTF \l_regex_max_state_int > {32766} - { \regex_build_error:n {-997} } + \int_compare:nNnTF \l_regex_max_state_int > { 32766 } + { \msg_error:nn { regex } { -997 } } { \int_incr:N \l_regex_max_state_int \tex_toks:D \l_regex_max_state_int { \s_regex_toks } @@ -314,15 +367,15 @@ { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { #1 { \int_use:N #2 } } + { #1 { \int_eval:n { #2 - \l_regex_left_state_int } } } } \cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { - #1 { \int_use:N #2 } - #3 { \int_use:N #4 } + #1 { \int_eval:n { #2 - \l_regex_left_state_int } } + #3 { \int_eval:n { #4 - \l_regex_left_state_int } } } } \cs_new_protected:Npn \regex_build:n #1 @@ -363,25 +416,19 @@ \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N \int_set_eq:NN \l_regex_capturing_group_int \c_zero - \int_zero:N \l_regex_left_state_int - \int_zero:N \l_regex_right_state_int \int_zero:N \l_regex_max_state_int \regex_build_new_state: \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_action_start_wildcard:nn - { \int_use:N \l_regex_left_state_int } - { \int_use:N \l_regex_right_state_int } - } + { \regex_action_start_wildcard:nn {0} {1} } } \cs_new_protected_nopar:Npn \regex_build_end: { \seq_if_empty:NF \l_regex_capturing_group_seq { - \regex_build_error:n {22} + \msg_error:nn { regex } {22} \prg_replicate:nn - { \seq_length:N \l_regex_capturing_group_seq } % ( + { \seq_length:N \l_regex_capturing_group_seq } { \regex_build_close_aux: \regex_build_group_: } } \regex_toks_put_right:Nx \l_regex_right_state_int @@ -399,7 +446,13 @@ \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_protected_nopar:cpn { regex_build_$: } % $ @@ -409,71 +462,72 @@ { \exp_not:N \int_compare:nNnT \c_minus_one = \l_regex_current_char_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ \cs_new_eq:Nc \regex_build_z: { regex_build_$: } %$ \cs_new_protected_nopar:Npn \regex_build_b: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } { } } } \cs_new_protected_nopar:Npn \regex_build_B: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF { } - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_protected_nopar:Npn \regex_if_word_boundary:TF { - \tl_if_empty:NTF \l_regex_look_behind_str - { \c_regex_w_tl } - { - \group_begin: - \cs_set_nopar:Npx \regex_tmp:w - { - \int_set:Nn \l_regex_current_char_int - { ` \str_head:N \l_regex_look_behind_str } - } - \regex_tmp:w - \c_regex_w_tl - \regex_break_point:TF - { \group_end: \c_regex_W_tl } - { \group_end: \c_regex_w_tl } - } + \group_begin: + \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl \regex_item_equal:n { -1 } } + { \group_end: \c_regex_w_tl } \regex_break_point:TF } \cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 { \cs_set:Npx \regex_build_tmp_class:n ##1 { - \exp_not:n { \exp_not:N \if_num:w } - \int_value:w `#1 = \l_regex_current_char_int - \regex_action_cost:n { ##1 } - \exp_not:n { \exp_not:N \fi: } + \regex_item_equal:n { \int_value:w `#1 } + \regex_break_point:TF { \regex_action_cost:n { ##1 } } { } } \regex_build_one_quantifier: } \cs_new_protected_nopar:cpn { regex_build_.: } { \cs_set:Npn \regex_build_tmp_class:n ##1 - { - \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int - \regex_action_cost:n {##1} - \exp_not:N \fi: - } + { \regex_item_dot:T { \regex_action_cost:n {##1} } } \regex_build_one_quantifier: } \cs_new_protected_nopar:Npn \regex_build_char_type:N #1 @@ -481,9 +535,7 @@ \cs_set:Npn \regex_build_tmp_class:n ##1 { \exp_not:N #1 - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } + \regex_break_point:TF { \regex_action_cost:n {##1} } { } } \regex_build_one_quantifier: } @@ -518,16 +570,8 @@ { \exp_not:o \l_regex_class_tl \bool_if:NTF \l_regex_class_bool - { - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } - } - { - \exp_not:N \regex_break_point:TF - { } - { \regex_action_cost:n {##1} } - } + { \regex_break_point:TF { \regex_action_cost:n {##1} } { } } + { \regex_break_point:TF { } { \regex_action_cost:n {##1} } } } \regex_build_one_quantifier: } @@ -553,7 +597,7 @@ \cs_new_protected_nopar:Npn \regex_class_single:N #1 { \tl_put_right:Nx \l_regex_class_tl - { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + { \regex_item_equal:n { \int_value:w `#1 } } } \cs_new_protected_nopar:Npn \regex_class_range:Nw #1#2#3 { @@ -572,14 +616,14 @@ \cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 { \if_num:w `#1 > `#2 \exp_stop_f: - \regex_build_error:n {8} + \msg_error:nn { regex } {8} \else: \tl_put_right:Nx \l_regex_class_tl { \if_num:w `#1 = `#2 \exp_stop_f: - \exp_not:N \regex_item_equal:n + \regex_item_equal:n \else: - \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \regex_item_range:nn { \int_value:w `#1 } \fi: { \int_value:w `#2 } } @@ -651,26 +695,29 @@ \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 } } -\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3#4 { - \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + \str_if_eq:xxTF + { \exp_not:n {#3#4} } + { \exp_not:N \regex_build_control:N \c_rbrace_str } { \tl_if_empty:nTF {#2} - { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { \regex_build_quantifier_aux:nnNN { {#1} } { n* } } { \int_compare:nNnT {#1} > {#2} - { \regex_build_error:n {4} } - \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + { \msg_error:nn { regex } {4} } + \regex_build_quantifier_aux:nnNN { {#1} {#2} } { nn } } } { \regex_build_quantifier_end:n { } \use:x { - \exp_not:n { \exp_args:No \tl_map_function:nN } - { \c_lbrace_str #1 #2 , } - \regex_build_raw:N + \exp_args:No \tl_map_function:nN + { \c_lbrace_str #1 , #2 } + \regex_build_raw:N } + #3 #4 } } \cs_new_protected_nopar:Npn \regex_build_quantifier_end:n #1 @@ -726,71 +773,27 @@ \regex_action_free:n \l_regex_tmpa_int } \cs_new_protected_nopar:Npn \regex_build_one_n: #1 - { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { %^^A safe \if nesting? - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } - } + { \prg_replicate:nn {#1} { \regex_build_one_: } } \cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: -\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_copy:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*: } } -\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*?: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*?: } } -\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:Nnn #1#2#3 { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - #2 - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } + \prg_replicate:nn {#2} { \regex_build_one_: } + \prg_replicate:nn {#3-#2} {#1} } +\cs_new_protected_nopar:Npx \regex_build_one_nn: + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_?: } } +\cs_new_protected_nopar:cpx { regex_build_one_nn?: } + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_??: } } \cs_new_protected_nopar:cpn { regex_build_(: } #1#2 { \str_if_eq:nnTF { #1 #2 } { \regex_build_control:N ? } @@ -806,10 +809,19 @@ \cs_new_protected_nopar:Npn \regex_build_open_aux: { \regex_build_new_state: - \seq_push:Nx \l_regex_left_state_seq - { \int_use:N \l_regex_left_state_int } - \seq_push:Nx \l_regex_right_state_seq - { \int_use:N \l_regex_right_state_int } + \seq_push:Nx \l_regex_group_begin_seq + { + \int_set:Nn \l_regex_left_state_int + { \int_use:N \l_regex_left_state_int } + } + \bool_if:NTF \l_regex_caseless_bool + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseless: } } + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseful: } } + \seq_push:Nx \l_regex_group_end_seq + { + \int_set:Nn \l_regex_right_state_int + { \int_use:N \l_regex_right_state_int } + } \regex_build_begin_alternation: } \cs_new_protected_nopar:cpn { regex_build_|: } @@ -820,7 +832,7 @@ \cs_new_protected_nopar:cpn { regex_build_): } { \seq_if_empty:NTF \l_regex_capturing_group_seq - { \regex_build_error:n {22} } + { \msg_error:nn { regex } { 22 } } { \regex_build_close_aux: \regex_build_group_quantifier: @@ -829,30 +841,41 @@ \cs_new_protected_nopar:Npn \regex_build_close_aux: { \regex_build_end_alternation: - \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl - \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + \regex_seq_pop:N \l_regex_group_begin_seq + \regex_seq_pop:N \l_regex_group_end_seq + \regex_seq_pop:N \l_regex_group_end_seq } \cs_new_protected_nopar:Npn \regex_build_begin_alternation: { \regex_build_new_state: - \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_seq_get:N \l_regex_group_begin_seq \regex_toks_put_right:Nx \l_regex_left_state_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } \cs_new_protected_nopar:Npn \regex_build_end_alternation: { - \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \regex_toks_put_right:Nx \l_regex_right_state_int - { \regex_action_free:n { \l_regex_tmpa_tl } } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \regex_seq_get:N \l_regex_group_end_seq + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } \cs_new_protected_nopar:Npn \regex_build_special_group:NN #1#2 { \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } { - \regex_build_error:n { -998 } + \msg_error:nn { regex } { -998 } \regex_build_control:N ( % ) \regex_build_raw:N ? #1 #2 @@ -863,6 +886,40 @@ \seq_push:Nx \l_regex_capturing_group_seq { * } \regex_build_open_aux: } +\cs_new_protected_nopar:Npn \regex_build_special_group_i: + { + \regex_build_options:NNN + + \regex_build_raw:N i + } +\cs_new_protected_nopar:cpn { regex_build_special_group_-: } + { + \regex_build_options:NNN - + } +\cs_new_protected_nopar:Npn \regex_build_options:NNN #1#2#3 + { + \token_if_eq_meaning:NNTF \regex_build_raw:N #2 + { + \cs_if_exist_use:cF { regex_build_option_#1#3: } + { \msg_error:nnx { regex } { unknown-option } { #3 } } + \regex_build_options:NNN #1 + } + { + \prg_case_str:nnn { #3 } + { % ( + { ) } { } + { - } { \regex_build_options:NNN - } + } + { \msg_error:nnx { regex } { invalid-in-option } { #3 } } + } + } +\cs_new_protected_nopar:cpn { regex_build_option_+i: } + { + \regex_build_caseless: + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } +\cs_new_protected_nopar:cpn { regex_build_option_-i: } + { \regex_build_caseful: } \cs_new_protected_nopar:Npn \regex_build_group_quantifier: { \tl_set:Nn \l_regex_one_or_group_tl { group } @@ -891,21 +948,33 @@ \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int \regex_build_new_state: \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int - \use:x + \regex_toks_put_left:Nx \l_regex_right_state_int + { + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_tmpa_int } % ^^A here we lie! + } + \cs_set:Npx \regex_tmp:w { \tex_toks:D \l_regex_tmpa_int - { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + { + \s_regex_toks + #1 { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } + \regex_tmp:w \regex_build_group_submatches:NN \l_regex_right_state_int \l_regex_left_state_int - \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int } \cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 { \regex_build_group_shift:N \regex_action_free:n + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int \regex_build_transition_aux:NN \regex_action_free:n #1 #2 \l_regex_tmpa_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } \cs_new_protected_nopar:cpn { regex_build_group_?: } { @@ -945,77 +1014,63 @@ \regex_action_free:n \l_regex_right_state_int \regex_action_free:n \l_regex_tmpa_int } -\cs_new_protected_nopar:Npn \regex_build_group_n: #1 +\cs_new_protected_nopar:Npn \regex_build_group_n_aux:n #1 { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x + \regex_toks_put_right:Nx \l_regex_right_state_int { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } + \regex_action_free:n + { \int_eval:n { \l_regex_max_state_int - \l_regex_left_state_int } } } - } -\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: -\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \int_set:Nn \l_regex_tmpb_int { \l_regex_max_state_int + \c_one } + \int_set:Nn \l_regex_max_state_int { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } + \l_regex_left_state_int - \c_one + + #1 * ( \l_regex_max_state_int - \l_regex_left_state_int + \c_one ) + } + \int_until_do:nNnn \l_regex_tmpb_int > \l_regex_max_state_int + { + \tex_toks:D \l_regex_tmpb_int = \tex_toks:D \l_regex_tmpa_int + \int_incr:N \l_regex_tmpa_int + \int_incr:N \l_regex_tmpb_int } } -\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n +\cs_new_protected_nopar:Npn \regex_build_group_n: #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: +\cs_new_protected_nopar:cpn { regex_build_group_n*: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN \regex_action_free:n \l_regex_tmpa_int - \use:x - { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \exp_not:N \fi: - } - } + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int } -\cs_new_protected:Npn \regex_if_state_free:nT #1 - { - \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int - \exp_after:wN \use_none:n - \else: - \exp_after:wN \use:n - \fi: +\cs_new_protected_nopar:cpn { regex_build_group_n*?: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*: } {#1} + } +\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*?: } {#1} } \cs_new_protected:Npn \regex_store_state:n #1 { @@ -1023,23 +1078,24 @@ \tex_skip:D \l_regex_max_index_int #1 sp \scan_stop: \regex_store_submatches:n {#1} } -\cs_new_protected_nopar:Npn \regex_state_use_submatches: +\cs_new_protected_nopar:Npn \regex_state_use_with_submatches: { \regex_state_use_aux:n { } } \cs_new_protected_nopar:Npn \regex_state_use: { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } -\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } -\cs_new_protected:Npn \regex_state_use_aux:n #1 - { - \regex_if_state_free:nT { \l_regex_current_state_int } - { - \tex_dimen:D \l_regex_current_state_int - = \l_regex_unique_step_int sp \scan_stop: - #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: - } +\cs_new_nopar:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected_nopar:Npn \regex_state_use_aux:n #1 + { + \if_num:w \tex_dimen:D \l_regex_current_state_int + < \l_regex_unique_step_int + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \exp_after:wN \l_regex_current_state_int + \fi: + \scan_stop: } \cs_new_protected_nopar:Npn \regex_disable_submatches: { - \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_state_use_with_submatches: \regex_state_use: \cs_set_eq:NN \regex_store_submatches:n \regex_protected_use_none:n \cs_set_eq:NN \regex_action_submatches:n @@ -1048,55 +1104,73 @@ \cs_new_protected:Npn \regex_protected_use_none:n #1 { } \cs_new_protected:Npn \regex_store_submatches:n #1 { - \exp_after:wN \regex_store_submatches_aux:Nw - \exp_after:wN \l_regex_current_submatches_prop - \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop - { #1 } + \tex_toks:D #1 \exp_after:wN + { + \tex_romannumeral:D + \exp_after:wN \regex_store_submatches_aux:w + \tex_the:D \tex_toks:D #1 + } } -\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks +\cs_new_protected:Npn \regex_store_submatches_aux:w #1 \s_regex_toks { - \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } - #2 - \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \regex_store_submatches_aux_ii:Nnnw + #1 + \regex_state_submatches:nn \c_minus_one \q_prop \s_regex_toks } -\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn - #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 +\cs_new_protected:Npn \regex_store_submatches_aux_ii:Nnnw + \regex_state_submatches:nn #1 #2 #3 \s_regex_toks { - \tex_toks:D #6 \exp_after:wN + \exp_after:wN \c_zero + \exp_after:wN \regex_state_submatches:nn \exp_after:wN { - \exp_after:wN \regex_state_submatches:nn \exp_after:wN - { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } - { #1 } - \regex_state_submatches:nn {#2} {#3} - \s_regex_toks - #5 + \int_value:w \int_eval:w + \l_regex_unique_step_int + \c_one + \exp_after:wN } + \exp_after:wN { \l_regex_current_submatches_prop } + \regex_state_submatches:nn {#1} {#2} + \s_regex_toks } \cs_new_protected:Npn \regex_state_submatches:nn #1#2 { \if_num:w #1 = \l_regex_unique_step_int - \tl_set:Nn \l_regex_current_submatches_prop {#2} + \tl_set:Nn \l_regex_current_submatches_prop { #2 } \fi: } \cs_new_protected:Npn \regex_match:n #1 { - \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \tl_set:Nx \l_regex_query_other_str { \tl_to_other_str:n {#1} } \regex_match_initial_setup: \regex_match_once: } \cs_new_protected_nopar:Npn \regex_match_once: { \regex_match_setup: - \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \exp_after:wN \regex_match_once_aux: \l_regex_query_other_str \q_recursion_tail \q_recursion_stop \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } } \cs_new_protected_nopar:Npn \regex_match_once_aux: - { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } + { + \int_compare:nNnTF \l_regex_start_step_int = \c_zero + { + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \regex_match_loop:N + } + { + \str_skip_do:nn + { \l_regex_start_step_int - \c_one } + { \regex_match_once_aux_ii:N } + } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux_ii:N #1 + { + \int_set:Nn \l_regex_current_char_int { `#1 } + \regex_match_loop:N + } \cs_new_protected_nopar:Npn \regex_match_initial_setup: { - \tl_clear:N \l_regex_look_behind_str \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } \int_set_eq:NN \l_regex_unique_step_int \c_minus_one @@ -1118,20 +1192,11 @@ \int_zero:N \l_regex_max_index_int \regex_store_state:n {1} } -\cs_new_protected_nopar:Npn \regex_match_loop_setup: - { - \int_incr:N \l_regex_current_step_int - \int_incr:N \l_regex_unique_step_int - \bool_set_false:N \l_regex_fresh_thread_bool - } \cs_new_eq:NN \s_regex_step \scan_stop: \cs_new:Npn \regex_break_step:w #1 \s_regex_step { } \cs_new_protected_nopar:Npn \regex_match_loop:N #1 { - \regex_match_loop_setup: - \token_if_eq_meaning:NNTF #1 \q_recursion_tail - { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } - { \int_set:Nn \l_regex_current_char_int {`#1} } + \regex_match_loop_setup:N #1 \cs_set_nopar:Npx \regex_tmp:w { \int_zero:N \l_regex_max_index_int @@ -1145,19 +1210,49 @@ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \quark_if_recursion_tail_stop:N #1 - \bool_if:NT \l_regex_look_behind_bool - { \tl_put_left:Nx \l_regex_look_behind_str {#1} } \regex_match_loop:N } \cs_new_nopar:Npn \regex_match_one_index:n #1 { - \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + \regex_match_one_index_aux:n + { \int_value:w \tex_skip:D #1 } } \cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 { \int_set:Nn \l_regex_current_state_int {#1} \prop_clear:N \l_regex_current_submatches_prop - \regex_state_use_submatches: + \regex_state_use_with_submatches: + } +\cs_new_protected_nopar:Npn \regex_match_loop_setup:N #1 + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int + \if_meaning:w #1 \q_recursion_tail + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \else: + \int_set:Nn \l_regex_current_char_int {`#1} + \fi: + \regex_match_loop_case_hook: + } +\cs_new_protected_nopar:Npn \regex_match_loop_case_hook: { } +\cs_new_protected_nopar:Npn \regex_match_loop_caseless_hook: + { + \int_set_eq:NN \l_regex_case_changed_char_int \l_regex_current_char_int + \if_num:w \l_regex_current_char_int < \c_ninety_one + \if_num:w \l_regex_current_char_int < \c_sixty_five + \else: + \int_add:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \else: + \if_num:w \l_regex_current_char_int < \c_one_hundred_twenty_three + \if_num:w \l_regex_current_char_int < \c_ninety_seven + \else: + \int_sub:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \fi: + \fi: } \cs_new_protected_nopar:Npn \regex_action_start_wildcard:nn #1#2 { @@ -1167,7 +1262,10 @@ \regex_action_cost:n {#1} } \cs_new_protected_nopar:Npn \regex_action_cost:n #1 - { \regex_store_state:n {#1} } + { + \exp_args:Nf \regex_store_state:n %^^A optimize! + { \int_eval:n { \l_regex_current_state_int + #1 } } + } \cs_new_protected_nopar:Npn \regex_action_success: { \regex_last_match_empty:F @@ -1184,30 +1282,22 @@ } \cs_new_protected_nopar:Npn \regex_action_free:n #1 { - \regex_if_state_free:nT {#1} + \cs_set_nopar:Npx \regex_tmp:w { - \use:x - { - \int_set:Nn \l_regex_current_state_int {#1} - \regex_store_submatches:n { \l_regex_current_state_int } - \regex_state_use: - \int_set:Nn \l_regex_current_state_int - { \int_use:N \l_regex_current_state_int } - \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop - { \exp_not:o \l_regex_current_submatches_prop } - } + \int_add:Nn \l_regex_current_state_int {#1} + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } } + \regex_tmp:w } \cs_new_protected_nopar:Npn \regex_action_submatch:n #1 { \prop_put:Nno \l_regex_current_submatches_prop {#1} { \int_use:N \l_regex_current_step_int } - \regex_store_submatches:n { \l_regex_current_state_int } } -\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } \cs_new_protected_nopar:Npn \regex_extract: { \seq_gclear:N \g_regex_submatches_seq @@ -1217,11 +1307,7 @@ \regex_extract_aux:nTF { ##1 } { \seq_gput_right:Nx \g_regex_submatches_seq - { - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpa_tl } - { \l_regex_tmpb_tl } - } + { \regex_query_substr:NN \l_regex_tmpa_tl \l_regex_tmpb_tl } } { \seq_gput_right:Nn \g_regex_submatches_seq { } } } @@ -1253,9 +1339,13 @@ = \int_use:N \l_regex_max_state_int \l_regex_capturing_group_int = \int_use:N \l_regex_capturing_group_int - \bool_if:NTF \l_regex_look_behind_bool - { \bool_set_true:N \l_regex_look_behind_bool } - { \bool_set_false:N \l_regex_look_behind_bool } + \token_if_eq_meaning:NNT + \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + { + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } \prg_stepwise_function:nnnN {1} {1} {\l_regex_max_state_int} \regex_set_aux:n @@ -1269,7 +1359,7 @@ \cs_new:Npn \regex_nfa:Nw #1 #2 \regex_nfa_end: { \msg_expandable_error:n { Automaton~#1 used~incorrectly. } } \cs_new_eq:NN \regex_nfa_end: \scan_stop: -\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { T , TF } { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } \cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop { @@ -1280,11 +1370,6 @@ \prg_return_false: \fi: } -\msg_new:nnn { regex } { not-nfa } - { - I~was~expecting~a~regular~expression~variable.\\ - Instead,~I~got~#1. - } \cs_new_protected_nopar:Npn \regex_use:N #1 { \exp_after:wN \use_none:nn #1 } \cs_new_protected:Npn \regex_user_aux:n #1 @@ -1296,7 +1381,7 @@ } \cs_new_protected_nopar:Npn \regex_return_after_group: { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \group_insert_after:N \prg_return_true: \else: \group_insert_after:N \prg_return_false: @@ -1304,7 +1389,7 @@ } \cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \regex_extract: \group_insert_after:N \seq_set_eq:NN \group_insert_after:N #1 @@ -1359,7 +1444,7 @@ } \cs_new_protected:Npn \regex_count:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1375,9 +1460,8 @@ \regex_count_after_group:N #3 } } - { } } -\cs_new_protected:Npn \regex_extract:nnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:nnN #1#2#3 { \regex_user_aux:n { @@ -1386,7 +1470,8 @@ \regex_extract_after_group:N #3 } } -\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:nnN #1#2#3 + { T , F , TF } { \regex_user_aux:n { @@ -1396,9 +1481,9 @@ \regex_return_after_group: } } -\cs_new_protected:Npn \regex_extract:NnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1407,9 +1492,9 @@ \regex_extract_after_group:N #3 } } - { } } -\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:NnN #1#2#3 + { T , F , TF } { \regex_check_nfa:NTF #1 { @@ -1423,6 +1508,48 @@ } { \prg_return_false: } } +\cs_new_protected:Npn \regex_extract_all:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_extract_all:NnN #1#2#3 + { + \regex_check_nfa:NT #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } + } +\cs_new_protected_nopar:Npn \regex_extract_all_aux: + { + \regex_extract: + \seq_gconcat:NNN \g_regex_extract_all_seq + \g_regex_extract_all_seq \g_regex_submatches_seq + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_extract_all_after_group:N #1 + { + \seq_if_empty:NF \g_regex_extract_all_seq + { + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_extract_all_seq + } + } \cs_new_protected:Npn \regex_split:nnN #1#2#3 { \regex_user_aux:n @@ -1436,7 +1563,7 @@ } \cs_new_protected:Npn \regex_split:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1447,7 +1574,6 @@ \regex_split_after_group:N #3 } } - { } } \cs_new_protected_nopar:Npn \regex_split_aux: { @@ -1459,9 +1585,8 @@ { \seq_gput_left:Nx \g_regex_submatches_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl } } { \msg_error:nn { regex } { internal } } @@ -1480,9 +1605,8 @@ { \seq_gput_right:Nx \g_regex_split_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \int_use:N \l_regex_current_step_int } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_current_step_int } } \group_insert_after:N \seq_set_eq:NN @@ -1494,40 +1618,57 @@ \str_aux_escape:NNNn \prg_do_nothing: \regex_replacement_escaped:N - \regex_replacement_raw:N + \prg_do_nothing: {#1} \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl \tl_set:Nx \l_regex_replacement_tl { \l_regex_replacement_tl \prg_do_nothing: } } -\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } \cs_new_nopar:Npn \regex_replacement_escaped:N #1 { - \if_num:w 9 < 1 #1 \exp_stop_f: - \exp_not:N \regex_replacement_escaped_ii:nN {#1} - \else: - #1 - \fi: + \regex_token_if_other_digit:NTF #1 + { + \exp_not:n + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } {#1} + } + { + \token_if_eq_charcode:NNTF g #1 + { \exp_not:N \regex_replacement_submatch:w } + { #1 } + } } -\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 +\cs_new_nopar:Npn \regex_replacement_submatch:w #1 { - \regex_token_if_other_digit:NTF #2 - { \regex_replacement_escaped_ii:nN {#1#2} } - { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + \exp_after:wN \token_if_eq_meaning:NNTF \c_lbrace_str #1 + { \regex_replacement_submatch_aux:nN { } } + { + \regex_token_if_other_digit:NTF #1 + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } + { \msg_expandable_error:n { \g misused~in~replacement~text. } } + #1 + } } -\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } +\cs_new_nopar:Npn \regex_replacement_submatch_aux:nN #1 #2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_submatch_aux:nN {#1#2} } + { + \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } {#1} + \exp_after:wN \token_if_eq_meaning:NNF \c_rbrace_str #2 + { + \msg_expandable_error:n { \g misused~in~replacement~text. } + #2 + } + } } \cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 { - \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N \tl_set:Nx \group_insert_after:N #1 - \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N { + \group_insert_after:N \tl_to_str:N + \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N } } \cs_new_protected:Npn \regex_replace_once_aux:Nn #1#2 { @@ -1539,15 +1680,16 @@ \regex_extract: \regex_extract_aux:nTF {0} { + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_tmpb_tl } { } } \tl_gset:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \regex_query_substr:NN \c_zero \l_regex_tmpa_tl \l_regex_replacement_tl - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpb_tl } { \c_max_int } + \exp_after:wN \regex_tmp:w \l_regex_query_other_str } } - { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_other_str } \group_end: } \cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 @@ -1592,12 +1734,10 @@ \tl_gclear:N \g_regex_replaced_str #2 \exp_args:No \regex_match:n {#1} + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_start_step_int } { } } \tl_gput_right:Nx \g_regex_replaced_str - { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \c_max_int } - } + { \exp_after:wN \regex_tmp:w \l_regex_query_other_str } \group_end: } \cs_new_protected_nopar:Npn \regex_replace_all_aux: @@ -1607,9 +1747,8 @@ { \tl_gput_right:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl \l_regex_replacement_tl } } @@ -1632,6 +1771,26 @@ \regex_replacement:n {#2} } } +\msg_new:nnn { regex } { -998 } { Unsupported~construct } +\msg_new:nnn { regex } { -997 } + { The~regular~expression~is~too~large~(32768~states). } +\msg_new:nnn { regex } { 1 } { \iow_char:N\\~at~end~of~pattern } +\msg_new:nnn { regex } { 4 } + { Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier. } +\msg_new:nnn { regex } { 6 } + { Missing~terminating~\iow_char:N\]~for~character~class } +\msg_new:nnn { regex } { 7 } + { Invalid~escape~sequence~in~character~class } +\msg_new:nnn { regex } { 8 } + { Range~out~of~order~in~character~class } +\msg_new:nnn { regex } { 22 } { Mismatched~parentheses } +\msg_new:nnn { regex } { 34 } + { Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large } +\msg_new:nnn { regex } { not-nfa } + { + LaTeX~was~expecting~a~regular~expression~variable.\\ + Instead,~LaTeX~found~'#1'. + } %% %% %% End of file `l3regex-trace.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty index 5da00fdd3d0..c57f415bc47 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty @@ -46,113 +46,121 @@ %% %% ----------------------------------------------------------------------- \RequirePackage{expl3} -\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: l3regex.dtx 2943 2011-10-30 15:59:43Z bruno $ {L3 Experimental Regular Expressions} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} \RequirePackage{l3str} +\int_const:Nn \c_forty_eight { 48 } +\int_const:Nn \c_fifty_eight { 58 } +\int_const:Nn \c_sixty_five { 65 } +\int_const:Nn \c_ninety_one { 91 } +\int_const:Nn \c_ninety_seven { 97 } +\int_const:Nn \c_one_hundred_twenty_three { 123 } +\int_const:Nn \c_one_hundred_twenty_seven { 127 } \cs_new:Npn \regex_tmp:w { } \tl_new:N \g_regex_tmpa_tl \tl_new:N \l_regex_tmpa_tl \tl_new:N \l_regex_tmpb_tl \int_new:N \l_regex_tmpa_int -\tl_new:N \l_regex_pattern_str +\int_new:N \l_regex_tmpb_int \int_new:N \l_regex_max_state_int \int_new:N \l_regex_left_state_int \int_new:N \l_regex_right_state_int -\seq_new:N \l_regex_left_state_seq -\seq_new:N \l_regex_right_state_seq +\seq_new:N \l_regex_group_begin_seq +\seq_new:N \l_regex_group_end_seq \int_new:N \l_regex_capturing_group_int \seq_new:N \l_regex_capturing_group_seq \tl_new:N \l_regex_one_or_group_tl -\int_new:N \l_regex_repetition_int -\bool_new:N \l_regex_look_behind_bool -\tl_new:N \l_regex_look_behind_str \cs_new_eq:NN \regex_build_tmp_class:n \use_none:n \bool_new:N \l_regex_class_bool \tl_new:N \l_regex_class_tl \tl_const:Nn \c_regex_d_tl { - \regex_item_range:nn {48} {57} % 0--9 + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 } \tl_const:Nn \c_regex_D_tl { - \regex_item_more:n {57} % 9 - \regex_item_range:nn {0} {47} % 0 + \regex_item_geq:n { \c_fifty_eight } % > `9 + \regex_item_range:nn { \c_zero } { 47 } % 0 } \tl_const:Nn \c_regex_h_tl { - \regex_item_equal:n {32} % space - \regex_item_equal:n {9} % tab + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_equal:n { \c_nine } % tab } \tl_const:Nn \c_regex_H_tl { - \regex_item_neq:n {32} % space - \regex_item_neq:n {9} % tab - \regex_break_true:w + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_ten } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab } \tl_const:Nn \c_regex_s_tl { - \regex_item_equal:n {32} % space - \regex_item_neq:n {11} % vtab - \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + \regex_item_equal:n { \c_thirty_two } % space + \regex_item_range:nn { \c_nine } { \c_ten } % tab, lf + \regex_item_range:nn { \c_twelve } { \c_thirteen } % ff, cr } \tl_const:Nn \c_regex_S_tl { - \regex_item_more:n {32} % > space - \regex_item_range:nn {14} {31} % tab < ... < space - \regex_item_range:nn {0} {8} % < tab - \regex_item_equal:n {11} % vtab + \regex_item_geq:n { 33 } % > space + \regex_item_range:nn { \c_fourteen } { 31 } % tab < ... < space + \regex_item_range:nn { \c_zero } { \c_eight } % < tab + \regex_item_equal:n { \c_eleven } % vtab } \tl_const:Nn \c_regex_v_tl { - \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + \regex_item_range:nn { \c_ten } { \c_thirteen } % lf, vtab, ff, cr } \tl_const:Nn \c_regex_V_tl { - \regex_item_more:n {13} % cr - \regex_item_range:nn {0} {9} % < lf + \regex_item_geq:n { \c_fourteen } % >cr + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } \tl_const:Nn \c_regex_w_tl { - \regex_item_range:nn {97} {122} % a--z - \regex_item_range:nn {65} {90} % A--Z - \regex_item_range:nn {48} {57} % 0--9 - \regex_item_equal:n {95} % _ + \regex_item_range:nn { \c_ninety_seven } { 122 } % a--z + \regex_item_range:nn { \c_sixty_five } { 90 } % A--Z + \regex_item_range:nn { \c_forty_eight } { 57 } % 0--9 + \regex_item_equal:n { 95 } % _ } \tl_const:Nn \c_regex_W_tl { - \regex_item_range:nn {0} {47} % <`0 - \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) - \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) - \regex_item_equal:n {96} % ` - \regex_item_more:n {122} % z + \regex_item_range:nn { \c_zero } { 47 } % <`0 + \regex_item_range:nn { \c_fifty_eight } { 64 } % (`9+1)--(`A-1) + \regex_item_range:nn { \c_ninety_one } { 94 } % (`Z+1)--(`_-1) + \regex_item_equal:n { 96 } % ` + \regex_item_geq:n { \c_one_hundred_twenty_three } % z } \tl_const:Nn \c_regex_N_tl { - \regex_item_neq:n {10} % lf - \regex_break_true:w + \regex_item_geq:n { \c_eleven } % > lf + \regex_item_range:nn { \c_zero } { \c_nine } % < lf } -\tl_new:N \l_regex_query_str -\int_new:N \l_regex_start_step_int -\int_new:N \l_regex_current_char_int +\tl_new:N \l_regex_query_other_str \int_new:N \l_regex_current_step_int +\int_new:N \l_regex_start_step_int +\int_new:N \l_regex_success_step_int \int_new:N \l_regex_unique_step_int +\int_new:N \l_regex_current_char_int +\int_new:N \l_regex_last_char_int +\int_new:N \l_regex_case_changed_char_int +\bool_new:N \l_regex_caseless_bool \int_new:N \l_regex_current_state_int \prop_new:N \l_regex_current_submatches_prop +\prop_new:N \l_regex_success_submatches_prop \int_new:N \l_regex_max_index_int \tl_new:N \l_regex_every_match_tl +\bool_new:N \l_regex_success_bool \cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} \cs_new_protected:Npn \regex_last_match_empty_yes:F { \int_compare:nNnF \l_regex_start_step_int = \l_regex_current_step_int } \cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F -\bool_new:N \l_regex_success_bool \bool_new:N \l_regex_success_empty_bool -\int_new:N \l_regex_success_step_int -\prop_new:N \l_regex_success_submatches_prop \bool_new:N \l_regex_fresh_thread_bool \seq_new:N \g_regex_submatches_seq \int_new:N \g_regex_match_count_int +\seq_new:N \g_regex_extract_all_seq \seq_new:N \g_regex_split_seq \tl_new:N \l_regex_replacement_tl \tl_new:N \g_regex_replaced_str @@ -169,40 +177,43 @@ \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w } } -\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N -\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 - { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } -\cs_new_protected_nopar:Npn \regex_build_error:n #1 +\cs_new_nopar:Npn \regex_query_substr:NN #1#2 { - \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} - { - \prg_case_int:nnn {#1} - { - {-999} {File~not~found} - {-998} {Unsupported~construct} - {-997} {The~regular~expression~is~too~large~(32768~states).} - {1} {\iow_char:N\\~at~end~of~pattern } - {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} - {6} {Missing~terminating~\iow_char:N\]~for~character~class } - {7} {Invalid~escape~sequence~in~character~class} - {8} {Range~out~of~order~in~character~class} - {22} {Mismatched~parentheses} - {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} - } - { Internal~bug. } - } + \exp_after:wN \regex_query_substr_aux:NN + \exp_after:wN #1 + \exp_after:wN #2 + \l_regex_query_other_str + \q_stop + } +\cs_new:Npn \regex_query_substr_aux:NN #1#2 + { + \str_skip_do:nn {#1} + { \str_collect_do:nn { #2 - #1 } { \use_i_delimit_by_q_stop:nw } } + } +\cs_new_protected_nopar:Npn \regex_seq_pop:N #1 + { + \seq_pop:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl + } +\cs_new_protected_nopar:Npn \regex_seq_get:N #1 + { + \seq_get:NN #1 \l_regex_tmpa_tl + \l_regex_tmpa_tl } -\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } -\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} -\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} -\cs_new_eq:NN \regex_break_point:TF \use_ii:nn -\cs_new_nopar:Npn \regex_item_equal:n #1 +\cs_new_protected_nopar:Npn \regex_item_dot:T + { \int_compare:nNnF \l_regex_current_char_int = \c_minus_one } +\cs_new_protected_nopar:Npn \regex_break_true:w + #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_protected_nopar:Npn \regex_break_false:w + #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_protected_nopar:Npn \regex_break_point:TF #1 #2 { #2 } +\cs_new_protected_nopar:Npn \regex_item_caseful_equal:n #1 { \if_num:w #1 = \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_range:nn #1 #2 +\cs_new_protected_nopar:Npn \regex_item_caseful_range:nn #1 #2 { \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \reverse_if:N \if_num:w #2 < \l_regex_current_char_int @@ -210,24 +221,60 @@ \fi: \fi: } -\cs_new_nopar:Npn \regex_item_less:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseful_geq:n #1 { - \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_more:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseless_equal:n #1 { - \if_num:w #1 < \l_regex_current_char_int + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \if_num:w #1 = \l_regex_case_changed_char_int \exp_after:wN \regex_break_true:w \fi: } -\cs_new_nopar:Npn \regex_item_neq:n #1 +\cs_new_protected_nopar:Npn \regex_item_caseless_range:nn #1 #2 { - \if_num:w #1 = \l_regex_current_char_int - \exp_after:wN \regex_break_false:w + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \reverse_if:N \if_num:w #2 < \l_regex_case_changed_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: \fi: } +\cs_new_protected_nopar:Npn \regex_item_caseless_geq:n #1 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + \reverse_if:N \if_num:w #1 > \l_regex_case_changed_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } +\cs_new:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } +\cs_new:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } +\cs_new_protected_nopar:Npn \regex_build_caseless: + { + \bool_set_true:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseless_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseless_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseless_geq:n } + } +\cs_new_protected_nopar:Npn \regex_build_caseful: + { + \bool_set_false:N \l_regex_caseless_bool + \cs_set:Npn \regex_item_equal:n { \regex_item_caseful_equal:n } + \cs_set:Npn \regex_item_range:nn { \regex_item_caseful_range:nn } + \cs_set:Npn \regex_item_geq:n { \regex_item_caseful_geq:n } + } \cs_new_protected:Npn \regex_get_digits:nw #1 { \tex_afterassignment:D \regex_tmp:w @@ -235,65 +282,71 @@ { \exp_not:n {#1} { \if_false: } } \fi: - \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_aux:N #1 +\cs_new_nopar:Npn \regex_get_digits_aux:NN #1#2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \else: - \if_charcode:w \c_space_token \exp_not:N #1 + \if_meaning:w \regex_build_raw:N #1 + \if_charcode:w \c_space_token \exp_not:N #2 \else: - \regex_get_digits_end:w + \if_num:w 9 < 1 \exp_not:N #2 \exp_stop_f: + #2 + \else: + \regex_get_digits_end:w #1 #2 + \fi: \fi: + \else: + \regex_get_digits_end:w #1 #2 \fi: - #1 \regex_get_digits_aux:N + \regex_get_digits_aux:NN } -\cs_new_nopar:Npn \regex_get_digits_end:w - \fi: \fi: #1 \regex_get_digits_aux:N +\cs_new_nopar:Npn \regex_get_digits_end:w #1 \fi: #2 \regex_get_digits_aux:NN { - \fi: \fi: + \fi: #2 \if_false: { { \fi: } } #1 } +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w \c_nine < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: \else: \prg_return_false: \fi: + } \prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } { - \if_num:w `#1 < 97 \exp_stop_f: - \if_num:w `#1 < 58 \exp_stop_f: - \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \if_num:w `#1 < \c_thirty_two + \prg_return_false: \else: \prg_return_true: \fi: + \else: \prg_return_false: \fi: \else: - \if_num:w \int_eval:w `#1 / 26 = \c_three - \prg_return_false: - \else: - \prg_return_true: - \fi: + \if_num:w `#1 < \c_sixty_five + \prg_return_true: \else: \prg_return_false: \fi: \fi: \else: - \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_true: \else: \prg_return_false: \fi: \else: - \prg_return_false: + \if_num:w `#1 < \c_one_hundred_twenty_seven + \prg_return_true: \else: \prg_return_false: \fi: \fi: \fi: } \prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } { - \if_num:w `#1 < 91 \exp_stop_f: - \if_num:w `#1 < 65 \exp_stop_f: - \if_num:w \c_nine < 1 #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \if_num:w `#1 < \c_ninety_one + \if_num:w `#1 < \c_fifty_eight + \if_num:w `#1 < \c_forty_eight + \prg_return_false: \else: \prg_return_true: \fi: \else: - \prg_return_true: + \if_num:w `#1 < \c_sixty_five + \prg_return_false: \else: \prg_return_true: \fi: \fi: \else: - \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four - \prg_return_true: + \if_num:w `#1 < \c_one_hundred_twenty_three + \if_num:w `#1 < \c_ninety_seven + \prg_return_false: \else: \prg_return_true: \fi: \else: \prg_return_false: \fi: @@ -301,8 +354,8 @@ } \cs_new_protected_nopar:Npn \regex_build_new_state: { - \int_compare:nNnTF \l_regex_max_state_int > {32766} - { \regex_build_error:n {-997} } + \int_compare:nNnTF \l_regex_max_state_int > { 32766 } + { \msg_error:nn { regex } { -997 } } { \int_incr:N \l_regex_max_state_int \tex_toks:D \l_regex_max_state_int { \s_regex_toks } @@ -314,15 +367,15 @@ { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { #1 { \int_use:N #2 } } + { #1 { \int_eval:n { #2 - \l_regex_left_state_int } } } } \cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 { \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { - #1 { \int_use:N #2 } - #3 { \int_use:N #4 } + #1 { \int_eval:n { #2 - \l_regex_left_state_int } } + #3 { \int_eval:n { #4 - \l_regex_left_state_int } } } } \cs_new_protected:Npn \regex_build:n #1 @@ -363,25 +416,19 @@ \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N \int_set_eq:NN \l_regex_capturing_group_int \c_zero - \int_zero:N \l_regex_left_state_int - \int_zero:N \l_regex_right_state_int \int_zero:N \l_regex_max_state_int \regex_build_new_state: \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_action_start_wildcard:nn - { \int_use:N \l_regex_left_state_int } - { \int_use:N \l_regex_right_state_int } - } + { \regex_action_start_wildcard:nn {0} {1} } } \cs_new_protected_nopar:Npn \regex_build_end: { \seq_if_empty:NF \l_regex_capturing_group_seq { - \regex_build_error:n {22} + \msg_error:nn { regex } {22} \prg_replicate:nn - { \seq_length:N \l_regex_capturing_group_seq } % ( + { \seq_length:N \l_regex_capturing_group_seq } { \regex_build_close_aux: \regex_build_group_: } } \regex_toks_put_right:Nx \l_regex_right_state_int @@ -399,7 +446,13 @@ \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_protected_nopar:cpn { regex_build_$: } % $ @@ -409,71 +462,72 @@ { \exp_not:N \int_compare:nNnT \c_minus_one = \l_regex_current_char_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ \cs_new_eq:Nc \regex_build_z: { regex_build_$: } %$ \cs_new_protected_nopar:Npn \regex_build_b: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } { } } } \cs_new_protected_nopar:Npn \regex_build_B: { - \bool_set_true:N \l_regex_look_behind_bool \regex_build_new_state: \regex_toks_put_right:Nx \l_regex_left_state_int { \exp_not:N \regex_if_word_boundary:TF { } - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } } \cs_new_protected_nopar:Npn \regex_if_word_boundary:TF { - \tl_if_empty:NTF \l_regex_look_behind_str - { \c_regex_w_tl } - { - \group_begin: - \cs_set_nopar:Npx \regex_tmp:w - { - \int_set:Nn \l_regex_current_char_int - { ` \str_head:N \l_regex_look_behind_str } - } - \regex_tmp:w - \c_regex_w_tl - \regex_break_point:TF - { \group_end: \c_regex_W_tl } - { \group_end: \c_regex_w_tl } - } + \group_begin: + \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl \regex_item_equal:n { -1 } } + { \group_end: \c_regex_w_tl } \regex_break_point:TF } \cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 { \cs_set:Npx \regex_build_tmp_class:n ##1 { - \exp_not:n { \exp_not:N \if_num:w } - \int_value:w `#1 = \l_regex_current_char_int - \regex_action_cost:n { ##1 } - \exp_not:n { \exp_not:N \fi: } + \regex_item_equal:n { \int_value:w `#1 } + \regex_break_point:TF { \regex_action_cost:n { ##1 } } { } } \regex_build_one_quantifier: } \cs_new_protected_nopar:cpn { regex_build_.: } { \cs_set:Npn \regex_build_tmp_class:n ##1 - { - \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int - \regex_action_cost:n {##1} - \exp_not:N \fi: - } + { \regex_item_dot:T { \regex_action_cost:n {##1} } } \regex_build_one_quantifier: } \cs_new_protected_nopar:Npn \regex_build_char_type:N #1 @@ -481,9 +535,7 @@ \cs_set:Npn \regex_build_tmp_class:n ##1 { \exp_not:N #1 - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } + \regex_break_point:TF { \regex_action_cost:n {##1} } { } } \regex_build_one_quantifier: } @@ -518,16 +570,8 @@ { \exp_not:o \l_regex_class_tl \bool_if:NTF \l_regex_class_bool - { - \exp_not:N \regex_break_point:TF - { \regex_action_cost:n {##1} } - { } - } - { - \exp_not:N \regex_break_point:TF - { } - { \regex_action_cost:n {##1} } - } + { \regex_break_point:TF { \regex_action_cost:n {##1} } { } } + { \regex_break_point:TF { } { \regex_action_cost:n {##1} } } } \regex_build_one_quantifier: } @@ -553,7 +597,7 @@ \cs_new_protected_nopar:Npn \regex_class_single:N #1 { \tl_put_right:Nx \l_regex_class_tl - { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + { \regex_item_equal:n { \int_value:w `#1 } } } \cs_new_protected_nopar:Npn \regex_class_range:Nw #1#2#3 { @@ -572,14 +616,14 @@ \cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 { \if_num:w `#1 > `#2 \exp_stop_f: - \regex_build_error:n {8} + \msg_error:nn { regex } {8} \else: \tl_put_right:Nx \l_regex_class_tl { \if_num:w `#1 = `#2 \exp_stop_f: - \exp_not:N \regex_item_equal:n + \regex_item_equal:n \else: - \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \regex_item_range:nn { \int_value:w `#1 } \fi: { \int_value:w `#2 } } @@ -651,26 +695,29 @@ \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 } } -\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3#4 { - \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + \str_if_eq:xxTF + { \exp_not:n {#3#4} } + { \exp_not:N \regex_build_control:N \c_rbrace_str } { \tl_if_empty:nTF {#2} - { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { \regex_build_quantifier_aux:nnNN { {#1} } { n* } } { \int_compare:nNnT {#1} > {#2} - { \regex_build_error:n {4} } - \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + { \msg_error:nn { regex } {4} } + \regex_build_quantifier_aux:nnNN { {#1} {#2} } { nn } } } { \regex_build_quantifier_end:n { } \use:x { - \exp_not:n { \exp_args:No \tl_map_function:nN } - { \c_lbrace_str #1 #2 , } - \regex_build_raw:N + \exp_args:No \tl_map_function:nN + { \c_lbrace_str #1 , #2 } + \regex_build_raw:N } + #3 #4 } } \cs_new_protected_nopar:Npn \regex_build_quantifier_end:n #1 @@ -726,71 +773,27 @@ \regex_action_free:n \l_regex_tmpa_int } \cs_new_protected_nopar:Npn \regex_build_one_n: #1 - { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { %^^A safe \if nesting? - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } - } + { \prg_replicate:nn {#1} { \regex_build_one_: } } \cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: -\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_copy:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*: } } -\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 +\cs_new_protected_nopar:cpx { regex_build_one_n*?: } #1 { - \regex_build_one_nn_aux:nn {#1} - { - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \fi: - } + \exp_not:N \prg_replicate:nn {#1} { \exp_not:N \regex_build_one_: } + \exp_not:c { regex_build_one_*?: } } -\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:Nnn #1#2#3 { - \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int - \regex_build_new_state: - \regex_build_new_state: - \regex_toks_put_right:Nx \l_regex_tmpa_int - { - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \regex_action_repeat_move:n - { \int_use:N \l_regex_left_state_int } - \exp_not:N \else: - #2 - \exp_not:N \fi: - } - \regex_toks_put_right:Nx \l_regex_left_state_int - { - \regex_build_tmp_class:n - { \int_use:N \l_regex_tmpa_int } - } + \prg_replicate:nn {#2} { \regex_build_one_: } + \prg_replicate:nn {#3-#2} {#1} } +\cs_new_protected_nopar:Npx \regex_build_one_nn: + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_?: } } +\cs_new_protected_nopar:cpx { regex_build_one_nn?: } + { \regex_build_one_nn_aux:Nnn \exp_not:c { regex_build_one_??: } } \cs_new_protected_nopar:cpn { regex_build_(: } #1#2 { \str_if_eq:nnTF { #1 #2 } { \regex_build_control:N ? } @@ -806,10 +809,19 @@ \cs_new_protected_nopar:Npn \regex_build_open_aux: { \regex_build_new_state: - \seq_push:Nx \l_regex_left_state_seq - { \int_use:N \l_regex_left_state_int } - \seq_push:Nx \l_regex_right_state_seq - { \int_use:N \l_regex_right_state_int } + \seq_push:Nx \l_regex_group_begin_seq + { + \int_set:Nn \l_regex_left_state_int + { \int_use:N \l_regex_left_state_int } + } + \bool_if:NTF \l_regex_caseless_bool + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseless: } } + { \seq_push:Nn \l_regex_group_end_seq { \regex_build_caseful: } } + \seq_push:Nx \l_regex_group_end_seq + { + \int_set:Nn \l_regex_right_state_int + { \int_use:N \l_regex_right_state_int } + } \regex_build_begin_alternation: } \cs_new_protected_nopar:cpn { regex_build_|: } @@ -820,7 +832,7 @@ \cs_new_protected_nopar:cpn { regex_build_): } { \seq_if_empty:NTF \l_regex_capturing_group_seq - { \regex_build_error:n {22} } + { \msg_error:nn { regex } { 22 } } { \regex_build_close_aux: \regex_build_group_quantifier: @@ -829,30 +841,41 @@ \cs_new_protected_nopar:Npn \regex_build_close_aux: { \regex_build_end_alternation: - \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl - \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + \regex_seq_pop:N \l_regex_group_begin_seq + \regex_seq_pop:N \l_regex_group_end_seq + \regex_seq_pop:N \l_regex_group_end_seq } \cs_new_protected_nopar:Npn \regex_build_begin_alternation: { \regex_build_new_state: - \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl - \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_seq_get:N \l_regex_group_begin_seq \regex_toks_put_right:Nx \l_regex_left_state_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } \cs_new_protected_nopar:Npn \regex_build_end_alternation: { - \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl - \regex_toks_put_right:Nx \l_regex_right_state_int - { \regex_action_free:n { \l_regex_tmpa_tl } } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \regex_seq_get:N \l_regex_group_end_seq + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_free:n + { + \int_eval:n + { \l_regex_right_state_int - \l_regex_left_state_int } + } + } } \cs_new_protected_nopar:Npn \regex_build_special_group:NN #1#2 { \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } { - \regex_build_error:n { -998 } + \msg_error:nn { regex } { -998 } \regex_build_control:N ( % ) \regex_build_raw:N ? #1 #2 @@ -863,6 +886,40 @@ \seq_push:Nx \l_regex_capturing_group_seq { * } \regex_build_open_aux: } +\cs_new_protected_nopar:Npn \regex_build_special_group_i: + { + \regex_build_options:NNN + + \regex_build_raw:N i + } +\cs_new_protected_nopar:cpn { regex_build_special_group_-: } + { + \regex_build_options:NNN - + } +\cs_new_protected_nopar:Npn \regex_build_options:NNN #1#2#3 + { + \token_if_eq_meaning:NNTF \regex_build_raw:N #2 + { + \cs_if_exist_use:cF { regex_build_option_#1#3: } + { \msg_error:nnx { regex } { unknown-option } { #3 } } + \regex_build_options:NNN #1 + } + { + \prg_case_str:nnn { #3 } + { % ( + { ) } { } + { - } { \regex_build_options:NNN - } + } + { \msg_error:nnx { regex } { invalid-in-option } { #3 } } + } + } +\cs_new_protected_nopar:cpn { regex_build_option_+i: } + { + \regex_build_caseless: + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } +\cs_new_protected_nopar:cpn { regex_build_option_-i: } + { \regex_build_caseful: } \cs_new_protected_nopar:Npn \regex_build_group_quantifier: { \tl_set:Nn \l_regex_one_or_group_tl { group } @@ -891,21 +948,33 @@ \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int \regex_build_new_state: \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int - \use:x + \regex_toks_put_left:Nx \l_regex_right_state_int + { + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_tmpa_int } % ^^A here we lie! + } + \cs_set:Npx \regex_tmp:w { \tex_toks:D \l_regex_tmpa_int - { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + { + \s_regex_toks + #1 { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } + \regex_tmp:w \regex_build_group_submatches:NN \l_regex_right_state_int \l_regex_left_state_int - \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int } \cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 { \regex_build_group_shift:N \regex_action_free:n + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int \regex_build_transition_aux:NN \regex_action_free:n #1 #2 \l_regex_tmpa_int - { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { + \regex_action_free:n + { \int_eval:n { \l_regex_right_state_int - \l_regex_tmpa_int } } + } } \cs_new_protected_nopar:cpn { regex_build_group_?: } { @@ -945,77 +1014,63 @@ \regex_action_free:n \l_regex_right_state_int \regex_action_free:n \l_regex_tmpa_int } -\cs_new_protected_nopar:Npn \regex_build_group_n: #1 +\cs_new_protected_nopar:Npn \regex_build_group_n_aux:n #1 { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x + \regex_toks_put_right:Nx \l_regex_right_state_int { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_move:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } + \regex_action_free:n + { \int_eval:n { \l_regex_max_state_int - \l_regex_left_state_int } } } - } -\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: -\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n - \regex_build_transition_aux:NN - \regex_action_free:n \l_regex_tmpa_int - \use:x + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \int_set:Nn \l_regex_tmpb_int { \l_regex_max_state_int + \c_one } + \int_set:Nn \l_regex_max_state_int { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \fi: - } + \l_regex_left_state_int - \c_one + + #1 * ( \l_regex_max_state_int - \l_regex_left_state_int + \c_one ) + } + \int_until_do:nNnn \l_regex_tmpb_int > \l_regex_max_state_int + { + \tex_toks:D \l_regex_tmpb_int = \tex_toks:D \l_regex_tmpa_int + \int_incr:N \l_regex_tmpa_int + \int_incr:N \l_regex_tmpb_int } } -\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 - { - \regex_build_group_shift:N \regex_action_repeat_move:n +\cs_new_protected_nopar:Npn \regex_build_group_n: #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: +\cs_new_protected_nopar:cpn { regex_build_group_n*: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN \regex_action_free:n \l_regex_tmpa_int - \use:x - { - \tex_toks:D \l_regex_tmpa_int - { - \s_regex_toks - \exp_not:N \if_num:w #1 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \else: - \regex_action_no_repeat_copy:n - { \int_use:N \l_regex_right_state_int } - \exp_not:N \if_num:w #2 > \l_regex_repetition_int - \tex_the:D \tex_toks:D \l_regex_tmpa_int - \exp_not:N \fi: - \exp_not:N \fi: - } - } + \regex_action_free:n \l_regex_right_state_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int } -\cs_new_protected:Npn \regex_if_state_free:nT #1 - { - \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int - \exp_after:wN \use_none:n - \else: - \exp_after:wN \use:n - \fi: +\cs_new_protected_nopar:cpn { regex_build_group_n*?: } #1 + { % ^^A todo: catch case #1 <= 0. + \regex_build_group_n_aux:n {#1} + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + \regex_build_group_submatches:NN + \l_regex_tmpa_int \l_regex_left_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*: } {#1} + } +\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 + { % ^^A Not Implemented Yet! + \msg_expandable_error:n { Quantifier~{m,n}~not~implemented~yet } + \use:c { regex_build_group_n*?: } {#1} } \cs_new_protected:Npn \regex_store_state:n #1 { @@ -1023,23 +1078,24 @@ \tex_skip:D \l_regex_max_index_int #1 sp \scan_stop: \regex_store_submatches:n {#1} } -\cs_new_protected_nopar:Npn \regex_state_use_submatches: +\cs_new_protected_nopar:Npn \regex_state_use_with_submatches: { \regex_state_use_aux:n { } } \cs_new_protected_nopar:Npn \regex_state_use: { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } -\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } -\cs_new_protected:Npn \regex_state_use_aux:n #1 - { - \regex_if_state_free:nT { \l_regex_current_state_int } - { - \tex_dimen:D \l_regex_current_state_int - = \l_regex_unique_step_int sp \scan_stop: - #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: - } +\cs_new_nopar:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected_nopar:Npn \regex_state_use_aux:n #1 + { + \if_num:w \tex_dimen:D \l_regex_current_state_int + < \l_regex_unique_step_int + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \exp_after:wN \l_regex_current_state_int + \fi: + \scan_stop: } \cs_new_protected_nopar:Npn \regex_disable_submatches: { - \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_state_use_with_submatches: \regex_state_use: \cs_set_eq:NN \regex_store_submatches:n \regex_protected_use_none:n \cs_set_eq:NN \regex_action_submatches:n @@ -1048,55 +1104,73 @@ \cs_new_protected:Npn \regex_protected_use_none:n #1 { } \cs_new_protected:Npn \regex_store_submatches:n #1 { - \exp_after:wN \regex_store_submatches_aux:Nw - \exp_after:wN \l_regex_current_submatches_prop - \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop - { #1 } + \tex_toks:D #1 \exp_after:wN + { + \tex_romannumeral:D + \exp_after:wN \regex_store_submatches_aux:w + \tex_the:D \tex_toks:D #1 + } } -\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks +\cs_new_protected:Npn \regex_store_submatches_aux:w #1 \s_regex_toks { - \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } - #2 - \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \regex_store_submatches_aux_ii:Nnnw + #1 + \regex_state_submatches:nn \c_minus_one \q_prop \s_regex_toks } -\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn - #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 +\cs_new_protected:Npn \regex_store_submatches_aux_ii:Nnnw + \regex_state_submatches:nn #1 #2 #3 \s_regex_toks { - \tex_toks:D #6 \exp_after:wN + \exp_after:wN \c_zero + \exp_after:wN \regex_state_submatches:nn \exp_after:wN { - \exp_after:wN \regex_state_submatches:nn \exp_after:wN - { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } - { #1 } - \regex_state_submatches:nn {#2} {#3} - \s_regex_toks - #5 + \int_value:w \int_eval:w + \l_regex_unique_step_int + \c_one + \exp_after:wN } + \exp_after:wN { \l_regex_current_submatches_prop } + \regex_state_submatches:nn {#1} {#2} + \s_regex_toks } \cs_new_protected:Npn \regex_state_submatches:nn #1#2 { \if_num:w #1 = \l_regex_unique_step_int - \tl_set:Nn \l_regex_current_submatches_prop {#2} + \tl_set:Nn \l_regex_current_submatches_prop { #2 } \fi: } \cs_new_protected:Npn \regex_match:n #1 { - \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \tl_set:Nx \l_regex_query_other_str { \tl_to_other_str:n {#1} } \regex_match_initial_setup: \regex_match_once: } \cs_new_protected_nopar:Npn \regex_match_once: { \regex_match_setup: - \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \exp_after:wN \regex_match_once_aux: \l_regex_query_other_str \q_recursion_tail \q_recursion_stop \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } } \cs_new_protected_nopar:Npn \regex_match_once_aux: - { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } + { + \int_compare:nNnTF \l_regex_start_step_int = \c_zero + { + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \regex_match_loop:N + } + { + \str_skip_do:nn + { \l_regex_start_step_int - \c_one } + { \regex_match_once_aux_ii:N } + } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux_ii:N #1 + { + \int_set:Nn \l_regex_current_char_int { `#1 } + \regex_match_loop:N + } \cs_new_protected_nopar:Npn \regex_match_initial_setup: { - \tl_clear:N \l_regex_look_behind_str \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } \int_set_eq:NN \l_regex_unique_step_int \c_minus_one @@ -1118,20 +1192,11 @@ \int_zero:N \l_regex_max_index_int \regex_store_state:n {1} } -\cs_new_protected_nopar:Npn \regex_match_loop_setup: - { - \int_incr:N \l_regex_current_step_int - \int_incr:N \l_regex_unique_step_int - \bool_set_false:N \l_regex_fresh_thread_bool - } \cs_new_eq:NN \s_regex_step \scan_stop: \cs_new:Npn \regex_break_step:w #1 \s_regex_step { } \cs_new_protected_nopar:Npn \regex_match_loop:N #1 { - \regex_match_loop_setup: - \token_if_eq_meaning:NNTF #1 \q_recursion_tail - { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } - { \int_set:Nn \l_regex_current_char_int {`#1} } + \regex_match_loop_setup:N #1 \cs_set_nopar:Npx \regex_tmp:w { \int_zero:N \l_regex_max_index_int @@ -1145,19 +1210,49 @@ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \quark_if_recursion_tail_stop:N #1 - \bool_if:NT \l_regex_look_behind_bool - { \tl_put_left:Nx \l_regex_look_behind_str {#1} } \regex_match_loop:N } \cs_new_nopar:Npn \regex_match_one_index:n #1 { - \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + \regex_match_one_index_aux:n + { \int_value:w \tex_skip:D #1 } } \cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 { \int_set:Nn \l_regex_current_state_int {#1} \prop_clear:N \l_regex_current_submatches_prop - \regex_state_use_submatches: + \regex_state_use_with_submatches: + } +\cs_new_protected_nopar:Npn \regex_match_loop_setup:N #1 + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int + \if_meaning:w #1 \q_recursion_tail + \int_set_eq:NN \l_regex_current_char_int \c_minus_one + \else: + \int_set:Nn \l_regex_current_char_int {`#1} + \fi: + \regex_match_loop_case_hook: + } +\cs_new_protected_nopar:Npn \regex_match_loop_case_hook: { } +\cs_new_protected_nopar:Npn \regex_match_loop_caseless_hook: + { + \int_set_eq:NN \l_regex_case_changed_char_int \l_regex_current_char_int + \if_num:w \l_regex_current_char_int < \c_ninety_one + \if_num:w \l_regex_current_char_int < \c_sixty_five + \else: + \int_add:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \else: + \if_num:w \l_regex_current_char_int < \c_one_hundred_twenty_three + \if_num:w \l_regex_current_char_int < \c_ninety_seven + \else: + \int_sub:Nn \l_regex_case_changed_char_int { \c_thirty_two } + \fi: + \fi: + \fi: } \cs_new_protected_nopar:Npn \regex_action_start_wildcard:nn #1#2 { @@ -1167,7 +1262,10 @@ \regex_action_cost:n {#1} } \cs_new_protected_nopar:Npn \regex_action_cost:n #1 - { \regex_store_state:n {#1} } + { + \exp_args:Nf \regex_store_state:n %^^A optimize! + { \int_eval:n { \l_regex_current_state_int + #1 } } + } \cs_new_protected_nopar:Npn \regex_action_success: { \regex_last_match_empty:F @@ -1184,30 +1282,22 @@ } \cs_new_protected_nopar:Npn \regex_action_free:n #1 { - \regex_if_state_free:nT {#1} + \cs_set_nopar:Npx \regex_tmp:w { - \use:x - { - \int_set:Nn \l_regex_current_state_int {#1} - \regex_store_submatches:n { \l_regex_current_state_int } - \regex_state_use: - \int_set:Nn \l_regex_current_state_int - { \int_use:N \l_regex_current_state_int } - \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop - { \exp_not:o \l_regex_current_submatches_prop } - } + \int_add:Nn \l_regex_current_state_int {#1} + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } } + \regex_tmp:w } \cs_new_protected_nopar:Npn \regex_action_submatch:n #1 { \prop_put:Nno \l_regex_current_submatches_prop {#1} { \int_use:N \l_regex_current_step_int } - \regex_store_submatches:n { \l_regex_current_state_int } } -\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } -\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } \cs_new_protected_nopar:Npn \regex_extract: { \seq_gclear:N \g_regex_submatches_seq @@ -1217,11 +1307,7 @@ \regex_extract_aux:nTF { ##1 } { \seq_gput_right:Nx \g_regex_submatches_seq - { - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpa_tl } - { \l_regex_tmpb_tl } - } + { \regex_query_substr:NN \l_regex_tmpa_tl \l_regex_tmpb_tl } } { \seq_gput_right:Nn \g_regex_submatches_seq { } } } @@ -1253,9 +1339,13 @@ = \int_use:N \l_regex_max_state_int \l_regex_capturing_group_int = \int_use:N \l_regex_capturing_group_int - \bool_if:NTF \l_regex_look_behind_bool - { \bool_set_true:N \l_regex_look_behind_bool } - { \bool_set_false:N \l_regex_look_behind_bool } + \token_if_eq_meaning:NNT + \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + { + \cs_set_eq:NN \regex_match_loop_case_hook: + \regex_match_loop_caseless_hook: + } \prg_stepwise_function:nnnN {1} {1} {\l_regex_max_state_int} \regex_set_aux:n @@ -1269,7 +1359,7 @@ \cs_new:Npn \regex_nfa:Nw #1 #2 \regex_nfa_end: { \msg_expandable_error:n { Automaton~#1 used~incorrectly. } } \cs_new_eq:NN \regex_nfa_end: \scan_stop: -\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { T , TF } { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } \cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop { @@ -1280,11 +1370,6 @@ \prg_return_false: \fi: } -\msg_new:nnn { regex } { not-nfa } - { - I~was~expecting~a~regular~expression~variable.\\ - Instead,~I~got~#1. - } \cs_new_protected_nopar:Npn \regex_use:N #1 { \exp_after:wN \use_none:nn #1 } \cs_new_protected:Npn \regex_user_aux:n #1 @@ -1296,7 +1381,7 @@ } \cs_new_protected_nopar:Npn \regex_return_after_group: { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \group_insert_after:N \prg_return_true: \else: \group_insert_after:N \prg_return_false: @@ -1304,7 +1389,7 @@ } \cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 { - \if_bool:N \l_regex_success_bool + \if_meaning:w \c_true_bool \l_regex_success_bool \regex_extract: \group_insert_after:N \seq_set_eq:NN \group_insert_after:N #1 @@ -1359,7 +1444,7 @@ } \cs_new_protected:Npn \regex_count:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1375,9 +1460,8 @@ \regex_count_after_group:N #3 } } - { } } -\cs_new_protected:Npn \regex_extract:nnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:nnN #1#2#3 { \regex_user_aux:n { @@ -1386,7 +1470,8 @@ \regex_extract_after_group:N #3 } } -\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:nnN #1#2#3 + { T , F , TF } { \regex_user_aux:n { @@ -1396,9 +1481,9 @@ \regex_return_after_group: } } -\cs_new_protected:Npn \regex_extract:NnN #1#2#3 +\cs_new_protected:Npn \regex_extract_once:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1407,9 +1492,9 @@ \regex_extract_after_group:N #3 } } - { } } -\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } +\prg_new_protected_conditional:Npnn \regex_extract_once:NnN #1#2#3 + { T , F , TF } { \regex_check_nfa:NTF #1 { @@ -1423,6 +1508,48 @@ } { \prg_return_false: } } +\cs_new_protected:Npn \regex_extract_all:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_extract_all:NnN #1#2#3 + { + \regex_check_nfa:NT #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_extract_all_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_extract_all_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_all_after_group:N #3 + } + } + } +\cs_new_protected_nopar:Npn \regex_extract_all_aux: + { + \regex_extract: + \seq_gconcat:NNN \g_regex_extract_all_seq + \g_regex_extract_all_seq \g_regex_submatches_seq + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_extract_all_after_group:N #1 + { + \seq_if_empty:NF \g_regex_extract_all_seq + { + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_extract_all_seq + } + } \cs_new_protected:Npn \regex_split:nnN #1#2#3 { \regex_user_aux:n @@ -1436,7 +1563,7 @@ } \cs_new_protected:Npn \regex_split:NnN #1#2#3 { - \regex_check_nfa:NTF #1 + \regex_check_nfa:NT #1 { \regex_user_aux:n { @@ -1447,7 +1574,6 @@ \regex_split_after_group:N #3 } } - { } } \cs_new_protected_nopar:Npn \regex_split_aux: { @@ -1459,9 +1585,8 @@ { \seq_gput_left:Nx \g_regex_submatches_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl } } { \msg_error:nn { regex } { internal } } @@ -1480,9 +1605,8 @@ { \seq_gput_right:Nx \g_regex_split_seq { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \int_use:N \l_regex_current_step_int } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_current_step_int } } \group_insert_after:N \seq_set_eq:NN @@ -1494,40 +1618,57 @@ \str_aux_escape:NNNn \prg_do_nothing: \regex_replacement_escaped:N - \regex_replacement_raw:N + \prg_do_nothing: {#1} \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl \tl_set:Nx \l_regex_replacement_tl { \l_regex_replacement_tl \prg_do_nothing: } } -\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } \cs_new_nopar:Npn \regex_replacement_escaped:N #1 { - \if_num:w 9 < 1 #1 \exp_stop_f: - \exp_not:N \regex_replacement_escaped_ii:nN {#1} - \else: - #1 - \fi: + \regex_token_if_other_digit:NTF #1 + { + \exp_not:n + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } {#1} + } + { + \token_if_eq_charcode:NNTF g #1 + { \exp_not:N \regex_replacement_submatch:w } + { #1 } + } } -\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 +\cs_new_nopar:Npn \regex_replacement_submatch:w #1 { - \regex_token_if_other_digit:NTF #2 - { \regex_replacement_escaped_ii:nN {#1#2} } - { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + \exp_after:wN \token_if_eq_meaning:NNTF \c_lbrace_str #1 + { \regex_replacement_submatch_aux:nN { } } + { + \regex_token_if_other_digit:NTF #1 + { \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } } + { \msg_expandable_error:n { \g misused~in~replacement~text. } } + #1 + } } -\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } +\cs_new_nopar:Npn \regex_replacement_submatch_aux:nN #1 #2 { - \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: - \prg_return_true: - \else: - \prg_return_false: - \fi: + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_submatch_aux:nN {#1#2} } + { + \exp_not:n { \seq_item:Nn \g_regex_submatches_seq } {#1} + \exp_after:wN \token_if_eq_meaning:NNF \c_rbrace_str #2 + { + \msg_expandable_error:n { \g misused~in~replacement~text. } + #2 + } + } } \cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 { - \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N \tl_set:Nx \group_insert_after:N #1 - \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N { + \group_insert_after:N \tl_to_str:N + \group_insert_after:N \g_regex_replaced_str + \group_insert_after:N } } \cs_new_protected:Npn \regex_replace_once_aux:Nn #1#2 { @@ -1539,15 +1680,16 @@ \regex_extract: \regex_extract_aux:nTF {0} { + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_tmpb_tl } { } } \tl_gset:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \regex_query_substr:NN \c_zero \l_regex_tmpa_tl \l_regex_replacement_tl - \str_from_to:Nnn \l_regex_query_str - { \l_regex_tmpb_tl } { \c_max_int } + \exp_after:wN \regex_tmp:w \l_regex_query_other_str } } - { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_other_str } \group_end: } \cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 @@ -1592,12 +1734,10 @@ \tl_gclear:N \g_regex_replaced_str #2 \exp_args:No \regex_match:n {#1} + \cs_set_nopar:Npn \regex_tmp:w + { \str_skip_do:nn { \l_regex_start_step_int } { } } \tl_gput_right:Nx \g_regex_replaced_str - { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \c_max_int } - } + { \exp_after:wN \regex_tmp:w \l_regex_query_other_str } \group_end: } \cs_new_protected_nopar:Npn \regex_replace_all_aux: @@ -1607,9 +1747,8 @@ { \tl_gput_right:Nx \g_regex_replaced_str { - \str_from_to:Nnn \l_regex_query_str - { \int_use:N \l_regex_start_step_int } - { \l_regex_tmpa_tl } + \regex_query_substr:NN + \l_regex_start_step_int \l_regex_tmpa_tl \l_regex_replacement_tl } } @@ -1632,6 +1771,26 @@ \regex_replacement:n {#2} } } +\msg_new:nnn { regex } { -998 } { Unsupported~construct } +\msg_new:nnn { regex } { -997 } + { The~regular~expression~is~too~large~(32768~states). } +\msg_new:nnn { regex } { 1 } { \iow_char:N\\~at~end~of~pattern } +\msg_new:nnn { regex } { 4 } + { Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier. } +\msg_new:nnn { regex } { 6 } + { Missing~terminating~\iow_char:N\]~for~character~class } +\msg_new:nnn { regex } { 7 } + { Invalid~escape~sequence~in~character~class } +\msg_new:nnn { regex } { 8 } + { Range~out~of~order~in~character~class } +\msg_new:nnn { regex } { 22 } { Mismatched~parentheses } +\msg_new:nnn { regex } { 34 } + { Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large } +\msg_new:nnn { regex } { not-nfa } + { + LaTeX~was~expecting~a~regular~expression~variable.\\ + Instead,~LaTeX~found~'#1'. + } %% %% %% End of file `l3regex.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty index 9a69d0da0a6..c705f6366b3 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty @@ -46,30 +46,27 @@ %% %% ----------------------------------------------------------------------- \RequirePackage{expl3} -\GetIdInfo$Id: l3str.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: l3str.dtx 2921 2011-10-17 13:23:36Z bruno $ {L3 Experimental Strings} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} -\cs_if_exist:NF \cs_if_exist_use:cF - { - \cs_new:Npn \cs_if_exist_use:cF #1 - { \cs_if_exist:cTF {#1} { \use:c {#1} } } - } \cs_if_exist:NF \use_i:nnnnnnnn { \cs_new:Npn \use_i:nnnnnnnn #1#2#3#4#5#6#7#8 {#1} } -\cs_set:Npn \str_tmp:w #1 - { - \cs_new_protected:cpx { str_#1:Nn } ##1##2 - { \exp_not:c { tl_#1:Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } - \exp_args:Nc \cs_generate_variant:Nn { str_#1:Nn } { Nx , cn , cx } - } -\str_tmp:w {set} -\str_tmp:w {gset} -\str_tmp:w {put_left} -\str_tmp:w {gput_left} -\str_tmp:w {put_right} -\str_tmp:w {gput_right} -\cs_set_protected_nopar:Npn \str_tmp:w { } +\group_begin: + \cs_set_protected_nopar:Npn \str_tmp:w #1 + { + \cs_new_protected:cpx { str_ #1 :Nn } ##1##2 + { \exp_not:c { tl_ #1 :Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } + \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :Nn } { Nx , cn , cx } + } + \str_tmp:w { set } + \str_tmp:w { gset } + \str_tmp:w { put_left } + \str_tmp:w { gput_left } + \str_tmp:w { put_right } + \str_tmp:w { gput_right } +\group_end: +\cs_new_protected_nopar:Npn \str_tmp:w { } \tl_new:N \g_str_tmpa_tl \tl_new:N \g_str_result_tl \int_new:N \l_str_char_int @@ -80,34 +77,31 @@ \tl_const:Nx \c_hash_str { \cs_to_str:N \# } \tl_const:Nx \c_percent_str { \cs_to_str:N \% } \group_begin: -\char_set_lccode:nn {`\*}{`\ } -\char_set_lccode:nn {`\A}{`\A} -\tl_to_lowercase:n { - \group_end: - \cs_new:Npn \tl_to_other_str:n #1 - { - \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % - A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop - } - \cs_new_nopar:Npn \tl_to_other_str_loop:w +\char_set_lccode:nn { `\* } { `\ } +\char_set_lccode:nn { `\A } { `\A } +\tl_to_lowercase:n + { + \group_end: + \cs_new:Npn \tl_to_other_str:n #1 + { + \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % + A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_loop:w #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \q_stop - { - \if_meaning:w A #8 - \tl_to_other_str_end:w - \fi: - \tl_to_other_str_loop:w - #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop - } - \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop - { \fi: #2 } -} -\cs_new_nopar:Npn \tl_to_other_str:N - { \exp_args:No \tl_to_other_str:n } -\cs_new:Npn \str_sanitize_args:Nn #1 #2 - { - \exp_args:Nf #1 - { \tl_to_other_str:n {#2} } + { + \if_meaning:w A #8 + \tl_to_other_str_end:w + \fi: + \tl_to_other_str_loop:w + #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop + { \fi: #2 } } +\cs_new_nopar:Npn \tl_to_other_str:N { \exp_args:No \tl_to_other_str:n } +\cs_new:Npn \str_sanitize_args:Nn #1#2 + { \exp_args:Nf #1 { \tl_to_other_str:n {#2} } } \cs_new:Npn \str_sanitize_args:Nnn #1#2#3 { \exp_args:Nff #1 @@ -141,15 +135,15 @@ \fi: \c_nine + \str_length_loop:NNNNNNNNN } -\cs_set_nopar:Npn \str_head:N { \exp_args:No \str_head:n } +\cs_new_nopar:Npn \str_head:N { \exp_args:No \str_head:n } \cs_set:Npn \str_head:n #1 { \exp_after:wN \str_head_aux:w \tl_to_str:n {#1} { { } } ~ \q_stop } -\cs_set_nopar:Npx \str_head_aux:w #1 ~ % - { \exp_not:N \use_i_delimit_by_q_stop:nw #1 { ~ } } +\cs_set_nopar:Npn \str_head_aux:w #1 ~ % + { \use_i_delimit_by_q_stop:nw #1 { ~ } } \cs_new:Npn \str_head_ignore_spaces:n #1 { \exp_after:wN \use_i_delimit_by_q_stop:nw \tl_to_str:n {#1} { } \q_stop } \cs_new_nopar:Npn \str_head_unsafe:n #1 @@ -169,15 +163,17 @@ } \cs_new_nopar:Npn \str_tail_unsafe:n #1 { \str_tail_aux_ii:w #1 X { } X \q_stop } -\cs_set_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } +\cs_new_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } \cs_new:Npn \str_skip_do:nn #1 { - \int_compare:nNnTF {#1} > \c_seven - { \str_skip_aux:nnnnnnnnn } - { \str_skip_end:n } + \if_num:w \int_eval:w #1 > \c_seven + \exp_after:wN \str_skip_aux:nnnnnnnnn + \else: + \exp_after:wN \str_skip_end:n + \fi: {#1} } -\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2 #3#4#5#6#7#8#9 +\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \exp_args:Nf \str_skip_do:nn { \int_eval:n { #1 - \c_seven } } {#2} } \cs_new:Npn \str_skip_end:n #1 { @@ -193,7 +189,7 @@ \else: \str_skip_end_ii:nwn { XXXXXXX } \fi: } -\cs_new:Npn \str_skip_end_ii:nwn #1 #2 \fi: #3 +\cs_new:Npn \str_skip_end_ii:nwn #1#2 \fi: #3 { \fi: \use_i:nnnnnnnn {#3} #1 } \cs_new:Npn \str_collect_do:nn #1#2 { \str_collect_aux:n {#1} { \str_collect_end_iii:nwNNNNNNN {#2} } } @@ -213,18 +209,18 @@ \cs_new:Npn \str_collect_end:n #1 { \if_case:w \int_eval:w #1 \int_eval_end: - \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{}{} } - \or: \str_collect_end_ii:nwn { {}{}{} } - \or: \str_collect_end_ii:nwn { {}{} } - \or: \str_collect_end_ii:nwn { {} } + \str_collect_end_ii:nwn { { } { } { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } { } } + \or: \str_collect_end_ii:nwn { { } { } } + \or: \str_collect_end_ii:nwn { { } } \or: \str_collect_end_ii:nwn { } - \else: \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \else: \str_collect_end_ii:nwn { { } { } { } { } { } { } { } } \fi: } -\cs_new:Npn \str_collect_end_ii:nwn #1 #2 \fi: #3 +\cs_new:Npn \str_collect_end_ii:nwn #1#2 \fi: #3 { \fi: #3 \q_stop #1 } \cs_new:Npn \str_collect_end_iii:nwNNNNNNN #1 #2 \q_stop #3#4#5#6#7#8#9 { #1 {#2#3#4#5#6#7#8#9} } @@ -264,30 +260,30 @@ { \use_none_delimit_by_q_stop:w } } } -\cs_new_nopar:Npn \str_from_to:Nnn { \exp_args:No \str_from_to:nnn } -\cs_new:Npn \str_from_to:nnn #1#2#3 +\cs_new_nopar:Npn \str_substr:Nnn { \exp_args:No \str_substr:nnn } +\cs_new:Npn \str_substr:nnn #1#2#3 { \exp_args:Nf \tl_to_str:n - { \str_sanitize_args:Nn \str_from_to_unsafe:nnn {#1}{#2}{#3} } + { \str_sanitize_args:Nn \str_substr_unsafe:nnn {#1} {#2} {#3} } } -\cs_new:Npn \str_from_to_ignore_spaces:nnn #1 - { \exp_args:No \str_from_to_unsafe:nnn { \tl_to_str:n {#1} } } -\cs_new:Npn \str_from_to_unsafe:nnn #1#2#3 % <string> <from> <to> +\cs_new:Npn \str_substr_ignore_spaces:nnn #1 + { \exp_args:No \str_substr_unsafe:nnn { \tl_to_str:n {#1} } } +\cs_new:Npn \str_substr_unsafe:nnn #1#2#3 { - \str_aux_eval_args:Nnnn \str_from_to_aux:nnnw + \str_aux_eval_args:Nnnn \str_substr_aux:nnnw { \str_length_unsafe:n {#1} } {#2} {#3} #1 \q_stop } -\cs_new:Npn \str_from_to_aux:nnnw #1#2#3 % <len> <from> <to> +\cs_new:Npn \str_substr_aux:nnnw #1#2#3 { - \exp_args:Nf \str_from_to_aux_ii:nnw + \exp_args:Nf \str_substr_aux_ii:nnw { \str_aux_normalize_range:nn {#2} {#1} } { \str_aux_normalize_range:nn {#3} {#1} } } -\cs_new:Npn \str_from_to_aux_ii:nnw #1#2 +\cs_new:Npn \str_substr_aux_ii:nnw #1#2 { \str_skip_do:nn {#1} { @@ -376,7 +372,7 @@ } \prg_new_protected_conditional:Npnn \str_aux_hexadecimal_test:N #1 { TF } { - \tl_if_in:onTF { \tl_to_str:n {abcdef} } {#1} + \tl_if_in:onTF { \tl_to_str:n { abcdef } } {#1} { \int_set:Nn \l_str_char_int { \c_sixteen * \l_str_char_int + `#1 - 87 } @@ -423,7 +419,7 @@ \cs_set_nopar:Npn \str_aux_escape_unescaped:N { #1 } \cs_set_nopar:Npn \str_aux_escape_escaped:N { #2 } \cs_set_nopar:Npn \str_aux_escape_raw:N { #3 } - \int_set:Nn \tex_escapechar:D {92} + \int_set:Nn \tex_escapechar:D { 92 } \tl_gset:Nx \g_str_result_tl { \tl_to_other_str:n {#4} } \tl_gset:Nx \g_str_result_tl { @@ -531,28 +527,22 @@ } } \group_end: -\msg_new:nnn { str } { x-missing-brace } - { - You~wrote~something~like~ - `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ - The~closing~brace~is~missing. - } \group_begin: - \char_set_catcode_other:n {`\^^@} + \char_set_catcode_other:n { `\^^@ } \cs_new_protected_nopar:Npn \str_aux_convert_store: { - \char_set_lccode:nn {\c_zero} {\l_str_char_int} + \char_set_lccode:nn { \c_zero } { \l_str_char_int } \tl_to_lowercase:n { \tl_gput_right:Nx \g_str_result_tl {^^@} } } \cs_new_protected_nopar:Npn \str_aux_convert_store:NNn #1#2#3 { - \char_set_lccode:nn {\c_zero} {#3} + \char_set_lccode:nn { \c_zero } {#3} \tl_to_lowercase:n { #1 #2 {^^@} } } \group_end: \cs_new_nopar:Npn \str_aux_byte_to_hexadecimal:N #1 { - \int_compare:nNnTF {`#1} < {256} + \int_compare:nNnTF {`#1} < { 256 } { \int_to_letter:n { \int_div_truncate:nn {`#1} \c_sixteen } \int_to_letter:n { \int_mod:nn {`#1} \c_sixteen } @@ -561,14 +551,14 @@ } \cs_new_nopar:Npn \str_aux_byte_to_octal:N #1 { - \int_compare:nNnTF {`#1} < {64} + \int_compare:nNnTF {`#1} < { 64 } { 0 \int_to_letter:n { \int_div_truncate:nn {`#1} \c_eight } \int_to_letter:n { \int_mod:nn {`#1} \c_eight } } { - \int_compare:nNnTF {`#1} < {256} + \int_compare:nNnTF {`#1} < { 256 } { \int_to_octal:n {`#1} } { \msg_expandable_error:n { Invalid~byte~`#1'. } } } @@ -579,7 +569,7 @@ { \str_map_tokens:Nn #2 \str_aux_byte_to_hexadecimal:N } } \tl_const:Nx \c_str_bytes_escape_name_str - { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str () /<>[] } + { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str ()/<>[] } \cs_new_protected_nopar:Npn \str_bytes_escape_name:NN #1#2 { \str_set:Nx #1 @@ -587,10 +577,10 @@ } \cs_new_nopar:Npn \str_bytes_escape_name_aux:N #1 { - \int_compare:nNnTF {`#1} < {"21} + \int_compare:nNnTF {`#1} < { "21 } { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } { \str_if_contains_char:NNTF \c_str_bytes_escape_name_str #1 @@ -607,10 +597,10 @@ } \cs_new_nopar:Npn \str_bytes_escape_string_aux:N #1 { - \int_compare:nNnTF {`#1} < {"21} + \int_compare:nNnTF {`#1} < { "21 } { \c_backslash_str \str_aux_byte_to_octal:N #1 } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_backslash_str \str_aux_byte_to_octal:N #1 } { \str_if_contains_char:NNT \c_str_bytes_escape_string_str #1 @@ -719,16 +709,16 @@ { \prg_case_str:xxn {#2} { - {n} {10} - {r} {13} - {t} {9} - {b} {8} - {f} {12} - {(} {40} - {)} {41} - {\c_backslash_str} {92} - {^^J} {-1} - {^^M} {-1} + { n } { 10 } + { r } { 13 } + { t } { 9 } + { b } { 8 } + { f } { 12 } + { ( } { 40 } + { ) } { 41 } + { \c_backslash_str } { 92 } + { ^^J } { -1 } + { ^^M } { -1 } } {`#2} } @@ -768,14 +758,14 @@ } \cs_new_nopar:Npn \str_bytes_percent_encode_aux:N #1 { - \int_compare:nNnTF {`#1} < {"41} + \int_compare:nNnTF {`#1} < { "41 } { \str_if_contains_char:NNTF \c_str_bytes_percent_encode_not_str #1 { #1 } { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } } { - \int_compare:nNnTF {`#1} > {"7E} + \int_compare:nNnTF {`#1} > { "7E } { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } { \str_if_contains_char:NNTF \c_str_bytes_percent_encode_str #1 @@ -837,19 +827,18 @@ \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_i:N #1 { \quark_if_recursion_tail_stop:N #1 - \int_set:Nn \l_str_char_int { `#1 } - \str_native_from_UTF_viii_aux_ii:nN {128} \c_one - \str_native_from_UTF_viii_aux_ii:nN {64} \c_zero - \str_native_from_UTF_viii_aux_ii:nN {32} \c_two - \str_native_from_UTF_viii_aux_ii:nN {16} \c_three - \str_native_from_UTF_viii_aux_ii:nN {8} \c_four + \int_set:Nn \l_str_char_int {`#1} + \str_native_from_UTF_viii_aux_ii:nN { 128 } \c_one + \str_native_from_UTF_viii_aux_ii:nN { 64 } \c_zero + \str_native_from_UTF_viii_aux_ii:nN { 32 } \c_two + \str_native_from_UTF_viii_aux_ii:nN { 16 } \c_three + \str_native_from_UTF_viii_aux_ii:nN { 8 } \c_four \msg_error:nnx { str } { utf8-invalid-byte } {#1} \use_i:nnn \str_native_from_UTF_viii_aux_i:N \q_stop \str_native_from_UTF_viii_aux_iii:N } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_ii:nN #1#2 +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_ii:nN #1#2 { \int_compare:nNnTF \l_str_char_int < {#1} { @@ -858,8 +847,7 @@ } { \int_sub:Nn \l_str_char_int {#1} } } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_iii:N #1 +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_iii:N #1 { \int_compare:nNnTF \l_str_bytes_int < \c_two { \str_native_from_UTF_viii_aux_iv: #1 } @@ -869,11 +857,11 @@ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w \fi: \quark_if_recursion_tail_stop:N #1 - \tex_multiply:D \l_str_char_int by 64 \scan_stop: % no interface?? + \tex_multiply:D \l_str_char_int by 64 \scan_stop: \int_decr:N \l_str_bytes_int - \int_compare:nNnTF {`#1} < {128} + \int_compare:nNnTF {`#1} < { 128 } { \use_ii:nn } - { \int_compare:nNnTF {`#1} < {192} } + { \int_compare:nNnTF {`#1} < { 192 } } { \int_add:Nn \l_str_char_int { `#1 - 128 } \str_native_from_UTF_viii_aux_iii:N @@ -884,47 +872,32 @@ } } } -\cs_new_protected_nopar:Npn - \str_native_from_UTF_viii_aux_iv: +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_iv: { \int_compare:nNnTF \l_str_bytes_int = \c_zero { \msg_error:nnx { str } { utf8-extra-byte } { \int_eval:n { \l_str_char_int + 128 } } } + { \str_native_from_UTF_viii_aux_v: } + \str_native_from_UTF_viii_aux_i:N + } +\pdftex_if_engine:TF + { + \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_v: { - \pdftex_if_engine:TF + \int_compare:nNnTF { \l_str_char_int } < { 256 } + { \str_aux_convert_store: } { - \int_compare:nNnTF { \l_str_char_int } < { 256 } - { \str_aux_convert_store: } - { - \msg_error:nnx { str } { utf8-pdftex-overflow } - { \int_use:N \l_str_char_int } - } + \msg_error:nnx { str } { utf8-pdftex-overflow } + { \int_use:N \l_str_char_int } } - { \str_aux_convert_store: } } - \str_native_from_UTF_viii_aux_i:N } -\msg_new:nnn { str } { utf8-invalid-byte } { - \int_compare:nNnTF {`#1} < {256} - { Byte~number~\int_eval:n {`#1}~invalid~in~utf-8~encoding. } - { The~character~number~\int_eval:n {`#1}~is~not~a~byte. } + \cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_v: + { \str_aux_convert_store: } } -\msg_new:nnn { str } { utf8-missing-byte } - { The~byte~number~\int_eval:n {`#1}~is~not~a~valid~continuation~byte. } -\msg_new:nnn { str } { utf8-extra-byte } - { The~byte~number~\int_eval:n {`#1}~is~only~valid~as~a~continuation~byte. } -\msg_new:nnnn { str } { utf8-premature-end } - { Incomplete~last~utf8~character. } - { - The~sequence~of~byte~that~I~need~to~convert~to~utf8 \ - ended~before~the~last~character~was~complete.~Perhaps \ - it~was~mistakenly~truncated? - } -\msg_new:nnn { str } { utf8-pdftex-overflow } - { The~character~number~#1~is~too~big~for~pdfTeX. } \cs_new_protected_nopar:Npn \str_UTF_viii_from_native:NN #1#2 { \group_begin: @@ -936,14 +909,14 @@ \cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_i:N #1 { \int_set:Nn \l_str_char_int {`#1} - \int_compare:nNnTF \l_str_char_int < {128} + \int_compare:nNnTF \l_str_char_int < { 128 } { \tl_gput_right:Nx \g_str_result_tl {#1} } { \tl_gclear:N \g_str_tmpa_tl \int_set:Nn \l_str_bytes_int { 64 } - \str_UTF_viii_from_native_aux_ii:n {32} - \str_UTF_viii_from_native_aux_ii:n {16} - \str_UTF_viii_from_native_aux_ii:n {8} + \str_UTF_viii_from_native_aux_ii:n { 32 } + \str_UTF_viii_from_native_aux_ii:n { 16 } + \str_UTF_viii_from_native_aux_ii:n { 8 } \ERROR % somehow the unicode char was > "1FFFFF > "10FFFF \tl_gclear:N \g_str_tmpa_tl \use_none:n \q_stop @@ -956,15 +929,41 @@ { \str_aux_convert_store:NNn \tl_gput_left:Nx \g_str_tmpa_tl - { 128 + \int_mod:nn { \l_str_char_int } {64} } - \tex_divide:D \l_str_char_int by 64 \scan_stop: % no interface?? + { 128 + \int_mod:nn { \l_str_char_int } { 64 } } + \tex_divide:D \l_str_char_int by 64 \scan_stop: \int_add:Nn \l_str_bytes_int {#1} \int_compare:nNnT \l_str_char_int < {#1} { \use_none_delimit_by_q_stop:w } } -\cs_gset:Npn \str_length_skip_spaces:N +\msg_new:nnnn { str } { x-missing-brace } + { Missing~closing~brace~in~ \token_to_str:N \x ~byte~sequence. } + { + You~wrote~something~like~ + `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ + The~closing~brace~is~missing. + } +\msg_new:nnn { str } { utf8-invalid-byte } + { + \int_compare:nNnTF {`#1} < { 256 } + { Byte~number~ \int_eval:n {`#1} ~invalid~in~utf-8~encoding. } + { The~character~number~ \int_eval:n {`#1} ~is~not~a~byte. } + } +\msg_new:nnn { str } { utf8-missing-byte } + { The~byte~number~ \int_eval:n {`#1} ~is~not~a~valid~continuation~byte. } +\msg_new:nnn { str } { utf8-extra-byte } + { The~byte~number~ \int_eval:n {`#1} ~is~only~valid~as~a~continuation~byte. } +\msg_new:nnnn { str } { utf8-premature-end } + { Incomplete~last~UTF8~character. } + { + The~sequence~of~byte~that~to~be~converted~to~UTF8~ + ended~before~the~last~character~was~complete.~Perhaps~ + it~was~mistakenly~truncated? + } +\msg_new:nnn { str } { utf8-pdftex-overflow } + { The~character~number~#1~is~too~big~for~pdfTeX. } +\cs_set:Npn \str_length_skip_spaces:N { \exp_args:No \str_length_skip_spaces:n } -\cs_gset_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n +\cs_set_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n %% %% %% End of file `l3str.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty b/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty index 79490432742..9d4a42407e3 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty @@ -47,7 +47,7 @@ %% ----------------------------------------------------------------------- %% \RequirePackage{xparse} -\GetIdInfo$Id: xcoffins.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: xcoffins.dtx 2963 2011-11-15 22:06:41Z joseph $ {L3 Experimental design-level coffins} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} @@ -146,6 +146,14 @@ { \coffin_resize:Nnn #1 {#2} {#3} } \NewDocumentCommand \ScaleCoffin { m m m } { \coffin_scale:Nnn #1 {#2} {#3} } +\NewDocumentCommand \CoffinDepth { m } + { \coffin_dp:N #1 } +\NewDocumentCommand \CoffinHeight { m } + { \coffin_ht:N #1 } +\NewDocumentCommand \CoffinTotalHeight { m } + { \dim_eval:w \coffin_ht:N #1 + \coffin_dp:N #1 \dim_eval_end: } +\NewDocumentCommand \CoffinWidth { m } + { \coffin_wd:N #1 } \NewDocumentCommand \DisplayCoffinHandles { m m } { \cs_if_exist:cTF { l_coffin_poles_ \int_value:w #1 _prop } diff --git a/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty b/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty index b626f072d30..e8a3345204f 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty @@ -48,11 +48,14 @@ %% ----------------------------------------------------------------------- %% \RequirePackage{l3names} -\GetIdInfo$Id: l3galley.dtx 2899 2011-10-09 21:22:06Z joseph $ +\GetIdInfo$Id: l3galley.dtx 2938 2011-10-25 18:41:20Z joseph $ {L3 Experimental galley code} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} \package_check_loaded_expl: +\cs_new_protected:Npn \scan_marker_new:N #1 + { \cs_new_eq:NN #1 \scan_stop: } +\cs_generate_variant:Nn \scan_marker_new:N { c } \cs_new_protected_nopar:Npn \galley_leave_vmode: { \tex_unhbox:D \c_empty_box } \clist_new:N \l_galley_parshape_left_indent_clist @@ -69,7 +72,6 @@ \cs_new_eq:NN \l_galley_width_dim \tex_hsize:D \bool_new:N \g_galley_begin_level_bool \bool_new:N \l_galley_begin_level_bool -\bool_gset_true:N \g_galley_begin_level_bool \bool_new:N \g_galley_omit_next_indent_bool \bool_new:N \l_galley_omit_next_indent_bool \bool_new:N \g_galley_parshape_set_bool @@ -107,9 +109,10 @@ \bool_gset_false:N \g_galley_parshape_set_bool \int_gzero:N \g_galley_previous_par_lines_int \tl_gclear:N \g_galley_restore_running_tl - \tl_gclear:N \g_galley_whatsits_previous_tl - \tl_gclear:N \g_galley_whatsits_next_tl + \tl_gclear:N \g_galley_whatsit_previous_tl + \tl_gclear:N \g_galley_whatsit_next_tl } +\galley_initialise_variables: \cs_new_protected_nopar:Npn \galley_initialise_settings: { \dim_set_eq:NN \l_galley_text_width_dim \l_galley_width_dim @@ -142,10 +145,10 @@ \g_galley_previous_par_lines_int \tl_set_eq:NN \l_galley_restore_running_tl \g_galley_restore_running_tl - \tl_set_eq:NN \l_galley_whatsits_previous_tl - \g_galley_whatsits_previous_tl - \tl_set_eq:NN \l_galley_whatsits_next_tl - \g_galley_whatsits_next_tl + \tl_set_eq:NN \l_galley_whatsit_previous_tl + \g_galley_whatsit_previous_tl + \tl_set_eq:NN \l_galley_whatsit_next_tl + \g_galley_whatsit_next_tl } \cs_new_protected_nopar:Npn \galley_restore_parameters: { @@ -171,10 +174,10 @@ \l_galley_previous_par_lines_int \tl_gset_eq:NN \g_galley_restore_running_tl \l_galley_restore_running_tl - \tl_gset_eq:NN \g_galley_whatsits_previous_tl - \l_galley_whatsits_previous_tl - \tl_gset_eq:NN \g_galley_whatsits_next_tl - \l_galley_whatsits_next_tl + \tl_gset_eq:NN \g_galley_whatsit_previous_tl + \l_galley_whatsit_previous_tl + \tl_gset_eq:NN \g_galley_whatsit_next_tl + \l_galley_whatsit_next_tl } \cs_new_protected_nopar:Npn \galley_level: { @@ -190,8 +193,28 @@ \galley_restore_parameters: \group_end: } +\scan_marker_new:N \s_par_omit \cs_new_protected_nopar:Npn \galley_std_par: { + \s_par_omit + \exp_after:wN \galley_std_par_aux_i: \tex_romannumeral:D - `0 + } +\cs_new_protected:Npn \galley_std_par_aux_i: + { + \peek_meaning:NTF \s_par_omit + { \galley_std_par_aux:N } + { \galley_std_par_aux_ii: } + } +\cs_new_protected:Npn \galley_std_par_aux:N #1 + { + \str_if_eq:xxF {#1} { \s_par_omit } + { + \galley_std_par_aux_ii: + #1 + } + } +\cs_new_protected_nopar:Npn \galley_std_par_aux_ii: + { \mode_if_vertical:TF { \tex_par:D } { @@ -206,6 +229,15 @@ \tl_gclear:N \g_galley_par_after_hook_tl \tex_penalty:D \c_ten_thousand } +\cs_new_protected:Npn \galley_end_par:n #1 + { + \s_par_omit + \bool_if:nF \g_galley_begin_level_bool + { + #1 + \galley_std_par: + } + } \cs_set_protected_nopar:Npn \par { \galley_std_par: } \tl_set:Nn \@par { \galley_std_par: } \cs_new_protected_nopar:Npn \galley_display_begin: @@ -354,14 +386,14 @@ { \g_galley_whatsit_next_tl \tl_gclear:N \g_galley_whatsit_next_tl - \bool_if:NT \g_galley_omit_next_indent_bool + \bool_if:NF \g_galley_omit_next_indent_bool { - \box_if_empty:NF \g_galley_last_box + \box_if_empty:NF \g_galley_last_box { \hbox_to_wd:nn \l_galley_par_indent_dim { } } } \skip_horizontal:N \l_galley_par_begin_skip \g_galley_par_begin_hook_tl - \bool_gset_true:N \g_galley_omit_next_indent_bool + \bool_gset_false:N \g_galley_omit_next_indent_bool } \cs_new_protected_nopar:Npn \galley_restore_running_parameters: { @@ -732,6 +764,20 @@ { \int_use:N \etex_widowpenalties:D \int_eval:w #1 \int_eval_end: , } \cs_new_protected_nopar:Npn \galley_interline_penalty: { \int_use:N \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero } +\cs_set_protected_nopar:Npn \nobreak + { \bool_gset_true:N \g_galley_no_break_next_bool } +\cs_set_protected_nopar:Npn \noindent + { + \tex_noindent:D + \bool_gset_false:N \g_galley_omit_next_indent_bool + } +\cs_set_protected_nopar:Npn \vspace #1 + { + \@ifstar + { \galley_set_user_vspace:n {#1} } + { \galley_set_user_vspace:n {#1} } + } +\cs_set_eq:NN \@@par \galley_std_par: \cs_set_protected_nopar:Npn \@afterheading { \bool_gset_true:N \g_galley_no_break_next_bool @@ -742,6 +788,8 @@ } \cs_set_protected:Npn \@hangfrom #1 { + \bool_gset_true:N \g_galley_omit_next_indent_bool + \leavevmode \setbox \@tempboxa = \hbox { {#1} } \galley_parshape_single_par:nnnN \c_one @@ -749,16 +797,9 @@ \c_zero_dim \c_false_bool \bool_gset_true:N \g_galley_no_break_next_bool - \bool_gset_true:N \galley_omit_next_indent_bool + \bool_gset_true:N \g_galley_omit_next_indent_bool \box \@tempboxa } -\cs_set_protected:Npn \@makefntext #1 - { - \UseTemplate { justification } { std } { indent-width = 1 em } - \bool_gset_true:N \galley_omit_next_indent_bool - \galley_leave_vmode: - \hb@xt@ 1.8em { \hss \@makefnmark } #1 - } %% %% %% End of file `l3galley.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty b/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty index 160fca57631..5ebe766a12b 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty @@ -48,7 +48,7 @@ %% ----------------------------------------------------------------------- %% \RequirePackage{l3names} -\GetIdInfo$Id: xgalley.dtx 2895 2011-10-09 15:58:19Z joseph $ +\GetIdInfo$Id: xgalley.dtx 2922 2011-10-17 19:01:04Z joseph $ {L3 Experimental galley} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} @@ -606,11 +606,6 @@ { \skip_eval:n { \l_galley_display_end_vspace_tl } } } } -\RenewDocumentCommand \nobreak { } - { \bool_gset_true:N \g_galley_no_break_next_bool } -\RenewDocumentCommand \noindent { } - { \bool_gset_false:N \g_galley_omit_next_indent_bool } -\RenewDocumentCommand \vspace { s m } { \galley_set_vspace_user:n {#2} } %% %% %% End of file `xgalley.sty'. |