summaryrefslogtreecommitdiff
path: root/Master
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2012-02-28 00:40:17 +0000
committerKarl Berry <karl@freefriends.org>2012-02-28 00:40:17 +0000
commited49b3dfe19b91f9fd96ab70fe95dcda8fba24c8 (patch)
tree0eb59527efd68f813d629e7d0977fe31d8d6419f /Master
parentb1436d6ee293a02df32a18832d21c54348335565 (diff)
l3experimental 3471 (26feb12)
git-svn-id: svn://tug.org/texlive/trunk@25523 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/README20
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3dt/l3dt.pdfbin517832 -> 521017 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3sort/l3sort.pdfbin500908 -> 501635 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3str/l3flag.pdfbin419245 -> 403012 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdfbin641586 -> 713499 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdfbin773178 -> 776415 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-analysis.pdfbin504918 -> 507795 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-build.pdfbin0 -> 463331 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdfbin481798 -> 481854 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdfbin636122 -> 636675 bytes
-rw-r--r--Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdfbin566439 -> 567159 bytes
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3dt/l3dt.dtx10
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3sort/l3sort.dtx4
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx7450
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx137
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins3
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-analysis.dtx54
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-build.dtx301
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx211
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3dt/l3dt.sty2
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3sort/l3sort.sty2
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty2824
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty3638
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty68
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-analysis.sty35
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-build.sty120
-rw-r--r--Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty2
27 files changed, 9478 insertions, 5403 deletions
diff --git a/Master/texmf-dist/doc/latex/l3experimental/README b/Master/texmf-dist/doc/latex/l3experimental/README
index d4097182801..02da76a3e61 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/README
+++ b/Master/texmf-dist/doc/latex/l3experimental/README
@@ -33,7 +33,7 @@ bundles:
* l3str
* xcoffins
* xgalley
-
+
l3dt
----
@@ -50,7 +50,7 @@ in a flexible manner using the l3sort module. The definition of how to sort
two items is provided by the programmer at the point at which the sort is
carried out. Internally, the sorting algorithm is designed to take advantage
of TeX token registers to allow a high performance and scalable sort.
-
+
l3str
-----
@@ -62,7 +62,7 @@ including for manipulations such as UTF8 mappings in pdfTeX. The second
part of the bundle is l3regex, a regular expression search-and-replace
implementation written in TeX primitives. The regex module works on a string
basis, ignoring category codes.
-
+
xcoffins
--------
@@ -76,10 +76,10 @@ xgalley
-------
In LaTeX3 terminology a galley is a rectangular area which receives
-text and other material filling it from top. The vertically extend of
-a galley is normally not restricted: instead certain chunks are taken
-off the top of an already partially filled galley to form columns or
-similar areas on a page. This process is typically asynchronous but
+text and other material filling it from top. The vertically extend of
+a galley is normally not restricted: instead certain chunks are taken
+off the top of an already partially filled galley to form columns or
+similar areas on a page. This process is typically asynchronous but
there are ways to control or change its behaviour. The xgalley module
provides a mechanism for filling galleys and controlling the spacing,
hyphenation and justification within them.
@@ -87,8 +87,8 @@ hyphenation and justification within them.
DISCUSSION
----------
-Discussion concerning the approach, suggestions for improvements,
-changes, additions, etc. should be addressed to the list LaTeX-L.
+Discussion concerning the approach, suggestions for improvements,
+changes, additions, etc. should be addressed to the list LaTeX-L.
You can subscribe to this list by sending mail to
@@ -104,7 +104,7 @@ BUGS
The issue tracker for LaTeX3 bugs is currently located at
https://github.com/latex3/svn-mirror/issues
-
+
Please report specific issues with LaTeX3 code there. More general
discussion should be directed to the LaTeX-L lists.
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3dt/l3dt.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3dt/l3dt.pdf
index 0b26a7400b7..a3d8982730a 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3dt/l3dt.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3dt/l3dt.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3sort/l3sort.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3sort/l3sort.pdf
index 53fb0a7caf9..b8fed31a92d 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3sort/l3sort.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3sort/l3sort.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3flag.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3flag.pdf
index d0c22781cc9..ec12db85996 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3flag.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3flag.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf
index cffc4b28d65..2e653d53199 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf
index 204e166ceb2..7c08e05d9c4 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-analysis.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-analysis.pdf
index 3c710d54592..19e5e394282 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-analysis.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-analysis.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-build.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-build.pdf
new file mode 100644
index 00000000000..92ca51f69a5
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3tl-build.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf
index ccfae9b6c9c..9b142d0d13f 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf
index 63d0647a4f7..8a7c17a3c49 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf
index 39f11d71db8..56afacd4b13 100644
--- a/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf
+++ b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf
Binary files differ
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3dt/l3dt.dtx b/Master/texmf-dist/source/latex/l3experimental/l3dt/l3dt.dtx
index e5063618228..0fd090d9ef7 100644
--- a/Master/texmf-dist/source/latex/l3experimental/l3dt/l3dt.dtx
+++ b/Master/texmf-dist/source/latex/l3experimental/l3dt/l3dt.dtx
@@ -36,7 +36,7 @@
%
%<*driver|package>
\RequirePackage{expl3}
-\GetIdInfo$Id: l3dt.dtx 3251 2012-01-28 09:42:44Z joseph $
+\GetIdInfo$Id: l3dt.dtx 3459 2012-02-26 08:58:53Z joseph $
{L3 Experimental data tables}
%</driver|package>
%<*driver>
@@ -500,15 +500,15 @@
% \cs{q_dt_header} \\
% \cs{q_dt_row} \\
% \meta{row$_0$} \\
-% \cs{q_dt} \meta{key$_0$} \cs{q_dt} \Arg{data$_{\0,\0}$} \\
-% \cs{q_dt} \meta{key$_1$} \cs{q_dt} \Arg{data$_{\0,\1}$} \\
+% \cs{q_dt} \meta{key$_0$} \cs{q_dt} \Arg{data$_{0,0}$} \\
+% \cs{q_dt} \meta{key$_1$} \cs{q_dt} \Arg{data$_{0,1}$} \\
% \ldots \\
% \cs{q_dt} \\
% \cs{q_nil} \\
% \cs{q_dt_row} \\
% \meta{row$_2$} \\
-% \cs{q_dt} \meta{key$_0$} \cs{q_dt} \Arg{data$_{\1,\0}$} \\
-% \cs{q_dt} \meta{key$_1$} \cs{q_dt} \Arg{data$_{\1,\1}$} \\
+% \cs{q_dt} \meta{key$_0$} \cs{q_dt} \Arg{data$_{1,0}$} \\
+% \cs{q_dt} \meta{key$_1$} \cs{q_dt} \Arg{data$_{1,1}$} \\
% \ldots \\
% \cs{q_dt} \\
% \cs{q_nil} \\
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3sort/l3sort.dtx b/Master/texmf-dist/source/latex/l3experimental/l3sort/l3sort.dtx
index 65a95dbee29..0d80aebcc51 100644
--- a/Master/texmf-dist/source/latex/l3experimental/l3sort/l3sort.dtx
+++ b/Master/texmf-dist/source/latex/l3experimental/l3sort/l3sort.dtx
@@ -1,5 +1,5 @@
% \iffalse
-%
+%
%% File l3sort.dtx (C) Copyright 2012 The LaTeX3 Project
%%
%% It may be distributed and/or modified under the conditions of the
@@ -36,7 +36,7 @@
%
%<*driver|package>
\RequirePackage{expl3}
-\GetIdInfo$Id: l3sort.dtx 3295 2012-02-02 09:30:14Z joseph $
+\GetIdInfo$Id: l3sort.dtx 3467 2012-02-26 15:39:35Z bruno $
{L3 Experimental sorting functions}
%</driver|package>
%<*driver>
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx
index d2968338f46..887884d7c3a 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 3326 2012-02-07 00:39:44Z bruno $
+\GetIdInfo$Id: l3regex.dtx 3463 2012-02-26 15:05:14Z bruno $
{L3 Experimental Regular Expressions}
%</driver|package>
%<*driver>
@@ -76,7 +76,8 @@
% The \pkg{l3regex} package provides regular expression testing,
% extraction of submatches, splitting, and replacement, all acting
% on token lists. The syntax of regular expressions is mostly a subset
-% of the PCRE syntax (and very close to POSIX), with some additions
+% of the \textsc{pcre} syntax (and very close to \textsc{posix}),
+% with some additions
% due to the fact that \TeX{} manipulates tokens rather than characters.
% For performance reasons, only a limited set of features are implemented.
% Notably, back-references are not supported.
@@ -180,18 +181,25 @@
% \item[\\S] Any token not matched by |\s|.
% \item[\\V] Any token not matched by |\v|.
% \item[\\W] Any token not matched by |\w|.
-% \item[\\N] Any token not matched by |\n|.
% \end{l3regex-syntax}
% Of those, |.|, |\D|, |\H|, |\N|, |\S|, |\V|, and |\W| will match arbitrary
% control sequences.
%
-% Character classes match exactly one character in the subject string.
+% Character classes match exactly one token in the subject.
% \begin{l3regex-syntax}
% \item[{[\ldots{}]}] Positive character class.
% Matches any of the specified tokens.
% \item[{[\char`\^\ldots{}]}] Negative character class.
% Matches any token other than the specified characters.
-% \item[{[x-y]}] Range (can be used with escaped characters).
+% \item[{x-y}] Within a character class, this denotes a range (can be
+% used with escaped characters).
+% \item[{[:\meta{name}:]}] Within a character class (one more set of
+% brackets), this denotes the \textsc{posix} character class
+% \meta{name}, which can be \texttt{alnum}, \texttt{alpha},
+% \texttt{ascii}, \texttt{blank}, \texttt{cntrl}, \texttt{digit},
+% \texttt{graph}, \texttt{lower}, \texttt{print}, \texttt{punct},
+% \texttt{space}, \texttt{upper}, \texttt{word}, or \texttt{xdigit}.
+% \item[{[:\char`\^\meta{name}:]}] Negative \textsc{posix} character class.
% \end{l3regex-syntax}
% For instance, |[a-oq-z\cC.]| matches any lowercase latin letter
% except |p|, as well as control sequences (see below for a description
@@ -264,25 +272,29 @@
% \item[\\c\Arg{regex}] A control sequence whose csname matches the
% \meta{regex}, anchored at the beginning and end, so that |\c{begin}|
% matches exactly \cs{begin}, and nothing else.
-% \item[\\cX\meta{character or class}] Matches a token with category
-% code |X| (any of |CBEMTPUDSLOA|) if it is also matched by the
-% \meta{character or class}. For instance, |\cL[A-Z]| matches
-% uppercase letters of category code letter, |\cC.| matches any
-% control sequence, and |\cO\d| matches digits of category other.
-% \item[{\\c[XYZ]\meta{character or class}}] Matches a token with
-% category |X|, |Y|, or |Z| (each being any of |CBEMTPUDSLOA|),
-% if it is also matched by the \meta{character or class}. For instance,
-% |\c[LSO].| matches tokens of category letter, space, or other.
-% \item[{\\c[\char`\^XYZ]\meta{character or class}}] Matches a token with
-% category different from |X|, |Y|, or |Z| (each being any of
-% |CBEMTPUDSLOA|), if it is also matched by the \meta{character or
-% class}. For instance, |\c[LSO].| matches tokens of category
-% letter, space, or other.
+% \item[\\cX] Applies to the next object, which can be a character,
+% character property, class, or group, and forces this object to
+% only match tokens with category |X| (any of |CBEMTPUDSLOA|. For
+% instance, |\cL[A-Z\d]| matches uppercase letters and digits of
+% category code letter, |\cC.| matches any control sequence, and
+% |\cO(abc)| matches |abc| where each character has category other.
+% \item[{\\c[XYZ]}] Applies to the next object, and forces it to only
+% match tokens with category |X|, |Y|, or |Z| (each being any of
+% |CBEMTPUDSLOA|). For instance, |\c[LSO](..)| matches two tokens of
+% category letter, space, or other.
+% \item[{\\c[\char`\^XYZ]}] Applies to the next object and prevents it
+% from matching any token with category |X|, |Y|, or |Z| (each being
+% any of |CBEMTPUDSLOA|). For instance, |\c[^O]\d| matches digits
+% which have any category different from other.
% \end{l3regex-syntax}
% The category code tests can be used inside classes; for instance,
% |[\cO\d \c[LO][A-F]]| matches what \TeX{} considers as hexadecimal
% digits, namely digits with category other, or uppercase letters from
-% |A| to |F| with category either letter or other.
+% |A| to |F| with category either letter or other. Within a group
+% affected by a category code test, the outer test can be overridden by
+% a nested test: for instance, |\cL(ab\cO\*cd)| matches |ab*cd| where
+% all characters are of category letter, except |*| which has category
+% other.
%
% The |\u| escape sequence allows to insert the contents of a token list
% directly into a regular expression or a replacement, avoiding the need
@@ -292,37 +304,50 @@
% argument once, in effect performing \cs{tl_to_str:v}. Quantifiers are
% not supported directly: use a group.
%
-% 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,
-% and should be escaped. Other non-alphanumeric characters can
-% still be escaped without harm. The escape sequences |\d|,
-% |\D|, etc. are also supported in character classes.
-% If the first character is |^|, then the meaning of the character
-% class is inverted. Ranges of characters can be expressed using
-% |-|, for instance, |[\D 0-5]| is equivalent to |[^6-9]|.
+% The option |(?i)| makes the match case insensitive (identifying
+% \texttt{A}--\texttt{Z} with \texttt{a}--\texttt{z}; no Unicode support
+% yet). This applies until the end of the group in which it appears, and
+% can be reverted using |(?-i)|. For instance, in
+% \verb"(?i)(a(?-i)b|c)d", the letters |a| and |d| are affected by the
+% |i| option. Characters within ranges and classes are affected
+% individually: |(?i)[Y-\\]| is equivalent to |[YZ\[\\yz]|, and
+% |(?i)[^aeiou]| matches any character which is not a vowel. Neither
+% character properties, nor |\c{...}| nor |\u{...}| are affected by the
+% |i| option.
+%
+% In character classes, only |[|, |^|, |-|, |]|, |\| and spaces are
+% special, and should be escaped. Other non-alphanumeric characters can
+% still be escaped without harm. Any escape sequence which matches a
+% single character (|\d|, |\D|, \emph{etc.}) is supported in character
+% classes. If the first character is |^|, then
+% the meaning of the character class is inverted. Ranges of characters
+% can be expressed using |-|, for instance, |[\D 0-5]| and |[^6-9]| are
+% equivalent.
%
% Capturing groups are a means of extracting information about the
% match. Parenthesized groups are labelled in the order of their
% 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
+% can be extracted and stored in a sequence of token lists using for
% instance \cs{regex_extract_once:nnNTF}.
%
-% \subsection{Syntax of in the replacement text}
+% The |\K| escape sequence resets the beginning of the match to the
+% current position in the token list. This only affects what is reported
+% as the full match. For instance,
+% \begin{verbatim}
+% \regex_extract_all:nnN { a \K . } { a123aaxyz } \l_foo_seq
+% \end{verbatim}
+% results in \cs{l_foo_seq} containing the items |{1}| and |{a}|: the
+% true matches are |{a1}| and |{aa}|, but they are trimmed by the use of
+% |\K|. The |\K| command does not affect capturing groups: for instance,
+% \begin{verbatim}
+% \regex_extract_once:nnN { (. \K c)+ \d } { acbc3 } \l_foo_seq
+% \end{verbatim}
+% results in \cs{l_foo_seq} containing the items |{c3}| and |{bc}|: the
+% true match is |{acbc3}|, with first submatch |{bc}|, but |\K| resets
+% the beginning of the match to the last position where it appears.
+%
+% \subsection{Syntax of 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
@@ -349,15 +374,15 @@
% |\0|, |\1| \emph{etc.}
% \end{l3regex-syntax}
%
-% \subsection{Precompiling regular expressions}
+% \subsection{Pre-compiling regular expressions}
%
% If a regular expression is to be used several times,
% it is better to compile it once rather than doing it
-% each time the regular expression is used. The precompiled
+% each time the regular expression is used. The compiled
% regular expression is stored in a variable. All
% of the \pkg{l3regex} module's functions can be given their
% regular expression argument either as an explicit string
-% or as a precompiled regular expression.
+% or as a compiled regular expression.
%
% \begin{function}{\regex_new:N}
% \begin{syntax}
@@ -372,7 +397,7 @@
% \begin{syntax}
% \cs{regex_set:Nn} \meta{regex~var} \Arg{regex}
% \end{syntax}
-% Stores a precompiled version of the \meta{regular expression}
+% Stores a compiled version of the \meta{regular expression}
% in the \meta{regex~var}. For instance, this function can be used
% as
% \begin{verbatim}
@@ -380,15 +405,32 @@
% \regex_set:Nn \l_my_regex { my\ (simple\ )? reg(ex|ular\ expression) }
% \end{verbatim}
% The assignment is local for \cs{regex_set:Nn} and global for
-% \cs{regex_gset:Nn}. Use \cs{regex_const:Nn} for precompiled expressions
+% \cs{regex_gset:Nn}. Use \cs{regex_const:Nn} for compiled expressions
% which will never change.
% \end{function}
%
+% \begin{function}{\regex_show:n, \regex_show:N}
+% \begin{syntax}
+% \cs{regex_show:n} \Arg{regex}
+% \end{syntax}
+% Shows how \pkg{l3regex} interprets the \meta{regex}. For instance,
+% \cs{regex_show:n} \verb+{\A X|Y}+ shows
+% \begin{verbatim}
+% +-branch
+% anchor at start (\A)
+% char code 88
+% +-branch
+% char code 89
+% \end{verbatim}
+% indicating that the anchor |\A| only applies to the first branch:
+% the second branch is not anchored to the beginning of the match.
+% \end{function}
+%
% \subsection{Matching}
%
% 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
+% while the later require a compiled expression as generated by
% \cs{regex_(g)set:Nn}.
%
% \begin{function}[TF]{\regex_match:nn, \regex_match:Nn}
@@ -416,7 +458,7 @@
% starts again from the character following the last character
% of the previous match, until reaching the end of the token list.
% Infinite loops are prevented in the case where the regular expression
-% can match an empty string: then we count one match between each
+% can match an empty token list: then we count one match between each
% pair of characters.
% For instance,
% \begin{verbatim}
@@ -514,8 +556,8 @@
% Searches for the \meta{regular expression} in the \meta{token list}
% and replaces the first match with the \meta{replacement}. The result
% is assigned locally to \meta{tl~var}. In the \meta{replacement},
-% |\0| represents the full match, |\1| represent the contents
-% of the first capturing group, |\2| of the second, \emph{etc.}
+% |\0| represents the full match, |\1| represent the contents of the
+% first capturing group, |\2| of the second, \emph{etc.}
% \end{function}
%
% \begin{function}[TF]{\regex_replace_all:nnN, \regex_replace_all:NnN}
@@ -523,97 +565,133 @@
% \cs{regex_replace_all:nnN} \Arg{regular expression} \Arg{replacement} \meta{tl~var}
% \cs{regex_replace_all:nnNTF} \Arg{regular expression} \Arg{replacement} \meta{tl~var} \Arg{true code} \Arg{false code}
% \end{syntax}
-% Replaces all occurrences of the \cs{regular expression}
-% in the \meta{token list} by the \meta{replacement}, where
-% |\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, and matches cannot overlap.
-% The result is assigned locally to \meta{tl~var}.
+% Replaces all occurrences of the \cs{regular expression} in the
+% \meta{token list} by the \meta{replacement}, where |\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, and matches cannot overlap. The result is assigned
+% locally to \meta{tl~var}.
% \end{function}
%
% \subsection{Bugs, misfeatures, future work, and other possibilities}
%
% The following need to be done now.
% \begin{itemize}
+% \item Change user function names!
% \item Clean up the use of messages.
-% \item Code comments.
-% \item Rewrite the documentation in a clearer way.
+% \item Rewrite the documentation in a more ordered way, perhaps add a
+% \textsc{bnf}?
+% \end{itemize}
+%
+% Additional error-checking to come.
+% \begin{itemize}
+% \item Detect that a trailing |\c|\meta{category} is an invalid regex.
+% \item Currently, |a{\x34}| is recognized as |a{4}|.
+% \item Cleaner error reporting in the replacement phase.
+% \item Add tracing information.
+% \item Detect attempts to use back-references.
+% \item Test for the maximum register \cs{c_max_register_int}.
+% \item Find out whether the fact that |\W| and friends match the
+% end-marker leads to bugs. Possibly update \cs{regex_item_reverse:n}.
+% \item Enforce that |\cC| can only be followed by a match-all dot.
% \end{itemize}
%
% Code improvements to come.
% \begin{itemize}
-% \item \cs{regex_show:N} to show how a given regular expression is
-% interpreted.
-% \item Test for the maximum register \cs{c_max_register_int}.
-% \item Use \tn{dimen} registers rather than \cs{l_regex_nesting_tl}
-% to build \cs{regex_nesting:n}.
-% \item Reduce the number of epsilon-transitions in alternatives.
-% \item Move the \enquote{reconstruction} part of \pkg{l3regex}
-% to \pkg{l3tl-analysis}.
-% \item Optimize regexes for csnames when the regex is a simple string.
-% \item Optimize simple strings: use less states (|abcade| should give
-% two states, for |abc| and |ade|). [Does that really make sense?]
-% \item Optimize groups with no alternative.
-% \item Optimize the use of \cs{prg_stepwise_...} functions.
-% \item Decide what |\c{\c{...}}| should do in the replacement text.
-% \item Fix the |\c{\cBx}| bug in replacement text.
+% \item Change \tn{skip} to \tn{dimen} for the array of active
+% threads, and shift the array of submatch informations so that it
+% starts at \tn{skip}$0$.
+% \item Optimize |\c{abc}| for matching a specific control sequence.
+% \item Only build \c{...} once.
+% \item Use \tn{skip} for the left and right state stacks when
+% compiling a regex.
+% \item Should \cs{regex_action_free_group:n} only be used for greedy
+% |{n,}| quantifier? (I think not.)
+% \item Quantifiers for |\u| and assertions.
+% \item Improve digit grabbing for the |\g| escape in replacement.
+% Allow arbitrary integer expressions for all those numbers?
+% \item When matching, keep track of an explicit stack of
+% \texttt{current_state} and \texttt{current_submatches}.
+% \item If possible, when a state is reused by the same thread, kill
+% other subthreads.
+% \item Use \tn{dimen} registers rather than \cs{l_regex_balance_tl}
+% to build \cs{regex_replacement_balance_one_match:n}.
+% \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|). [Does that really make sense?]
+% \item Optimize groups with no alternative.
+% \item Optimize states with a single \cs{regex_action_free:n}.
+% \item Optimize the use of \cs{regex_action_success:} by inserting it
+% in state $2$ directly instead of having an extra transition.
+% \item Optimize the use of \cs{prg_stepwise_...} functions.
+% \item Groups don't capture within regexes for csnames; optimize and
+% document.
+% \item Decide and document what |\c{\c{...}}| should do in the
+% replacement text, similar questions for |\u|.
+% \item Better \enquote{show} for anchors, properties, and catcode tests.
+% \item Does |\K| really need a new state for itself?
+% \item When compiling, use a boolean \texttt{in_cs} and less magic
+% numbers.
+% \item Instead of checking whether the character is special or
+% alphanumeric using its character code, check if it is special in
+% regexes with \cs{cs_if_exist} tests.
% \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 |(*..)| and |(?..)| sequences to set some options
-% (partially implemented).
-% \item |\K| for resetting the beginning of the match.
-% \item UTF-8 mode for pdf\TeX{}.
-% \item Newline conventions are not done.
-% In particular, we should have an option for |.| not to match newlines.
-% Also, |\A| should differ from |^|, and |\Z|, |\z| and |$| should
-% differ.
-% \item Unicode properties: |\p{..}| and |\P{..}|;
-% |\X| which should match any \enquote{extended} Unicode sequence.
-% This requires to manipulate a lot of data, hence a lot of optimization
-% ahead.
+% \item Allow |\cL(abc)| in replacement text.
+% \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 |(*..)| and |(?..)| sequences to set some options.
+% \item UTF-8 mode for pdf\TeX{}.
+% \item Newline conventions are not done.
+% In particular, we should have an option for |.| not to match newlines.
+% Also, |\A| should differ from |^|, and |\Z|, |\z| and |$| should
+% differ.
+% \item Unicode properties: |\p{..}| and |\P{..}|;
+% |\X| which should match any \enquote{extended} Unicode sequence.
+% This requires to manipulate a lot of data, probably using tree-boxes.
% \end{itemize}
%
-% The following features of PCRE or Perl will probably not be implemented.
+% The following features of \textsc{pcre} or Perl will probably not be
+% implemented.
% \begin{itemize}
-% \item |\ddd|, matching the character with code \texttt{ddd} in octal;
-% \item POSIX character classes |[:alpha:]| \emph{etc.}, this is redundant;
-% \item Callout with |(?C...)|, we cannot run arbitrary user code during
-% the matching, because the regex code uses registers in an unsafe way;
-% \item Conditional subpatterns (other than with a look-ahead
-% or look-behind condition): this is non-regular, isn't it?
-% \item Named subpatterns: \TeX{} programmers have lived so far without
-% any need for named macro parameters.
+% \item |\ddd|, matching the character with octal code \texttt{ddd};
+% \item Callout with |(?C...)|, we cannot run arbitrary user code
+% during the matching, because the regex code uses registers in an
+% unsafe way;
+% \item Conditional subpatterns (other than with a look-ahead or
+% look-behind condition): this is non-regular, isn't it?
+% \item Named subpatterns: \TeX{} programmers have lived so far
+% without any need for named macro parameters.
% \end{itemize}
%
-% The following features of PCRE or perl will definitely not be implemented.
+% The following features of \textsc{pcre} or Perl will definitely not be
+% implemented.
% \begin{itemize}
-% \item |\cx|, similar to \TeX{}'s own |\^^x|;
-% \item Comments: \TeX{} already has its own system for comments.
-% \item |\Q...\E| escaping: this would require to read the argument
-% verbatim, which is not in the scope of this module.
-% \item Atomic grouping, possessive quantifiers: those tools, mostly
-% meant to fix catastrophic backtracking, are unnecessary in a
-% non-backtracking algorithm, and difficult to implement.
-% \item Subroutine calls: this syntactic sugar is difficult to include
-% in a non-backtracking algorithm, in particular because the
-% corresponding group should be treated as atomic. Also, we cannot
-% afford to run user code within the regular expression matching,
-% because of our \enquote{misuse} of registers.
-% \item Recursion: this is a non-regular feature.
-% \item Back-references: non-regular feature, this requires backtracking,
-% which is prohibitively slow.
-% \item Backtracking control verbs: intrinsically tied to backtracking.
-% \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.
+% \item |\cx|, similar to \TeX{}'s own |\^^x|;
+% \item Comments: \TeX{} already has its own system for comments.
+% \item |\Q...\E| escaping: this would require to read the argument
+% verbatim, which is not in the scope of this module.
+% \item Atomic grouping, possessive quantifiers: those tools, mostly
+% meant to fix catastrophic backtracking, are unnecessary in a
+% non-backtracking algorithm, and difficult to implement.
+% \item Subroutine calls: this syntactic sugar is difficult to include
+% in a non-backtracking algorithm, in particular because the
+% corresponding group should be treated as atomic. Also, we cannot
+% afford to run user code within the regular expression matching,
+% because of our \enquote{misuse} of registers.
+% \item Recursion: this is a non-regular feature.
+% \item Back-references: non-regular feature, this requires
+% backtracking, which is prohibitively slow.
+% \item Backtracking control verbs: intrinsically tied to
+% backtracking.
+% \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}
@@ -626,7 +704,7 @@
% \begin{macrocode}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
-\RequirePackage{l3str, l3tl-analysis, l3flag}
+\RequirePackage{l3tl-build, l3tl-analysis, l3flag, l3str}
% \end{macrocode}
%
% \subsection{Plan of attack}
@@ -638,36 +716,42 @@
% code tends to run slowly, and we must use faster, albeit slightly more
% restrictive, techniques, coming from automata theory.
%
-% Given a regular expression of $n$ characters, we build a
-% non-deterministic finite automaton (NFA) with roughly $n$ states,
-% which accepts precisely those token lists matching that regular
-% expression. Then loop through the query token list one token (one
-% \enquote{index}) at a time, exploring in parallel every possible path
-% through the NFA. When performing this matching, we keep track of an
-% array of the states currently \enquote{active}, which are to be
-% considered in order when the next token is read.
+% Given a regular expression of $n$ characters, we do the following:
+% \begin{itemize}
+% \item (Compiling.) Analyse the regex, finding invalid input, and
+% convert it to an internal representation.
+% \item (Building.) Convert the compiled regex to a non-deterministic
+% finite automaton (\textsc{nfa}) with roughly $n$ states which
+% accepts precisely token lists matching that regex.
+% \item (Matching.) Loop through the query token list one token (one
+% \enquote{position}) at a time, exploring in parallel every
+% possible path (\enquote{active thread}) through the \textsc{nfa},
+% considering active threads in an order determined by the
+% quantifiers' greediness.
+% \end{itemize}
%
% We use the following vocabulary in the code comments (and in variable
% names).
% \begin{itemize}
% \item \emph{Group}: index of the capturing group, $-1$ for
% non-capturing groups.
-% \item \emph{Index}: each token in the query is labelled by an integer
-% \meta{index}, with $\cs{l_regex_min_index_int} - 1 \leq \meta{index}
-% \leq \cs{l_regex_max_index_int}$. The lowest and highest indexes
-% correspond to imaginary begin and end markers (with inacessible
-% category code and character code).
+% \item \emph{Position}: each token in the query is labelled by an
+% integer \meta{position}, with $\texttt{min_pos} - 1 \leq
+% \meta{position} \leq \texttt{max_pos}$. The lowest and highest
+% positions correspond to imaginary begin and end markers (with
+% inacessible category code and character code).
% \item \emph{Query}: the token list to which we apply the regular
% expression.
-% \item \emph{State}: each state of the NFA is labelled by an integer
-% \meta{state} with $0 \leq \meta{state} <
-% \cs{l_regex_max_state_int}$.
-% \item \emph{Active state}: state of the NFA that is reached when
-% reading the query string for the matching. Those states are
-% ordered according to the greediness of quantifiers.
+% \item \emph{State}: each state of the \textsc{nfa} is labelled by an
+% integer \meta{state} with $\texttt{min_state} \leq \meta{state} <
+% \texttt{max_state}$.
+% \item \emph{Active thread}: state of the \textsc{nfa} that is reached
+% when reading the query token list for the matching. Those threads
+% are ordered according to the greediness of quantifiers.
% \item \emph{Step}: used when matching, starts at $0$, incremented
% every time a character is read, and is not reset when searching
-% for repeated matches.
+% for repeated matches. The integer \cs{l_regex_step_int} is a
+% unique id for all the steps of the matching algorithm.
% \end{itemize}
%
% To acheive a good performance, we abuse \TeX{}'s registers in two
@@ -676,764 +760,120 @@
% functions. And we store integers in \tn{dimen} registers in scaled
% points (\texttt{sp}), using \TeX{}'s implicit conversion from
% dimensions to integers in some contexts. Specifically, the registers
-% are used as follows. When building,
+% are used as follows. When compiling, \tn{toks} registers are used
+% under the hood by functions from the \pkg{l3tl-build} module. When
+% building,
% \begin{itemize}
-% \item \tn{toks}\meta{state} has two parts separated by \cs{s_stop}:
-% a property list which holds the submatch information, followed by
-% the tests and actions to perform in the \meta{state} of the NFA.
-% \item \tn{skip}$i$ has the form \meta{group} \texttt{plus}
+% \item \tn{toks}\meta{state} holds the tests and actions to perform
+% in the \meta{state} of the \textsc{nfa}.
+% \item (Not implemented yet.)
+% \tn{skip}$i$ has the form \meta{group id} \texttt{plus}
% \meta{left state} \texttt{minus} \meta{right state}.
-% ^^A todo: not done yet.
% \end{itemize}
% When matching,
% \begin{itemize}
% \item \tn{dimen}\meta{state} is equal to the last \meta{step} in
% which the \meta{state} was active.
-% \item \tn{skip}$i$ holds the \meta{state} number corresponding to
-% the $i$-th active \meta{state} (smaller $i$ is higher precendence)
-% and has no shrink or stretch components.
-% \item \tn{toks}\meta{index} holds \meta{tokens} which \texttt{o}-
-% and \texttt{x}-expand to the \meta{index}-th token in the query.
+% \item (Currently, we use \tn{skip} instead of \tn{dimen}.)
+% \tn{dimen}\meta{thread}, with $\texttt{min_active} \leq
+% \meta{thread} < \texttt{max_active}$, is equal to the
+% \meta{state} in which the \meta{thread} currently is. The
+% \meta{threads} or ordered starting from the best to the least
+% preferred.
+% \item \tn{toks}\meta{thread} holds the submatch information for the
+% \meta{thread}, as the contents of a property list.
+% \item \tn{muskip}\meta{position} holds as its main and stretch
+% components the character and category code of the token at this
+% \meta{position} in the query.
+% \item \tn{toks}\meta{position} holds \meta{tokens} which \texttt{o}-
+% and \texttt{x}-expand to the \meta{position}-th token in the query.
+% \item \tn{skip} registers hold the value of end-points of all
+% submatches as would be extracted by the \cs{regex_extract}
+% functions. Since smaller \tn{skip} registers are used, the minimum
+% index is twice \texttt{max_state}, and the used registers go up to
+% \cs{l_regex_submatch_int}. They are organized in blocks of
+% \texttt{capturing_group}, each block corresponding to one match
+% with all its submatches stored in consecutive \tn{skip}s.
% \end{itemize}
% \tn{count} registers are not abused, which means that we can safely
-% use named integers in this module.
+% use named integers in this module. Note that \tn{box} registers are
+% not abused either; maybe we could leverage those for some purpose.
+%
+% The code is structured as follows. Variables are introduced in the
+% relevant section. First we present some generic helper functions. Then
+% comes the code for compiling a regular expression, and for showing the
+% result of the compilation. The building phase converts a compiled
+% regex to \textsc{nfa} states, and the automaton is run by the code in
+% the following section. The only remaining brick is parsing the
+% replacement text and performing the replacement. We are then ready for
+% all the user functions. Finally, messages, and a little bit of tracing
+% code.
%
-% The code is structured as follows. Variables, constants and various
-% helper functions are introduced first, to limit the clutter in later
-% parts. Then functions pertaining to parsing the regular expression are
-% defined: that part is rather long because of the many bells and
-% whistles of the regex notation. The next subsection takes care of
-% running the NFA. Thereafter, we give tools for the replacements.
-% Finally, user functions.
-%
-% \subsection{Constants and variables}
+% \subsection{Helpers}
%
-% \begin{macro}{\regex_tmp:w}
-% \begin{variable}
-% {\l_regex_internal_a_int, \l_regex_internal_b_int, \l_regex_internal_c_int}
-% \begin{variable}{\l_regex_internal_a_tl, \l_regex_internal_b_tl}
-% \begin{variable}{\g_regex_internal_tl}
-% Temporary variables used for various purposes.
+% \begin{macro}[aux]{\tl_to_str:V}
+% A variant we need for the |\u| escape in the replacement text.
% \begin{macrocode}
-\cs_new:Npn \regex_tmp:w { }
-\tl_new:N \l_regex_internal_a_tl
-\tl_new:N \l_regex_internal_b_tl
-\int_new:N \l_regex_internal_a_int
-\int_new:N \l_regex_internal_b_int
-\int_new:N \l_regex_internal_c_int
-\tl_new:N \g_regex_internal_tl
+\cs_generate_variant:Nn \tl_to_str:n { V }
% \end{macrocode}
-% \end{variable}
-% \end{variable}
-% \end{variable}
% \end{macro}
%
-% \subsubsection{Character properties}
+% \subsubsection{Constants and variables}
%
-% \begin{variable}
-% {
-% \c_regex_d_tl, \c_regex_D_tl, \c_regex_h_tl, \c_regex_H_tl,
-% \c_regex_s_tl, \c_regex_S_tl, \c_regex_v_tl, \c_regex_V_tl,
-% \c_regex_w_tl, \c_regex_W_tl, \c_regex_N_tl
-% }
-%
-% These constant token lists encode which characters are recognized by
-% |\d|, |\D|, |\w|, \emph{etc.} in regular expressions. Namely,
-% |\d=[0-9]|, |\w=[0-9A-Z_a-z]|, \verb*+\s=[\ \^^I\^^J\^^L\^^M]+,
-% \verb*+\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 tests appear is optimized for usual
-% mostly lower case letter text.
-% \begin{macrocode}
-\tl_const:Nn \c_regex_d_tl
- { \regex_item_range:nn \c_forty_eight { 57 } } % 0--9
-\tl_const:Nn \c_regex_h_tl
- {
- \regex_item_equal:n \c_thirty_two % space
- \regex_item_equal:n \c_nine % tab
- }
-\tl_const:Nn \c_regex_s_tl
- {
- \regex_item_equal:n \c_thirty_two % space
- \regex_item_equal:n \c_nine % tab
- \regex_item_equal:n \c_ten % lf
- \regex_item_equal:n \c_twelve % ff
- \regex_item_equal:n \c_thirteen % cr
- }
-\tl_const:Nn \c_regex_v_tl
- { \regex_item_range:nn \c_ten \c_thirteen } % lf, vtab, ff, cr
-\tl_const:Nn \c_regex_w_tl
- {
- \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_D_tl
- { \c_regex_d_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_H_tl
- { \c_regex_h_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_S_tl
- { \c_regex_s_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_V_tl
- { \c_regex_v_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_W_tl
- { \c_regex_w_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_N_tl
- {
- \regex_item_equal:n \c_ten
- \regex_break_point:TF { } { \regex_break_true:w }
- }
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\c_regex_._tl}
-% The dot meta-character matches any character, except the end marker,
-% whose character code is $-2$.
-% \begin{macrocode}
-\tl_const:cn { c_regex_._tl }
- {
- \if_num:w \l_regex_current_char_int > - \c_two
- \exp_after:wN \regex_break_true:w
- \fi:
- }
-% \end{macrocode}
-% \end{variable}
-%
-% \subsubsection{Variables used while building}
-%
-% \begin{variable}{\l_regex_build_mode_int}
-% While building, ten modes are recognized, labelled $-63$, $-23$,
-% $-6$, $-2$, $0$, $2$, $3$, $6$, $23$, $63$. See
-% section~\ref{sec:regex-modes}.
+% \begin{macro}[aux]{\regex_tmp:w}
+% Temporary function used for various short-term purposes.
% \begin{macrocode}
-\int_new:N \l_regex_build_mode_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_max_state_int}
-% \begin{variable}{\l_regex_left_state_int, \l_regex_right_state_int}
-% The last state that was allocated is $\cs{l_regex_max_state_int}-1$,
-% so that \cs{l_regex_max_state_int} always points to a free state.
-% The starting state and end state of the last group (which any
-% quantifier would act on) are stored as
-% \cs{l_regex_left/right_state_int}. In almost all cases, the left
-% and right pointers only differ by~$1$.
-% \begin{macrocode}
-\int_new:N \l_regex_max_state_int
-\int_new:N \l_regex_left_state_int
-\int_new:N \l_regex_right_state_int
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_left_state_seq, \l_regex_right_state_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.
-% \begin{macrocode}
-\seq_new:N \l_regex_left_state_seq
-\seq_new:N \l_regex_right_state_seq
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_end_group_seq}
-% \begin{variable}{\l_regex_end_alternation_seq}
-% These sequences hold actions to be performed at the end of a group,
-% and at the end of each branch of the alternation, respectively.
-% Currently, \cs{l_regex_end_group_seq} is used to keep track of
-% letter case, and \cs{l_regex_end_alternation_seq} is used
-% for \verb"(?|...)" groups.
-% \begin{macrocode}
-\seq_new:N \l_regex_end_group_seq
-\seq_new:N \l_regex_end_alternation_seq
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_capturing_group_int}
-% \begin{variable}{\l_regex_capturing_group_seq}
-% \begin{variable}{\l_regex_capturing_group_max_int}
-% \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 \texttt{max_int} variable is used in the case of the special
-% groups \verb+(?|...|...)+, which reset the capturing group number in
-% each alternative: at the end of the group, we must use a number
-% larger than every capturing group appearing in any alternative.
-% \begin{macrocode}
-\int_new:N \l_regex_capturing_group_int
-\seq_new:N \l_regex_capturing_group_seq
-\int_new:N \l_regex_capturing_group_max_int
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_one_or_group_tl}
-% When looking for quantifiers, this variable holds either
-% \enquote{one} or \enquote{group} depending on whether the
-% object to which the quantifier applies matches one character
-% (\emph{i.e.}, is a character or character class), or is a group.
-% \begin{macrocode}
-\tl_new:N \l_regex_one_or_group_tl
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_catcodes_int, \l_regex_catcodes_default_int}
-% \begin{variable}
-% {
-% \c_regex_catcode_C_int, \c_regex_catcode_B_int, \c_regex_catcode_E_int,
-% \c_regex_catcode_M_int, \c_regex_catcode_T_int, \c_regex_catcode_P_int,
-% \c_regex_catcode_U_int, \c_regex_catcode_D_int, \c_regex_catcode_S_int,
-% \c_regex_catcode_L_int, \c_regex_catcode_O_int, \c_regex_catcode_A_int
-% }
-% \begin{variable}{\c_regex_catcodes_all_int}
-% We wish to allow constructions such as |\c[^BE](..\cL[a-z]..)|,
-% matching two tokens which are neither a begin-group nor an end-group
-% token, followed by a token of category letter and character code in
-% |[a-z]|, followed by two more tokens which are neither begin-group
-% nor end-group tokens. For this to work, we need to keep track of
-% lists of allowed category codes: \cs{l_regex_catcodes_int} and
-% \cs{l_regex_catcodes_default_int} are bitmaps, sums of $4^c$, for
-% all allowed catcodes $c$. The latter is local to each capturing
-% group, and we reset \cs{l_regex_catcodes_int} to that value after
-% each character or class, changing it only when encountering a |\c|
-% escape. Errata: the \cs{l_regex_catcodes_default_int} is not used
-% yet, because the idea of having \verb+\cL(...|...)+ act on the whole
-% contents of the group is not yet implemented.
-% \begin{macrocode}
-\int_new:N \l_regex_catcodes_int
-\int_new:N \l_regex_catcodes_default_int
-\int_const:Nn \c_regex_catcode_C_int { "1 }
-\int_const:Nn \c_regex_catcode_B_int { "4 }
-\int_const:Nn \c_regex_catcode_E_int { "10 }
-\int_const:Nn \c_regex_catcode_M_int { "40 }
-\int_const:Nn \c_regex_catcode_T_int { "100 }
-\int_const:Nn \c_regex_catcode_P_int { "1000 }
-\int_const:Nn \c_regex_catcode_U_int { "4000 }
-\int_const:Nn \c_regex_catcode_D_int { "10000 }
-\int_const:Nn \c_regex_catcode_S_int { "100000 }
-\int_const:Nn \c_regex_catcode_L_int { "400000 }
-\int_const:Nn \c_regex_catcode_O_int { "1000000 }
-\int_const:Nn \c_regex_catcode_A_int { "4000000 }
-\int_const:Nn \c_regex_catcodes_all_int { "5515155 }
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_catcodes_bool}
-% Controls whether the bitmap of category codes built should be
-% inverted or not.
-% \begin{macrocode}
-\bool_new:N \l_regex_catcodes_bool
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_tests_bool,\l_regex_tests_tl}
-% The tests which should be performed on an item of the token list are
-% stored in \cs{l_regex_tests_tl}. In the case of character classes,
-% \cs{l_regex_tests_bool} is \texttt{false} for negative character
-% classes. In the other case, \cs{l_regex_tests_tl} is directly
-% converted to a state of the NFA. The two variables are cleared
-% (boolean set to \texttt{true}) after looking for a quantifier for
-% the class or single character or property. It would seem cleaner to
-% clear them instead before grabbing the next character or class, but
-% that doesn't interact correctly with constructions like |\cL[...]|
-% where |\cL| inserts some tests in \cs{l_regex_tests_tl}, which
-% should not be eliminated before finding the class.
-% \begin{macrocode}
-\bool_new:N \l_regex_tests_bool
-\tl_new:N \l_regex_tests_tl
+\cs_new:Npn \regex_tmp:w { }
% \end{macrocode}
-% \end{variable}
+% \end{macro}
%
% \begin{variable}
% {
-% \l_regex_tests_saved_bool,
-% \l_regex_tests_saved_tl,
-% \l_regex_catcodes_saved_int
+% \l_regex_internal_a_tl, \l_regex_internal_b_tl,
+% \l_regex_internal_a_int, \l_regex_internal_b_int,
+% \l_regex_internal_c_int, \l_regex_internal_bool,
+% \l_regex_internal_seq, \g_regex_internal_tl,
% }
-% In nested class, which can only occur as |[\cA[...]]|, the variables
-% \cs{l_regex_tests_bool}, \cs{l_regex_tests_tl}, and
-% \cs{l_regex_catcodes_int} from the outer class must be saved while
-% processing the inner class.
-% \begin{macrocode}
-\bool_new:N \l_regex_tests_saved_bool
-\tl_new:N \l_regex_tests_saved_tl
-\int_new:N \l_regex_catcodes_saved_int
-% \end{macrocode}
-% \end{variable}
-%
-% \subsubsection{Variables used when matching}
-%
-% \begin{variable}{\l_regex_min_index_int}
-% \begin{variable}{\l_regex_max_index_int}
-% The tokens in the query are indexed from \cs{l_regex_min_index_int}
-% for the first to $\cs{l_regex_max_index_int}-1$ for the last, and
-% their information is stored in \tn{muskip} and \tn{toks} registers
-% with those numbers. We don't start from $0$ because the \tn{toks}
-% registers with low numbers are used to hold the states of the NFA.
-% \begin{macrocode}
-\int_new:N \l_regex_min_index_int
-\int_new:N \l_regex_max_index_int
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_nesting_int}
-% The first phase when matching is to go once through the query token
-% list and store the information for each token as \tn{muskip} and
-% \tn{toks} registers. During this phase, \cs{l_regex_nesting_int}
-% counts the balance of begin-group and end-group character tokens
-% which appear before a given point in the string, and is stored as
-% the shrink component of the \tn{muskip} registers. This integer is
-% also used to keep track of the balance in the replacement text.
-% \begin{macrocode}
-\int_new:N \l_regex_nesting_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_current_index_int}
-% \begin{variable}{\l_regex_start_index_int}
-% \begin{variable}{\l_regex_success_index_int}
-% While reading through the query token list,
-% \cs{l_regex_current_index_int} is the position in the token list,
-% starting at \cs{l_regex_min_index_int} for the first token.
-% Each match begins at the position given by
-% \cs{l_regex_start_index_int}. Whenever an execution thread succeeds,
-% the corresponding index is stored into \cs{l_regex_success_index_int},
-% which will be the next starting index (except in the case of empty
-% matches).
-% \begin{macrocode}
-\int_new:N \l_regex_current_index_int
-\int_new:N \l_regex_start_index_int
-\int_new:N \l_regex_success_index_int
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_current_char_int}
-% \begin{variable}{\l_regex_current_catcode_int}
-% \begin{variable}{\l_regex_current_token_tl}
-% \begin{variable}{\l_regex_last_char_int}
-% \begin{variable}{\l_regex_case_changed_char_int}
-% The character and category codes of the token at the current
-% position; \meta{tokens} which \texttt{o}- and \texttt{x}-expand to
-% the token (as provided by \cs{tl_analysis:n}); the character code of
-% the token at the previous position; and the character code of the
-% result of changing the case of the current token
-% (|A-Z|$\leftrightarrow$|a-z|). This last integer is only computed if
-% the \enquote{case insensitive} option |(?i)| is used in the regex.
-% The \cs{l_regex_current_char_int} variable is also used in various
-% other phases to hold a character code.
-% \begin{macrocode}
-\int_new:N \l_regex_current_char_int
-\int_new:N \l_regex_current_catcode_int
-\tl_new:N \l_regex_current_token_tl
-\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}
-% \end{variable}
-% \end{variable}
-%
-% \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}
-\bool_new:N \l_regex_caseless_bool
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_current_state_int}
-% For every character in the token list, 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: transitions are then
-% given as shifts relative to the current state.
-% In some cases 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_step_int}
-% In the case of repeated matches, \cs{l_regex_current_index_int}
-% is reset to the end-position of the previous match. In contrast,
-% \cs{l_regex_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_step_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_max_active_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
-% after treating the previous character is \cs{l_regex_max_active_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_active_int} is
-% reset to zero, effectively clearing the array.
-% \begin{macrocode}
-\int_new:N \l_regex_max_active_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_every_match_tl}
-% Every time a match is found, this token list is used. For single
-% matching, the token list is empty. For multiple matching, the token
-% list is set to repeat the matching.
-% \begin{macrocode}
-\tl_new:N \l_regex_every_match_tl
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_fresh_thread_bool}
-% \begin{variable}{\l_regex_success_empty_bool}
-% \begin{macro}{\regex_if_two_empty_matches:F}
-% When doing multiple matches, we need to avoid infinite loops where
-% each iteration matches the same empty token list. When an empty
-% token list is matched, the next successful match of the same empty
-% token list is suppressed. We detect empty matches by setting
-% \cs{l_regex_fresh_thread_bool} to \texttt{true} for threads which
-% directly come from the start of the regular expression, and testing
-% that boolean whenever a thread succeeds. The function
-% \cs{regex_if_two_empty_matches:F} is redefined at every match
-% attempt, depending on whether the previous match was empty or not:
-% if it was, then the function must cancel a purported success if it
-% is empty and at the same spot as the previous match; otherwise, we
-% definitely don't have two identical empty matches, so the function
-% is \cs{use:n}.
-% \begin{macrocode}
-\bool_new:N \l_regex_fresh_thread_bool
-\bool_new:N \l_regex_success_empty_bool
-\cs_new_eq:NN \regex_if_two_empty_matches:F \use:n
-% \end{macrocode}
-% \end{macro}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\g_regex_success_bool}
-% \begin{variable}{\l_regex_saved_success_bool}
-% \begin{variable}{\l_regex_success_match_bool}
-% The boolean \cs{g_regex_success_bool} is true if there was at least
-% one successful match, and \cs{l_regex_success_match_bool} is true if
-% the current match attempt was successful. The variable
-% \cs{g_regex_success_bool} is the only global variable in this whole
-% module. When nesting \cs{regex} functions internally, the value of
-% \cs{g_regex_success_bool} is saved into
-% \cs{l_regex_saved_success_bool}, which is local, hence not affected
-% by the changes due to inner regex functions.
+% Temporary variables used for various purposes.
% \begin{macrocode}
-\bool_new:N \g_regex_success_bool
-\bool_new:N \l_regex_saved_success_bool
-\bool_new:N \l_regex_success_match_bool
+\tl_new:N \l_regex_internal_a_tl
+\tl_new:N \l_regex_internal_b_tl
+\int_new:N \l_regex_internal_a_int
+\int_new:N \l_regex_internal_b_int
+\int_new:N \l_regex_internal_c_int
+\bool_new:N \l_regex_internal_bool
+\seq_new:N \l_regex_internal_seq
+\tl_new:N \g_regex_internal_tl
% \end{macrocode}
% \end{variable}
-% \end{variable}
-% \end{variable}
-%
-% \subsubsection{Regular expression variables}
%
% \begin{variable}{\c_regex_no_match_regex}
% This regular expression matches nothing, but is still a valid
-% regular expression. It is used as the initial value for regular
+% regular expression. We could use a failing assertion, but I went for
+% an empty class. It is used as the initial value for regular
% expressions declared using \cs{regex_new:N}.
% \begin{macrocode}
\tl_const:Nn \c_regex_no_match_regex
{
- \regex_nfa:Nw \c_regex_no_match_regex
- \l_regex_max_state_int = \c_one
- \l_regex_capturing_group_int = \c_zero
- \tex_toks:D \c_zero { \s_stop }
- \s_stop
- }
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{macro}{\regex_new:N}
-% As for many data types deriving from the \texttt{tl} data type,
-% creating a new one is simply a matter of checking that it does not
-% exist yet, and setting it equal to a default value.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_new:N #1
- {
- \chk_if_free_cs:N #1
- \cs_gset_eq:NN #1 \c_regex_no_match_regex
+ \regex_branch:n
+ { \regex_class:NnnnN \c_true_bool { } { 1 } { 0 } \c_true_bool }
}
% \end{macrocode}
-% \end{macro}
-%
-% \begin{variable}{\l_regex_internal_regex}
-% This holds a temporary pre-compiled regular expression
-% when matching a control sequence name.
-% \begin{macrocode}
-\regex_new:N \l_regex_internal_regex
-% \end{macrocode}
-% \end{variable}
-%
-% \subsubsection{Other variables}
-%^^A todo: move?
-%^^A todo: run with the check-declarations option.
-%
-% \begin{variable}{\l_regex_replacement_csnames_int}
-% The behaviour of closing braces inside a replacement text depends on
-% whether a sequences |\c{| or |\u{| has been encountered. The number
-% of \enquote{open} such sequences that should be closed by |}| is
-% stored in \cs{l_regex_replacement_csnames_int}, and decreased by
-% $1$ by each |}|.
-% \begin{macrocode}
-\int_new:N \l_regex_replacement_csnames_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_replacement_int}
-% \begin{macrocode}
-\int_new:N \l_regex_replacement_int
-% \end{macrocode}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_submatch_int}
-% \begin{variable}{\l_regex_submatch_start_int}
-% \begin{macrocode}
-\int_new:N \l_regex_submatch_int
-\int_new:N \l_regex_submatch_start_int
-% \end{macrocode}
-% \end{variable}
-% \end{variable}
-%
-% \begin{variable}{\l_regex_match_count_int}
-% The number of matches found so far is stored
-% in \cs{l_regex_match_count_int}. This is only used
-% in the \cs{regex_count:nnN} functions.
-% \begin{macrocode}
-\int_new:N \l_regex_match_count_int
-% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{regex_begin, regex_end}
-% Those flags are raised to indicate extra begin-group
-% or end-group tokens when extracting submatches.
+% \begin{variable}{\l_regex_balance_int}
+% The first thing we do when matching is to go once through the query
+% token list and store the information for each token as \tn{muskip}
+% and \tn{toks} registers. During this phase, \cs{l_regex_balance_int}
+% counts the balance of begin-group and end-group character tokens
+% which appear before a given point in the token list, and we store it
+% as the shrink component of each \tn{muskip} register. This variable
+% is also used to keep track of the balance in the replacement text.
% \begin{macrocode}
-\flag_new:n { regex_begin }
-\flag_new:n { regex_end }
+\int_new:N \l_regex_balance_int
% \end{macrocode}
% \end{variable}
%
-% \subsection{Helpers}
-%
-% \subsubsection{Toks}
-%
-% Two unrelated sets of functions for manipulating \tn{toks} registers.
-%
-% \begin{macro}[int]{\regex_toks_put_left:Nx}
-% \begin{macro}[int]{\regex_toks_put_right:Nx}
-% During the building phase, every \tn{toks} register starts with
-% \cs{s_stop}, and we wish to add \texttt{x}-expanded material to
-% those registers. The expansion is done \enquote{by hand} for
-% optimization (these operations are used quite a lot). When adding
-% material to the left, we define \cs{regex_tmp:w} to remove the
-% \cs{s_stop} marker and put it back to the left of the new material.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2
- {
- \cs_set_nopar:Npx \regex_tmp:w \s_stop { \s_stop #2 }
- \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
- { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 }
- }
-\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2
- {
- \cs_set_nopar:Npx \regex_tmp:w {#2}
- \tex_toks:D #1 \exp_after:wN
- { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w }
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
-% \begin{macro}[int, rEXP]{\regex_toks_range:nn}
-% \begin{macro}[aux, rEXP]{\regex_toks_range:ww}
-% Some non-expandable functions store pieces of their result in
-% \tn{toks} registers. Those pieces are concatenated using
-% \cs{regex_toks_range:nn}, which expects two integers, \meta{start}
-% and \meta{end}, and expands to the contents of the \tn{toks}
-% registers from \meta{start} (inclusive) to \meta{end}
-% (exclusive). This correctly expands to nothing if \meta{start}
-% $\geq$ \meta{end}.
-% \begin{macrocode}
-\cs_new:Npn \regex_toks_range:nn #1#2
- {
- \exp_after:wN \regex_toks_range:ww
- \int_use:N \int_eval:w #1 \exp_after:wN ;
- \int_use:N \int_eval:w #2 ;
- \prg_break_point:n { }
- }
-\cs_new:Npn \regex_toks_range:ww #1 ; #2 ;
- {
- \if_num:w #1 < #2 \exp_stop_f:
- \else:
- \exp_after:wN \prg_map_break:
- \fi:
- \tex_the:D \tex_toks:D #1 \exp_stop_f:
- \exp_after:wN \regex_toks_range:ww
- \int_use:N \int_eval:w #1 + \c_one ; #2 ;
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
-% On the one hand, when performing the matching, the \tn{toks} registers
-% hold submatch information, followed by the instruction for a given
-% state of the NFA. The two parts are separated by \cs{s_stop}. On the
-% other hand, we provide functions to unpack the contents from a range
-% of \tn{toks} within an \texttt{x}-expanding assignment.
-%
-% When it is time to extract submatches from the token list, the various
-% tokens are stored in \tn{toks} registers numbered from
-% \cs{l_regex_min_index_int} inclusive to \cs{l_regex_max_index_int}
-% exclusive. Furthermore, the \tn{skip} registers hold\ldots{} ^^A ??
-%
-% \begin{macro}[int]{\regex_query_submatch:nn}
-% \begin{macro}[int]{\regex_query_submatch:w}
-%^^A ??
-% \begin{macrocode}
-\cs_new:Npn \regex_query_submatch:nn #1#2
- {
- \if_num:w #1 < \l_regex_capturing_group_int
- \exp_after:wN \regex_query_submatch:w
- \int_use:N \int_eval:w #1 + #2 ;
- \fi:
- }
-\cs_new:Npn \regex_query_submatch:w #1 ;
- {
- \regex_toks_range:nn
- { \tex_skip:D #1 }
- { \etex_gluestretch:D \tex_skip:D #1 }
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Sequences}
-%
-% \begin{macro}[int]{\regex_seq_pop_int:NN}
-% \begin{macro}[int]{\regex_seq_get_int:NN}
-% \begin{macro}[int]{\regex_seq_push_int:NN}
-% When building the regular expression, we keep track of some integers
-% (pointers to various states) without help from \TeX{}'s grouping.
-% Here are variants of \cs{seq_pop:NN} and \cs{seq_get:NN} which
-% assign using \cs{int_set:Nn} rather than \cs{tl_set:Nn}.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_seq_pop_int:NN #1#2
- {
- \seq_pop:NN #1 \l_regex_internal_a_tl
- \int_set:Nn #2 \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_get_int:NN #1#2
- {
- \seq_get:NN #1 \l_regex_internal_a_tl
- \int_set:Nn #2 \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_push_int:NN #1#2
- { \seq_push:No #1 { \int_use:N #2 } }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \begin{macro}[int]{\regex_seq_pop_use:N}
-% \begin{macro}[int]{\regex_seq_get_use: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 should
-% be run immediately.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_seq_pop_use:N #1
- {
- \seq_pop:NN #1 \l_regex_internal_a_tl
- \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_get_use:N #1
- {
- \seq_get:NN #1 \l_regex_internal_a_tl
- \l_regex_internal_a_tl
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Grabbing digits}
-%
-% \begin{macro}[int]{\regex_get_digits:nw}
-% \begin{macro}[aux]{\regex_get_digits_loop:N,\regex_get_digits_end:w}
-% When parsing the \texttt{\{} quantifier, we need a tool to grab
-% digits until reaching the first non-digit. This
-% \cs{regex_get_digits:nw} function places whatever digits it found as
-% a brace group after |#1|.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_get_digits:nw #1
- {
- \tex_afterassignment:D \regex_tmp:w
- \cs_set_nopar:Npx \regex_tmp:w
- {
- \exp_not:n {#1}
- { \if_false: } } \fi:
- \regex_get_digits_aux:NN
- }
-\cs_new:Npn \regex_get_digits_aux:NN #1#2
- {
- \if_meaning:w \regex_build_raw:N #1
- \if_num:w 9 < 1 \exp_not:N #2 \exp_stop_f:
- #2
- \else:
- \regex_get_digits_end:w #1 #2
- \fi:
- \else:
- \regex_get_digits_end:w #1 #2
- \fi:
- \regex_get_digits_aux:NN
- }
-\cs_new:Npn \regex_get_digits_end:w #1 \fi: #2 \regex_get_digits_aux:NN
- {
- \fi: #2
- \if_false: { { \fi: } }
- #1
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
% \subsubsection{Testing characters}
%
% \begin{macro}[int]{\regex_break_point:TF}
@@ -1458,9 +898,23 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[int]{\regex_item_caseful_equal:n}
-% \begin{macro}[int]{\regex_item_caseful_range:nn}
-% \begin{macro}[int]{\regex_item_caseful_geq:n}
+% \begin{macro}[int]{\regex_item_reverse:n}
+% This function makes showing regular expressions easier, and lets us
+% define |\D| in terms of |\d| for instance. There is a subtelty: the
+% end of the query is marked by $-2$, and will thus match |\D| and
+% other negated properties; this case is catched by another part of
+% the code.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_item_reverse:n #1
+ {
+ #1
+ \regex_break_point:TF { } \regex_break_true:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]
+% {\regex_item_caseful_equal:n, \regex_item_caseful_range:nn}
% Simple comparisons triggering \cs{regex_break_true:w} when true.
% \begin{macrocode}
\cs_new_protected:Npn \regex_item_caseful_equal:n #1
@@ -1477,29 +931,24 @@
\fi:
\fi:
}
-\cs_new_protected:Npn \regex_item_caseful_geq:n #1
- {
- \reverse_if:N \if_num:w #1 > \l_regex_current_char_int
- \exp_after:wN \regex_break_true:w
- \fi:
- }
% \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{macro}[int]
+% {\regex_item_caseless_equal:n, \regex_item_caseless_range:nn}
+% For caseless matching, we perform the test both on the
+% \texttt{current_char} and on the \texttt{case_changed_char}. Before
+% doing the second set of tests, we make sure that
+% \texttt{case_changed_char} has been computed.
% \begin{macrocode}
\cs_new_protected:Npn \regex_item_caseless_equal:n #1
{
\if_num:w #1 = \l_regex_current_char_int
\exp_after:wN \regex_break_true:w
\fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \fi:
\if_num:w #1 = \l_regex_case_changed_char_int
\exp_after:wN \regex_break_true:w
\fi:
@@ -1511,64 +960,57 @@
\exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
\fi:
\fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \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: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 phase, hard-coding which
-% states take care of caseless versus caseful matching.
+% \begin{macro}[int]{\regex_compute_case_changed_char:}
+% This function is called when \cs{l_regex_case_changed_char_int} has
+% not yet been computed (or rather, when it is set to the marker value
+% \cs{c_max_int}). If the current character code is in the range
+% $[65,90]$ (upper-case), then add $32$, making it lowercase. If it is
+% in the lower-case letter range $[97,122]$, subtract $32$.
% \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 }
+\cs_new_protected_nopar:Npn \regex_compute_case_changed_char:
+ {
+ \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}
-% \end{macro}
%
-% \begin{macro}[int]{\regex_build_caseless:,\regex_build_caseful:}
-% Switch between caseful and caseless matching.
-% This is only done during the building phase.
+% \begin{macro}[int, EXP]{\regex_item_equal:n, \regex_item_range:nn}
+% Those must always be defined to expand to a \texttt{caseful}
+% (default) or \texttt{caseless} version, and not be protected: they
+% must expand when compiling, to hard-code which tests are caseless or
+% caseful.
% \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 }
- }
+\cs_new_eq:NN \regex_item_equal:n ?
+\cs_new_eq:NN \regex_item_range:nn ?
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_item_catcode:nT}
+% \begin{macro}[int]{\regex_item_catcode:nT, \regex_item_catcode_reverse:nT}
% \begin{macro}[aux]{\regex_item_catcode_aux:}
% The argument is a sum of powers of $4$ with exponents given by the
% allowed category codes (between $0$ and $13$). Dividing by a given
@@ -1594,39 +1036,172 @@
\exp_after:wN \use_none:n
\fi:
}
+\cs_new_protected:Npn \regex_item_catcode_reverse:nT #1#2
+ { \regex_item_catcode:nT {#1} { \regex_item_reverse:n {#2} } }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
+% \begin{macro}[int]{\regex_item_exact:nn, \regex_item_exact_cs:c}
+% This matches an exact \meta{category}-\meta{character code} pair, or
+% an exact control sequence.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_item_exact:nn #1#2
+ {
+ \if_num:w #1 = \l_regex_current_catcode_int
+ \if_num:w #2 = \l_regex_current_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_exact_cs:c #1
+ {
+ \int_compare:nNnTF \l_regex_current_catcode_int = \c_zero
+ {
+ \str_if_eq:xxTF
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
+ }
+ { #1 }
+ { \regex_break_true:w } { }
+ }
+ { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}[int]{\regex_item_cs:n}
-% Match a control sequence (the argument is a pre-compiled regex).
+% Match a control sequence (the argument is a compiled regex).
% First test the catcode of the current token to be zero.
% Then perform the matching test, and break if the csname
% indeed matches. The three \cs{exp_after:wN} expand the contents
-% of \cs{l_regex_current_token_tl} (of the form \cs{exp_not:n}
+% of the \tn{toks}\meta{current position} (of the form \cs{exp_not:n}
% \Arg{control sequence}) to \meta{control sequence}.
% \begin{macrocode}
\cs_new_protected:Npn \regex_item_cs:n #1
{
\int_compare:nNnT \l_regex_current_catcode_int = \c_zero
{
- \tl_set:Nn \l_regex_internal_regex {#1}
- \bool_set_eq:NN \l_regex_saved_success_bool \g_regex_success_bool
- \exp_args:NNx \regex_match:NnTF \l_regex_internal_regex
- {
- \exp_after:wN \exp_after:wN
- \exp_after:wN \cs_to_str:N \l_regex_current_token_tl
- }
- {
- \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool
- \regex_break_true:w
- }
- { \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool }
+ \group_begin:
+ \regex_single_match:
+ \regex_disable_submatches:
+ \regex_build_for_cs:n {#1}
+ \bool_set_eq:NN \l_regex_saved_success_bool \g_regex_success_bool
+ \exp_args:Nx \regex_match:n
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
+ }
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \group_insert_after:N \regex_break_true:w
+ \fi:
+ \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool
+ \group_end:
}
}
% \end{macrocode}
% \end{macro}
%
+% \subsubsection{Character property tests}
+%
+% \begin{macro}[aux]
+% {
+% \regex_prop_d:, \regex_prop_h:, \regex_prop_s:,
+% \regex_prop_v:, \regex_prop_w:, \regex_prop_N:
+% }
+% Character property tests for |\d|, |\W|, \emph{etc.} These character
+% properties are not affected by the |(?i)| option. The characters
+% recognized by each one are as follows: |\d=[0-9]|,
+% |\w=[0-9A-Z_a-z]|, \verb*+\s=[\ \^^I\^^J\^^L\^^M]+,
+% \verb*+\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 tests appear is optimized for usual
+% mostly lower case letter text.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_prop_d:
+ { \regex_item_caseful_range:nn \c_forty_eight { 57 } } % 0--9
+\cs_new_protected_nopar:Npn \regex_prop_h:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ }
+\cs_new_protected_nopar:Npn \regex_prop_s:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ \regex_item_caseful_equal:n \c_ten % lf
+ \regex_item_caseful_equal:n \c_twelve % ff
+ \regex_item_caseful_equal:n \c_thirteen % cr
+ }
+\cs_new_protected_nopar:Npn \regex_prop_v:
+ { \regex_item_caseful_range:nn \c_ten \c_thirteen } % lf, vtab, ff, cr
+\cs_new_protected_nopar:Npn \regex_prop_w:
+ {
+ \regex_item_caseful_range:nn \c_ninety_seven { 122 } % a--z
+ \regex_item_caseful_range:nn \c_sixty_five { 90 } % A--Z
+ \regex_item_caseful_range:nn \c_forty_eight { 57 } % 0--9
+ \regex_item_caseful_equal:n { 95 } % _
+ }
+\cs_new_protected_nopar:Npn \regex_prop_N:
+ { \regex_item_reverse:n { \regex_item_caseful_equal:n \c_ten } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]
+% {
+% \regex_posix_alnum:, \regex_posix_alpha:, \regex_posix_ascii:,
+% \regex_posix_blank:, \regex_posix_cntrl:, \regex_posix_digit:,
+% \regex_posix_graph:, \regex_posix_lower:, \regex_posix_print:,
+% \regex_posix_punct:, \regex_posix_space:, \regex_posix_upper:,
+% \regex_posix_word: , \regex_posix_xdigit:
+% }
+% \textsc{posix} properties. No surprise.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_posix_alnum:
+ { \regex_posix_alpha: \regex_posix_digit: }
+\cs_new_protected_nopar:Npn \regex_posix_alpha:
+ { \regex_posix_lower: \regex_posix_upper: }
+\cs_new_protected_nopar:Npn \regex_posix_ascii:
+ { \regex_item_caseful_range:nn \c_zero \c_one_hundred_twenty_seven }
+\cs_new_eq:NN \regex_posix_blank: \regex_prop_h:
+\cs_new_protected_nopar:Npn \regex_posix_cntrl:
+ {
+ \regex_item_caseful_range:nn \c_zero { 31 }
+ \regex_item_caseful_equal:n \c_one_hundred_twenty_seven
+ }
+\cs_new_eq:NN \regex_posix_digit: \regex_prop_d:
+\cs_new_protected_nopar:Npn \regex_posix_graph:
+ { \regex_item_caseful_range:nn { 33 } { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_lower:
+ { \regex_item_caseful_range:nn \c_ninety_seven { 122 } }
+\cs_new_protected_nopar:Npn \regex_posix_print:
+ { \regex_item_caseful_range:nn \c_thirty_two { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_punct:
+ {
+ \regex_item_caseful_range:nn { 33 } { 47 }
+ \regex_item_caseful_range:nn { 58 } { 64 }
+ \regex_item_caseful_range:nn { 91 } { 96 }
+ \regex_item_caseful_range:nn { 123 } { 126 }
+ }
+\cs_new_protected_nopar:Npn \regex_posix_space:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two
+ \regex_item_caseful_range:nn \c_nine \c_thirteen
+ }
+\cs_new_protected_nopar:Npn \regex_posix_upper:
+ { \regex_item_caseful_range:nn \c_sixty_five { 90 } }
+\cs_new_eq:NN \regex_posix_word: \regex_prop_w:
+\cs_new_protected_nopar:Npn \regex_posix_xdigit:
+ {
+ \regex_posix_digit:
+ \regex_item_caseful_range:nn \c_sixty_five { 70 }
+ \regex_item_caseful_range:nn \c_ninety_seven { 102 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \subsubsection{Simple character escape}
%
% Before actually parsing the regular expression or the replacement
@@ -1644,53 +1219,56 @@
% \meta{inline~3} \Arg{token list} The \meta{token list} is converted to
% a string, then read from left to right, interpreting backslashes as
% escaping the next character. Unescaped characters are fed to the
-% function \meta{fn1}, and escaped characters are fed to the function
-% \meta{fn2} within an \texttt{x}-expansion context (typically those
+% function \meta{inline~1}, and escaped characters are fed to the function
+% \meta{inline~2} within an \texttt{x}-expansion context (typically those
% functions perform some tests on their argument to decide how to output
% them). The escape sequences |\a|, |\e|, |\f|, |\n|, |\r|, |\t| and
% |\x| are recognized, and those are replaced by the corresponding
-% character, then fed to \meta{fn3}. The result is then left in the
-% input stream.
+% character, then fed to \meta{inline~3}. The result is then left in the
+% input stream. Spaces are ignored unless escaped.
%
-% 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 appropriate
-% character to a third function. Spaces are ignored unless
-% escaped.
+% The conversion is mostly done within an \texttt{x}-expanding
+% assignment, except for the |\x| escape sequence, which is not amenable
+% to that in general. For this, we use the general framework of
+% \cs{tl_set_build:Nw}.
%
% \begin{macro}[int]{\regex_escape_use:nnnn}
-% \begin{macro}[aux]{\regex_escape_loop:N}
-% \begin{macro}[aux]+\regex_escape_\:w+
-% Go through |#4| once, applying |#1|, |#2|, or |#3| to each character
-% (after de-escaping it), then leave the result in the input
-% stream. Most of the work is done within an \texttt{x}-expanding
-% assignment, but the |\x| escape sequence cannot be done in that
-% way. Therefore, we interrupt the assignment at each |\x| escape
-% sequence, store the partial result in a \tn{toks} register, and at
-% the end unpack all of the \tn{toks} registers to the left of the
-% last chunk of \cs{l_regex_internal_a_tl}.
+% The result is built in \cs{l_regex_internal_a_tl}, which is then
+% left in the input stream. Go through |#4| once, applying |#1|,
+% |#2|, or |#3| as relevant to each character (after de-escaping
+% it). Note that we cannot replace \cs{tl_se:Nx} and
+% \cs{tl_build_one:o} by a single call to \cs{tl_build_one:x}, because
+% the \texttt{x}-expanding assignment is interrupted by |\x|.
% \begin{macrocode}
\cs_new_protected:Npn \regex_escape_use:nnnn #1#2#3#4
{
- \group_begin:
+%<trace> \trace_push:nnn { regex } { 1 } { regex_escape_use:nnnn }
+ \tl_set_build:Nw \l_regex_internal_a_tl
\cs_set_nopar:Npn \regex_escape_unescaped:N ##1 { #1 }
\cs_set_nopar:Npn \regex_escape_escaped:N ##1 { #2 }
\cs_set_nopar:Npn \regex_escape_raw:N ##1 { #3 }
\int_set:Nn \tex_escapechar:D { 92 }
- \str_gset_other:Nn \g_regex_internal_tl {#4}
- \int_zero:N \l_regex_internal_a_int
- \tl_set:Nx \l_regex_internal_a_tl
+ \str_gset_other:Nn \g_regex_internal_tl { #4 }
+ \tl_set:Nx \l_regex_internal_b_tl
{
\exp_after:wN \regex_escape_loop:N \g_regex_internal_tl
{ break } \prg_break_point:n { }
}
- \use:x
- {
- \group_end:
- \regex_toks_range:nn \c_zero \l_regex_internal_a_int
- \exp_not:o \l_regex_internal_a_tl
- }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \tl_build_end:
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_escape_use:nnnn }
+ \l_regex_internal_a_tl
}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_escape_loop:N}
+% \begin{macro}[aux]+\regex_escape_\:w+
+% \cs{regex_escape_loop:N} reads one character: if it is special
+% (space, backslash, or end-marker), perform the associated action,
+% otherwise it is simply an unescaped character. After a backslash,
+% the same is done, but unknown characters are \enquote{escaped}.
+% \begin{macrocode}
\cs_new:Npn \regex_escape_loop:N #1
{
\cs_if_exist_use:cF { regex_escape_\token_to_str:N #1:w }
@@ -1707,11 +1285,9 @@
% \end{macrocode}
% \end{macro}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_escape_unescaped:N}
-% \begin{macro}[aux]{\regex_escape_escaped:N}
-% \begin{macro}[aux]{\regex_escape_raw:N}
+% \begin{macro}[aux]
+% {\regex_escape_unescaped:N, \regex_escape_escaped:N, \regex_escape_raw:N}
% Those functions are never called before being given a new meaning,
% so their definitions here don't matter.
% \begin{macrocode}
@@ -1720,20 +1296,18 @@
\cs_new_eq:NN \regex_escape_raw:N ?
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]+\regex_escape_\q_recursion_tail:w+
-% \begin{macro}[aux]+\regex_escape_/\q_recursion_tail:w+
-% \begin{macro}[aux]+\regex_escape_ :w+
% \begin{macro}[aux]
% {
+% \regex_escape_break:w, \regex_escape_/break:w,
% \regex_escape_/a:w, \regex_escape_/e:w, \regex_escape_/f:w,
% \regex_escape_/n:w, \regex_escape_/r:w, \regex_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.
+% \begin{macro}[aux]+\regex_escape_ :w+
+% The loop is ended upon seeing the end-marker
+% \enquote{\texttt{break}}, with an error if the string ended in a
+% backslash. Spaces are ignored, and |\a|, |\e|, |\f|, |\n|, |\r|,
+% |\t| take their meaning here.
% \begin{macrocode}
\cs_new_eq:NN \regex_escape_break:w \prg_map_break:
\cs_new_nopar:cpn { regex_escape_/break:w }
@@ -1758,126 +1332,149 @@
% \end{macrocode}
% \end{macro}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
% \begin{macro}[aux]{\regex_escape_/x:w}
-% \begin{macro}[aux]{\regex_escape_x_test:N}
-% \begin{macro}[aux]{\regex_escape_x_unbraced_i:N}
-% \begin{macro}[aux]{\regex_escape_x_unbraced_ii:N}
-% \begin{macro}[aux]{\regex_escape_x_braced_loop:N}
-% \begin{macro}[aux]{\regex_escape_x_braced_end:N}
-% \begin{macro}[aux]{\regex_escape_x_end:}
-% When |\x| is encountered, interrupt the assignment,
-% and distinguish the cases of a braced or unbraced syntax.
-% In the braced case, collect arbitrarily many hexadecimal digits,
-% building the number in \cs{l_regex_current_char_int} (using
-% \cs{str_aux_hexadecimal_use:NTF}), and check that
-% the run of digits was stopped by a closing brace.
-% In the unbraced case, collect up to two hexadecimal digits,
-% possibly less, building the character number in \cs{l_regex_current_char_int}.
-% In both cases, once all digits have been collected, use
-% the \TeX{} primitive \tn{lowercase} to produce that character,
-% and use an \cs{if_false:} trick to restart the assignment.
-% \begin{macrocode}
-\cs_new_nopar:cpn { regex_escape_/x:w } \regex_escape_loop:N
+% \begin{macro}[aux]{\regex_escape_x_end:w, \regex_escape_x_large:n}
+% When |\x| is encountered, \cs{regex_escape_x_test:N} is responsible
+% for grabbing some hexadecimal digits, and feeding the result to
+% \cs{regex_escape_x_end:w}. If the number is $<256$, then it is
+% turned into a byte and fed to \cs{regex_escape_raw:N}. Otherwise,
+% interrupt the assignment, and either produce an error, or use a
+% standard \tn{lowercase} trick depending on the precise value.
+% \begin{macrocode}
+\cs_new:cpn { regex_escape_/x:w } \regex_escape_loop:N
{
- \if_false: { \fi: }
- \tex_toks:D \l_regex_internal_a_int
- \exp_after:wN { \l_regex_internal_a_tl }
- \int_incr:N \l_regex_internal_a_int
- \int_zero:N \l_regex_current_char_int
- \regex_escape_x_test:N
+ \exp_after:wN \regex_escape_x_end:w
+ \int_value:w "0 \regex_escape_x_test:N
}
-\cs_new_protected:Npx \regex_escape_x_test:N #1
+\cs_new:Npn \regex_escape_x_end:w #1 ;
{
- \exp_not:N \token_if_eq_charcode:NNTF \c_space_token #1
- { \exp_not:N \regex_escape_x_test:N }
+ \int_compare:nNnTF {#1} < \c_two_hundred_fifty_six
{
- \l_regex_current_char_int = "0
- \exp_not:N \token_if_eq_charcode:NNTF \c_lbrace_str #1
- { \exp_not:N \regex_escape_x_braced_loop:N }
- { \exp_not:N \regex_escape_x_unbraced_i:N #1 }
+ \exp_last_unbraced:Nf \regex_escape_raw:N
+ { \str_output_byte:n {#1} }
}
+ { \regex_escape_x_large:n {#1} }
}
-\cs_new:Npn \regex_escape_x_unbraced_i:N #1
- {
- \str_aux_hexadecimal_use:NTF #1
- { \regex_escape_x_unbraced_ii:N }
- { \exp_stop_f: \regex_escape_x_end: #1 }
- }
-\cs_new:Npn \regex_escape_x_unbraced_ii:N #1
+\group_begin:
+ \char_set_catcode_other:n { 0 }
+ \cs_new:Npn \regex_escape_x_large:n #1
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \int_compare:nNnTF {#1} > \c_max_char_int
+ {
+ \msg_kernel_error:nnx { regex } { x-overflow } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N
+ }
+ {
+ \char_set_lccode:nn { \c_zero } {#1}
+ \tl_to_lowercase:n
+ {
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi:
+ \regex_escape_raw:N ^^@
+ \regex_escape_loop:N
+ }
+ }
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_escape_x_test:N}
+% Find out whether the first character is a left brace (allowing any
+% number of hexadecimal digits), or not (allowing up to two
+% hexadecimal digits). We need to check for the end-of-string marker.
+% Eventually, call either \cs{regex_escape_x_loop:N} or
+% \cs{regex_escape_x_ii:N}.
+% \begin{macrocode}
+\cs_new:Npn \regex_escape_x_test:N #1
{
- \token_if_eq_charcode:NNTF \c_space_token #1
- { \regex_escape_x_unbraced_ii:N }
+ \str_if_eq:xxTF {#1} { break } { ; }
{
- \str_aux_hexadecimal_use:NTF #1
- { \exp_stop_f: \regex_escape_x_end: }
- { \exp_stop_f: \regex_escape_x_end: #1 }
+ \if_charcode:w \c_space_token #1
+ \exp_after:wN \regex_escape_x_test:N
+ \else:
+ \exp_after:wN \regex_escape_x_test_ii:N
+ \exp_after:wN #1
+ \fi:
}
}
-\cs_new:Npn \regex_escape_x_braced_loop:N #1
+\cs_new:Npn \regex_escape_x_test_ii:N #1
+ {
+ \if_charcode:w \c_lbrace_str #1
+ \exp_after:wN \regex_escape_x_loop:N
+ \else:
+ \str_aux_hexadecimal_use:NTF #1
+ { \exp_after:wN \regex_escape_x_ii:N }
+ { ; \exp_after:wN \regex_escape_loop:N \exp_after:wN #1 }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_escape_x_ii:N}
+% This looks for the second digit in the unbraced case.
+% \begin{macrocode}
+\cs_new:Npn \regex_escape_x_ii:N #1
{
- \token_if_eq_charcode:NNTF \c_space_token #1
- { \regex_escape_x_braced_loop:N }
+ \str_if_eq:xxTF {#1} { break } { ; }
{
\str_aux_hexadecimal_use:NTF #1
- { \regex_escape_x_braced_loop:N }
- { \exp_stop_f: \regex_escape_x_braced_end:N #1 }
+ { ; \regex_escape_loop:N }
+ { ; \regex_escape_loop:N #1 }
}
}
-\cs_new_protected:Npx \regex_escape_x_braced_end:N #1
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_escape_x_loop:N}
+% Grab hexadecimal digits, skip spaces, and at the end, check that
+% there is a right brace, otherwise raise an error outside the
+% assignment.
+% \begin{macrocode}
+\cs_new:Npn \regex_escape_x_loop:N #1
{
- \exp_not:N \token_if_eq_charcode:NNTF \c_rbrace_str #1
- { \exp_not:N \regex_escape_x_end: }
+ \str_aux_hexadecimal_use:NTF #1
+ { \regex_escape_x_loop:N }
{
- \msg_kernel_error:nnx { regex } { x-missing-rbrace }
- { \int_use:N \l_regex_current_char_int }
- \exp_not:N \regex_escape_x_end: #1
+ \token_if_eq_charcode:NNTF \c_space_token #1
+ { \regex_escape_x_loop:N }
+ {
+ ;
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #1
+ { \regex_escape_loop:N }
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \msg_kernel_error:nn { regex } { x-missing-rbrace } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N #1
+ }
+ }
}
}
-\group_begin:
- \char_set_catcode_other:N \^^@
- \cs_new_protected_nopar:Npn \regex_escape_x_end:
- {
- \if_num:w \l_regex_current_char_int > \c_max_char_int
- \msg_kernel_error:nnx { regex } { x-overflow }
- { \int_use:N \l_regex_current_char_int }
- \exp_after:wN \use:n
- \else:
- \tex_lccode:D \c_zero \l_regex_current_char_int
- \exp_after:wN \tl_to_lowercase:n
- \fi:
- {
- \tl_set:Nx \l_regex_internal_a_tl
- { \if_false: } \fi:
- \regex_escape_raw:N ^^@
- \regex_escape_loop:N
- }
- }
-\group_end:
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[EXP,aux]{\regex_char_if_alphanumeric:NTF}
-% \begin{macro}[EXP,aux]{\regex_char_if_special:NTF}
-% These two tests are used in the first pass when parsing a
-% regular expression. That pass is responsible for finding
-% escaped and non-escaped characters, and recognizing which
-% ones have special meanings and which should be interpreted
-% as \enquote{raw} characters. Namely,
+% \begin{macro}[EXP, aux]
+% {\regex_char_if_alphanumeric:NTF, \regex_char_if_special:NTF}
+% These two tests are used in the first pass when parsing a regular
+% expression. That pass is responsible for finding escaped and
+% non-escaped characters, and recognizing which 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
@@ -1933,15 +1530,178 @@
}
% \end{macrocode}
% \end{macro}
+%
+% \subsection{Compiling}
+%
+% A regular expression starts its life as a string of characters. In
+% this section, we convert it to internal instructions, resulting in a
+% \enquote{compiled} regular expression. This compiled expression is
+% then turned into states of an automaton in the building
+% phase. Compiled regular expressions consist of the following:
+% \begin{itemize}
+% \item \cs{regex_class:NnnnN} \meta{boolean} \Arg{tests} \Arg{min}
+% \Arg{more} \meta{lazyness}
+% \item \cs{regex_group:nnnN} \Arg{branches} \Arg{min} \Arg{more}
+% \meta{lazyness}, also \cs{regex_group_no_capture:nnnN} and
+% \cs{regex_group_resetting:nnnN} with the same syntax.
+% \item \cs{regex_branch:n} \Arg{contents}
+% \item \cs{regex_command_K:}
+% \item \cs{regex_assertion:Nn} \meta{boolean} \Arg{assertion test},
+% where the \meta{assertion test} is \cs{regex_b_test:} or
+% |{|\cs{regex_anchor:N} \meta{integer}|}|
+% \end{itemize}
+% Tests can be the following:
+% \begin{itemize}
+% \item \cs{regex_item_caseful_equal:n} \Arg{char code}
+% \item \cs{regex_item_caseless_equal:n} \Arg{char code}
+% \item \cs{regex_item_caseful_range:nn} \Arg{min} \Arg{max}
+% \item \cs{regex_item_caseless_range:nn} \Arg{min} \Arg{max}
+% \item \cs{regex_item_catcode:nT} \Arg{catcode bitmap} \Arg{tests}
+% \item \cs{regex_item_catcode_reverse:nT} \Arg{catcode bitmap} \Arg{tests}
+% \item \cs{regex_item_reverse:n} \Arg{tests}
+% \item \cs{regex_item_exact:nn} \Arg{catcode} \Arg{char code}
+% \item \cs{regex_item_exact_cs:c} \Arg{csname}
+% \item \cs{regex_item_cs:n} \Arg{compiled regex}
+% \end{itemize}
+%
+% \subsubsection{Variables used when compiling}
+%
+% \begin{variable}{\l_regex_group_level_int}
+% We make sure to open the same number of groups as we close.
+% \begin{macrocode}
+\int_new:N \l_regex_group_level_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_mode_int}
+% While compiling, ten modes are recognized, labelled $-63$, $-23$,
+% $-6$, $-2$, $0$, $2$, $3$, $6$, $23$, $63$. See
+% section~\ref{sec:regex-modes}.
+% \begin{macrocode}
+\int_new:N \l_regex_mode_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_catcodes_int, \l_regex_default_catcodes_int}
+% \begin{variable}{\l_regex_catcodes_bool}
+% We wish to allow constructions such as |\c[^BE](..\cL[a-z]..)|,
+% where the outer catcode test applies to the whole group, but is
+% superseeded by the inner catcode test. For this to work, we need to
+% keep track of lists of allowed category codes:
+% \cs{l_regex_catcodes_int} and \cs{l_regex_default_catcodes_int} are
+% bitmaps, sums of $4^c$, for all allowed catcodes $c$. The latter is
+% local to each capturing group, and we reset
+% \cs{l_regex_catcodes_int} to that value after each character or
+% class, changing it only when encountering a |\c| escape. The boolean
+% records whether the list of categories of a catcode test has to be
+% inverted: compare |\c[^BE]| and |\c[BE]|.
+% \begin{macrocode}
+\int_new:N \l_regex_catcodes_int
+\int_new:N \l_regex_default_catcodes_int
+\bool_new:N \l_regex_catcodes_bool
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \c_regex_catcode_C_int, \c_regex_catcode_B_int, \c_regex_catcode_E_int,
+% \c_regex_catcode_M_int, \c_regex_catcode_T_int, \c_regex_catcode_P_int,
+% \c_regex_catcode_U_int, \c_regex_catcode_D_int, \c_regex_catcode_S_int,
+% \c_regex_catcode_L_int, \c_regex_catcode_O_int, \c_regex_catcode_A_int
+% }
+% \begin{variable}{\c_regex_all_catcodes_int}
+% Constants: $4^c$ for each category, and the sum of all powers of $4$.
+% \begin{macrocode}
+\int_const:Nn \c_regex_catcode_C_int { "1 }
+\int_const:Nn \c_regex_catcode_B_int { "4 }
+\int_const:Nn \c_regex_catcode_E_int { "10 }
+\int_const:Nn \c_regex_catcode_M_int { "40 }
+\int_const:Nn \c_regex_catcode_T_int { "100 }
+\int_const:Nn \c_regex_catcode_P_int { "1000 }
+\int_const:Nn \c_regex_catcode_U_int { "4000 }
+\int_const:Nn \c_regex_catcode_D_int { "10000 }
+\int_const:Nn \c_regex_catcode_S_int { "100000 }
+\int_const:Nn \c_regex_catcode_L_int { "400000 }
+\int_const:Nn \c_regex_catcode_O_int { "1000000 }
+\int_const:Nn \c_regex_catcode_A_int { "4000000 }
+\int_const:Nn \c_regex_all_catcodes_int { "5515155 }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_internal_regex}
+% The compilation step stores its result in this variable.
+% \begin{macrocode}
+\cs_new_eq:NN \l_regex_internal_regex \c_regex_no_match_regex
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_show_prefix_seq}
+% This sequence holds the prefix that makes up the line displayed to
+% the user. The various items must be removed from the right, which is
+% tricky with a token list, hence we use a sequence.
+% \begin{macrocode}
+\seq_new:N \l_regex_show_prefix_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_show_lines_int}
+% A hack. To know whether a given class has a single item in it or
+% not, we count the number of lines when showing the class.
+% \begin{macrocode}
+\int_new:N \l_regex_show_lines_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Generic helpers used when compiling}
+%
+% \begin{macro}[int]{\regex_get_digits:NTFw}
+% \begin{macro}[aux, rEXP]{\regex_get_digits_loop:w}
+% If followed by some raw digits, collect them one by one in the
+% integer variable |#1|, and take the \texttt{true} branch. Otherwise,
+% take the \texttt{false} branch.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_get_digits:NTFw #1#2#3#4#5
+ {
+ \regex_if_raw_digit:NNTF #4 #5
+ { #1 = #5 \regex_get_digits_loop:nw {#2} }
+ { #3 #4 #5 }
+ }
+\cs_new:Npn \regex_get_digits_loop:nw #1#2#3
+ {
+ \regex_if_raw_digit:NNTF #2 #3
+ { #3 \regex_get_digits_loop:nw {#1} }
+ { \scan_stop: #1 #2 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
% \end{macro}
%
-% \subsection{Building}
+% \begin{macro}[aux, EXP]{\regex_if_raw_digit:NNTF}
+% Test used when grabbing digits for the |{m,n}| quantifier.
+% It only accepts non-escaped digits.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \regex_if_raw_digit:NN #1#2 { TF }
+ {
+ \if_meaning:w \regex_compile_raw:N #1
+ \if_int_compare:w \c_one < 1 #2 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
%
-% \subsubsection{Build mode}
+% \subsubsection{Mode}
% \label{sec:regex-modes}
%
-% When building the NFA corresponding to a given regex, we can be in
-% ten distinct modes, which we label by some magic numbers:
+% When compiling the \textsc{nfa} corresponding to a given regex string,
+% we can be in ten distinct modes, which we label by some magic numbers:
% \begin{itemize}
% \item[-6] |[\c{...}]| control sequence in a class,
% \item[-2] |\c{...}| control sequence,
@@ -1955,8 +1715,8 @@
% \item[63] |[\c[...]]| class inside mode $6$.
% \end{itemize}
% This list is exhaustive, because |\c| escape sequences cannot be
-% nested, and character classes cannot be nested directly. The choice
-% of numbers is such as to optimize the most useful tests, and make
+% nested, and character classes cannot be nested directly. The choice of
+% numbers is such as to optimize the most useful tests, and make
% transitions from one mode to another as simple as possible.
% \begin{itemize}
% \item Even modes mean that we are not directly in a character class.
@@ -1980,15 +1740,15 @@
% any mode.
% \end{itemize}
%
-% \begin{macro}[int, EXP]{\regex_build_if_in_class:TF}
-% Test whether we are currently in a character class (at the
-% inner-most level of nesting). There, many escape sequences are not
-% recognized, and special characters are normal. Also, for every raw
-% character, we must look ahead for a possible raw dash.
+% \begin{macro}[int, EXP]{\regex_if_in_class:TF}
+% Test whether we are directly in a character class (at the innermost
+% level of nesting). There, many escape sequences are not recognized,
+% and special characters are normal. Also, for every raw character, we
+% must look ahead for a possible raw dash.
% \begin{macrocode}
-\cs_new_nopar:Npn \regex_build_if_in_class:TF
+\cs_new_nopar:Npn \regex_if_in_class:TF
{
- \if_int_odd:w \l_regex_build_mode_int
+ \if_int_odd:w \l_regex_mode_int
\exp_after:wN \use_i:nn
\else:
\exp_after:wN \use_ii:nn
@@ -1997,16 +1757,35 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_build_if_assertions_forbidden:TF}
+% \begin{macro}[int, EXP]{\regex_if_in_cs:TF}
+% Right braces are special only directly inside control sequences (at
+% the inner-most level of nesting, not counting groups).
+% \begin{macrocode}
+\cs_new_nopar:Npn \regex_if_in_cs:TF
+ {
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_ii:nn
+ \else:
+ \if_int_compare:w \l_regex_mode_int < \c_zero
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]{\regex_if_in_class_or_catcode:TF}
% Assertions are only allowed in modes $0$, $-2$, and $-6$,
% \emph{i.e.}, even, non-positive modes.
% \begin{macrocode}
-\cs_new_nopar:Npn \regex_build_if_assertions_forbidden:TF
+\cs_new_nopar:Npn \regex_if_in_class_or_catcode:TF
{
- \if_int_odd:w \l_regex_build_mode_int
+ \if_int_odd:w \l_regex_mode_int
\exp_after:wN \use_i:nn
\else:
- \if_int_compare:w \l_regex_build_mode_int > \c_zero
+ \if_int_compare:w \l_regex_mode_int > \c_zero
\exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
\else:
\exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
@@ -2016,295 +1795,457 @@
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Helpers for building an NFA}
-%
-% \begin{macro}[int]{\regex_build_new_state:}
-% Add a new state to the NFA. At the end of the building phase, we
-% want every \tn{toks} register to start with \cs{s_stop}, hence
-% initialize the new register appropriately. Then update
-% the left, right, and max \meta{states}.
+% \begin{macro}[int, EXP]{\regex_if_within_catcode:TF}
+% This test takes the true branch if we are in a catcode test, either
+% immediately following it (modes $2$ and $6$) or in a class on which
+% it applies (modes $23$ and $63$). This is used to tweak how left
+% brackets behave in modes $2$ and $6$.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_new_state:
+\cs_new_nopar:Npn \regex_if_within_catcode:TF
{
- \tex_toks:D \l_regex_max_state_int { \s_stop }
- \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
- \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int
- \int_incr:N \l_regex_max_state_int
+ \if_int_compare:w \l_regex_mode_int > \c_zero
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_transition:NN}
-% \begin{macro}[aux]{\regex_build_transitions:NNNN}
-% These functions create a new state, and put one or two transitions
-% starting from the old current state.
+% \begin{macro}[aux]{\regex_chk_c_allowed:T}
+% The |\c| escape sequence is only allowed in modes $0$ and $3$,
+% \emph{i.e.}, not within any other |\c| escape sequence.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_transition:NN #1#2
+\cs_new_protected:Npn \regex_chk_c_allowed:T
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- { #1 { \int_eval:n { #2 - \l_regex_left_state_int } } }
+ \if_num:w \l_regex_mode_int = \c_zero
+ \exp_after:wN \use:n
+ \else:
+ \if_num:w \l_regex_mode_int = \c_three
+ \exp_after:wN \exp_after:wN \exp_after:wN \use:n
+ \else:
+ \msg_kernel_error:nn { regex } { c-bad-mode }
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_none:n
+ \fi:
+ \fi:
}
-\cs_new_protected:Npn \regex_build_transitions:NNNN #1#2#3#4
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_mode_quit_c:}
+% This function changes the mode as it is needed just after a catcode
+% test.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_mode_quit_c:
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- {
- #1 { \int_eval:n { #2 - \l_regex_left_state_int } }
- #3 { \int_eval:n { #4 - \l_regex_left_state_int } }
- }
+ \if_num:w \l_regex_mode_int = \c_two
+ \l_regex_mode_int = \c_zero
+ \else:
+ \if_num:w \l_regex_mode_int = \c_six
+ \l_regex_mode_int = \c_three
+ \fi:
+ \fi:
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \subsubsection{From regex to NFA: framework}
+% \subsubsection{Framework}
%
-% \begin{macro}[int]{\regex_build:w, \regex_build_end:}
-% \begin{macro}[int]{\regex_build_exit_class:, \regex_build_exit_groups:}
-% There are two situations where we want to convert a regular
-% expression given as a string to an NFA. The first case is when the
-% regex is given directly by the user, as an \texttt{n} argument to
-% one of the \pkg{l3regex} functions, and the second case is when
-% building the regex given as an argument of the |\c{...}| escape
-% sequence within another regular expression. In both cases, some
-% variables must be reset before starting to build the NFA, using
-% \cs{regex_build:w}. Once building has ended (for instance when
-% encountering the closing brace in |\c{...}|), we make sure to close
-% any dangling class or open group, then add
-% \cs{regex_action_success:} to make the match successful if it
-% reaches the last state. The capturing group number is incremented:
-% then its value tells us how many capturing group there are,
-% including $0$.
+% \begin{macro}[int]{\regex_compile:w, \regex_compile_end:}
+% Used when compiling a user regex or a regex for the |\c{...}| escape
+% sequence within another regex. Start building a token list within a
+% group (with \texttt{x}-expansion at the outset), and set a few
+% variables (group level, catcodes), then start the first branch. At
+% the end, make sure there are no dangling classes nor groups, close
+% the last branch: we are done building \cs{l_regex_internal_regex}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build:w
- {
- \int_set_eq:NN \l_regex_catcodes_default_int \c_regex_catcodes_all_int
- \int_set_eq:NN \l_regex_catcodes_int \l_regex_catcodes_default_int
- \int_set_eq:NN \l_regex_capturing_group_int \c_zero
- \seq_clear:N \l_regex_capturing_group_seq
- \tl_clear:N \l_regex_tests_tl
- \bool_set_true:N \l_regex_tests_bool
- \int_zero:N \l_regex_max_state_int
- \regex_build_new_state:
- }
-\cs_new_protected_nopar:Npn \regex_build_end:
+\cs_new_protected_nopar:Npn \regex_compile:w
{
- \regex_build_exit_class:
- \regex_build_exit_groups:
- \regex_toks_put_right:Nx \l_regex_right_state_int
- { \regex_action_success: }
- \int_incr:N \l_regex_capturing_group_int
+ \tl_set_build_x:Nw \l_regex_internal_regex
+ \int_zero:N \l_regex_group_level_int
+ \int_set_eq:NN \l_regex_default_catcodes_int \c_regex_all_catcodes_int
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
}
-\cs_new_protected_nopar:Npn \regex_build_exit_class:
+\cs_new_protected_nopar:Npn \regex_compile_end:
{
- \regex_build_if_in_class:TF
- {
- \msg_kernel_error:nn { regex } { missing-rbrack }
- \use:c { regex_build_]: }
- }
- { }
- }
-\cs_new_protected_nopar:Npn \regex_build_exit_groups:
- {
- \seq_if_empty:NF \l_regex_capturing_group_seq
- {
+ \regex_if_in_class:TF
+ {
+ \msg_kernel_error:nn { regex } { missing-rbrack }
+ \use:c { regex_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_num:w \l_regex_group_level_int > \c_zero
\msg_kernel_error:nnx { regex } { missing-rparen }
- { \seq_length:N \l_regex_capturing_group_seq }
+ { \int_use:N \l_regex_group_level_int }
\prg_replicate:nn
- { \seq_length:N \l_regex_capturing_group_seq }
- { \regex_build_close_aux: \regex_build_group_: }
- }
+ { \l_regex_group_level_int }
+ {
+ \tl_build_one:n
+ {
+ \if_false: { \fi: }
+ \if_false: { \fi: } { 1 } { 0 } \c_true_bool
+ }
+ \tl_build_end:
+ \tl_build_one:o \l_regex_internal_regex
+ }
+ \fi:
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[int]{\regex_build:n}
-% Setup some variables with \cs{regex_build:w}, and additionally set
-% the build mode to $0$: we are neither in a class nor a |\c|
-% construction. Initiate the regular expression by a wildcard: the
-% search is unanchored and should be tried at every index in the token
-% list. We surround the regular expression by parentheses with
-% \cs{regex_build_open_aux:} and \cs{regex_build_close_aux:}, so that
-% any alternative appears within a group (\emph{e.g.}, \verb"ab|cd"
-% will now be within the external group); this also forms a capturing
-% group (labelled $0$), which gives us the whole match as the $0$-th
-% submatch. The regular expression itself is parsed by using the
-% generic framework of \cs{regex_escape_use:nnnn} to recognize special
-% characters, escaped ones, and escape sequences such as |\n| or
-% |\x{02f}|. This results in successive \cs{regex_build_raw:N},
-% \cs{regex_build_escaped:N}, \cs{regex_build_special:N} followed by
-% their arguments. The trailing \cs{prg_do_nothing:} ensure that the
-% look-ahead done by some of the operations is harmless. Finally,
-% \cs{regex_build_end:} adds the finishing code (checking that
-% parentheses are properly nested, for instance).
+% \begin{macro}[int]{\regex_compile:n}
+% The compilation is done between \cs{regex_compile:w} and
+% \cs{regex_compile_end:}, starting in mode~$0$. Then
+% \cs{regex_escape_use:nnnn} distinguishes special characters, escaped
+% alphanumerics, and raw characters, interpreting |\a|, |\x| and other
+% sequences. The $4$ trailing \cs{prg_do_nothing:} are needed because
+% some functions defined later look up to $4$ tokens ahead. Before
+% ending, make sure that any |\c{...}| is properly closed.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build:n #1
+\cs_new_protected:Npn \regex_compile:n #1
{
- \regex_build:w
- \int_set_eq:NN \l_regex_build_mode_int \c_zero
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- { \regex_action_start_wildcard: }
- \regex_build_open_aux:
- \regex_escape_use:nnnn
- {
- \regex_char_if_special:NTF ##1
- \regex_build_special:N \regex_build_raw:N ##1
- }
- {
- \regex_char_if_alphanumeric:NTF ##1
- \regex_build_escaped:N \regex_build_raw:N ##1
- }
- { \regex_build_raw:N ##1 }
- { #1 }
- \prg_do_nothing: \prg_do_nothing:
- \prg_do_nothing: \prg_do_nothing:
- \int_compare:nNnT \l_regex_build_mode_int < \c_zero
+ \regex_compile:w
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_set_eq:NN \l_regex_mode_int \c_zero
+ \regex_escape_use:nnnn
+ {
+ \regex_char_if_special:NTF ##1
+ \regex_compile_special:N \regex_compile_raw:N ##1
+ }
+ {
+ \regex_char_if_alphanumeric:NTF ##1
+ \regex_compile_escaped:N \regex_compile_raw:N ##1
+ }
+ { \regex_compile_raw:N ##1 }
+ { #1 }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ \int_compare:nNnT \l_regex_mode_int < \c_zero
{
\msg_kernel_error:nn { regex } { c-missing-rbrace }
- \exp_after:wN \regex_build_special:N \c_rbrace_str
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
}
- \seq_put_right:Nn \l_regex_capturing_group_seq {0}
- \regex_build_close_aux: \regex_build_group_:
- \regex_build_end:
+ \regex_compile_end:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_special:N}
-% \begin{macro}[aux]{\regex_build_escaped:N}
+% \begin{macro}[int]{\regex_compile_escaped:N, \regex_compile_special:N}
% If the special character or escaped alphanumeric has a particular
% meaning in regexes, the corresponding function is used. Otherwise,
% it is interpreted as a raw character. We distinguish special
% characters from escaped alphanumeric characters because they behave
% differently when appearing as an end-point of a range.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_special:N #1
+\cs_new_protected:Npn \regex_compile_special:N #1
{
- \cs_if_exist_use:cF { regex_build_#1: }
- { \regex_build_raw:N #1 }
+ \cs_if_exist_use:cF { regex_compile_#1: }
+ { \regex_compile_raw:N #1 }
}
-\cs_new_protected:Npn \regex_build_escaped:N #1
+\cs_new_protected:Npn \regex_compile_escaped:N #1
{
- \cs_if_exist_use:cF { regex_build_/#1: }
- { \regex_build_raw:N #1 }
+ \cs_if_exist_use:cF { regex_compile_/#1: }
+ { \regex_compile_raw:N #1 }
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[int]{\regex_build_one:n, \regex_build_one:x}
-% In a class, add the argument to the current class. Outside a class,
-% this argument is the only test, and we look for quantifiers.
+% \begin{macro}[int]{\regex_compile_one:x}
+% This is used after finding one \enquote{test}, such as |\d|, or a
+% raw character. If that followed a catcode test (\emph{e.g.}, |\cL|),
+% then restore the mode. If we are not in a class, then the test is
+% \enquote{standalone}, and we need to add \cs{regex_class:NnnnN} and
+% search for quantifiers. In any case, insert the test, possibly
+% together with a catcode test if appropriate.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_one:n #1
- { \regex_build_one:x { \exp_not:n {#1} } }
-\cs_new_protected:Npn \regex_build_one:x #1
+\cs_new_protected:Npn \regex_compile_one:x #1
{
- \tl_put_right:Nx \l_regex_tests_tl
+ \regex_mode_quit_c:
+ \regex_if_in_class:TF { }
+ {
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ }
+ \tl_build_one:x
{
- \if_num:w \l_regex_catcodes_int < \c_regex_catcodes_all_int
+ \if_num:w \l_regex_catcodes_int < \c_regex_all_catcodes_int
\regex_item_catcode:nT { \int_use:N \l_regex_catcodes_int }
+ { \exp_not:N \exp_not:n {#1} }
\else:
- \exp_after:wN \use:n
+ \exp_not:N \exp_not:n {#1}
\fi:
- {#1}
}
- \int_set_eq:NN \l_regex_catcodes_int \l_regex_catcodes_default_int
- \if_num:w \l_regex_build_mode_int = \c_two
- \l_regex_build_mode_int = \c_zero
- \else:
- \if_num:w \l_regex_build_mode_int = \c_six
- \l_regex_build_mode_int = \c_three
- \fi:
- \fi:
- \if_int_odd:w \l_regex_build_mode_int \else:
- \exp_after:wN \regex_build_one_quantifier:
- \fi:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_if_in_class:TF { } { \regex_compile_quantifier:w }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]
+% {\regex_compile_abort_tokens:n, \regex_compile_abort_tokens:x}
+% This function places the collected tokens back in the input stream,
+% each as a raw character. Spaces are not preserved.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_compile_abort_tokens:n #1
+ {
+ \use:x
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \regex_compile_raw:N
+ }
+ }
+\cs_generate_variant:Nn \regex_compile_abort_tokens:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Quantifers}
+%
+% \begin{macro}[int]{\regex_compile_quantifier:w}
+% This looks ahead and finds any quantifier (special character equal
+% to either of \texttt{?+*\{}).
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_compile_quantifier:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
+ {
+ \cs_if_exist_use:cF { regex_compile_quantifier_#2:w }
+ { \regex_compile_quantifier_none: #1 #2 }
+ }
+ { \regex_compile_quantifier_none: #1 #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_compile_quantifier_none:}
+% \begin{macro}[aux]{\regex_compile_quantifier_abort:xNN}
+% Those functions are called whenever there is no quantifier, or a
+% braced construction is invalid (equivalent to no quantifier, and
+% whatever characters were grabbed are left raw).
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_compile_quantifier_none:
+ { \tl_build_one:n { \if_false: { \fi: } { 1 } { 0 } \c_false_bool } }
+\cs_new_protected:Npn \regex_compile_quantifier_abort:xNN #1#2#3
+ {
+ \regex_compile_quantifier_none:
+ \msg_kernel_warning:nnxx { regex } { invalid-quantifier } {#1} {#3}
+ \regex_compile_abort_tokens:x {#1}
+ #2 #3
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_compile_quantifier_lazyness:nnNN}
+% Once the \enquote{main} quantifier (\texttt{?}, \texttt{*},
+% \texttt{+} or a braced construction) is found, we check whether it
+% is lazy (followed by a question mark). We then add to the compiled
+% regex a closing brace (ending \cs{regex_class:NnnnN} and friends),
+% the start-point of the range, its end-point, and a boolean,
+% \texttt{true} for lazy and \texttt{false} for greedy operators.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_compile_quantifier_lazyness:nnNN #1#2#3#4
+ {
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ? }
+ { \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_true_bool } }
+ {
+ \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_false_bool }
+ #3 #4
+ }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_tests_action_cost:n}
-% The argument is the target state if the test succeeds.
+% \begin{macro}[aux]
+% {
+% \regex_compile_quantifier_?:w,
+% \regex_compile_quantifier_*:w,
+% \regex_compile_quantifier_+:w
+% }
+% For each \enquote{basic} quantifier, |?|, |*|, |+|, feed the correct
+% arguments to \cs{regex_compile_quantifier_lazyness:nnNN}, $-1$ means
+% that there is no upper bound on the number of repetitions.
+% \begin{macrocode}
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_?:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { 1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_*:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { -1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_+:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 1 } { -1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]+\regex_compile_quantifier_{:w+ ^^A}
+% \begin{macro}[aux]
+% {
+% \regex_compile_quantifier_braced_i:w,
+% \regex_compile_quantifier_braced_ii:w,
+% \regex_compile_quantifier_braced_iii:w,
+% }
+% Three possible syntaxes: \texttt{\{\meta{int}\}},
+% \texttt{\{\meta{int},\}}, or \texttt{\{\meta{int},\meta{int}\}}. Any
+% other syntax causes us to abort and put whatever we collected back
+% in the input stream, as \texttt{raw} characters, including the
+% opening brace. Grab a number into \cs{l_regex_internal_a_int}. If
+% the number is followed by a right brace, the range is $[a,a]$. If
+% followed by a comma, grab one more number, and call the \texttt{_ii}
+% or \texttt{_iii} auxiliary. Those auxiliaries check for a closing
+% brace, leading to the range $[a,\infty]$ or $[a,b]$, encoded as
+% $\{a\}\{-1\}$ and $\{a\}\{b-a\}$.
% \begin{macrocode}
-\cs_new:Npn \regex_tests_action_cost:n #1
+\cs_new_protected:cpn { regex_compile_quantifier_ \c_lbrace_str :w }
+ {
+ \regex_get_digits:NTFw \l_regex_internal_a_int
+ { \regex_compile_quantifier_braced_i:w }
+ { \regex_compile_quantifier_abort:xNN { \c_lbrace_str } }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_i:w #1#2
+ {
+ \prg_case_str:xxn { #1 #2 }
+ {
+ { \regex_compile_special:N \c_rbrace_str }
+ {
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { 0 }
+ }
+ { \regex_compile_special:N , }
+ {
+ \regex_get_digits:NTFw \l_regex_internal_b_int
+ { \regex_compile_quantifier_braced_iii:w }
+ { \regex_compile_quantifier_braced_ii:w }
+ }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_ii:w #1#2
+ {
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { -1 }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int , }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_iii:w #1#2
{
- \exp_not:o \l_regex_tests_tl
- \bool_if:NTF \l_regex_tests_bool
- { \regex_break_point:TF { \regex_action_cost:n {#1} } { } }
- { \regex_break_point:TF { } { \regex_action_cost:n {#1} } }
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \if_num:w \l_regex_internal_a_int > \l_regex_internal_b_int
+ \msg_kernel_error:nnxx { regex } { backwards-quantifier }
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ \int_zero:N \l_regex_internal_b_int
+ \else:
+ \int_sub:Nn \l_regex_internal_b_int \l_regex_internal_a_int
+ \fi:
+ \exp_args:Noo \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ {
+ \c_lbrace_str
+ \int_use:N \l_regex_internal_a_int ,
+ \int_use:N \l_regex_internal_b_int
+ }
+ #1 #2
+ }
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \subsubsection{Raw characters}
%
-% \begin{macro}[int]{\regex_build_raw_error:N}
-% Within character classes, some escaped alphanumeric sequences such
-% as |\b| do not have any meaning. They are replaced by a raw
-% character, after spitting out an error.
+% \begin{macro}[int]{\regex_compile_raw_error:N}
+% Within character classes, and following catcode tests, some escaped
+% alphanumeric sequences such as |\b| do not have any meaning. They
+% are replaced by a raw character, after spitting out an error.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_raw_error:N #1
+\cs_new_protected:Npn \regex_compile_raw_error:N #1
{
- \if_int_odd:w \l_regex_build_mode_int
- \msg_kernel_error:nnx { regex } { class-bad-escape } {#1}
- \else:
- \msg_kernel_error:nnx { regex } { catcode-bad-escape } {#1}
- \fi:
- \regex_build_raw:N #1
+ \msg_kernel_error:nnx { regex } { bad-escape } {#1}
+ \regex_compile_raw:N #1
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_build_raw:N}
+% \begin{macro}[int]{\regex_compile_raw:N}
% If we are in a character class and the next character is an
% unescaped dash, this denotes a range. Otherwise, the current
% character |#1| matches itself.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_raw:N #1#2#3
+\cs_new_protected:Npn \regex_compile_raw:N #1#2#3
{
- \regex_build_if_in_class:TF
- { \str_if_eq:nnTF {#2#3} { \regex_build_special:N - } }
- { \use_ii:nn }
- { \regex_class_range:Nw #1 }
+ \regex_if_in_class:TF
{
- \regex_build_one:x { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ \str_if_eq:nnTF {#2#3} { \regex_compile_special:N - }
+ { \regex_compile_range:Nw #1 }
+ {
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ #2 #3
+ }
+ }
+ {
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
#2 #3
}
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_class_range:Nw}
+% \begin{macro}[aux]{\regex_compile_range:Nw, \regex_if_end_range:NNTF}
% We have just read a raw character followed by a dash; this should be
-% followed by an end-point for the range. If the following character
-% is an escaped alphanumeric, or if it is an unescaped right bracket,
-% then we have an error, so we put the initial character and the dash
-% back, as raw characters. Otherwise, build the range, checking that
-% it is in the right order, and optimizing for equal end-points.
+% followed by an end-point for the range. Valid end-points are: any
+% raw character; any special character, except a right bracket. In
+% particular, escaped characters are forbidden.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_class_range:Nw #1#2#3
+\prg_new_protected_conditional:Npnn \regex_if_end_range:NN #1#2 { TF }
{
- \token_if_eq_meaning:NNTF #2 \regex_build_escaped:N % [
- { \use_i:nn } { \str_if_eq:nnTF { #2#3 } { \regex_build_special:N ] } }
- {
- \msg_kernel_warning:nnxx { regex } { range-missing-end } % [
- {#1} { \token_if_eq_charcode:NNF #3 ] { \c_backslash_str } #3 }
- \tl_put_right:Nx \l_regex_tests_tl
- {
- \regex_item_equal:n { \int_value:w `#1 ~ }
- \regex_item_equal:n { \int_value:w `- ~ }
- }
- #2#3
- }
+ \if_meaning:w \regex_compile_raw:N #1
+ \prg_return_true:
+ \else:
+ \if_meaning:w \regex_compile_special:N #1
+ \if_charcode:w ] #2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_compile_range:Nw #1#2#3
+ {
+ \regex_if_end_range:NNTF #2 #3
{
\if_num:w `#1 > `#3 \exp_stop_f:
\msg_kernel_error:nnxx { regex } { range-backwards } {#1} {#3}
\else:
- \tl_put_right:Nx \l_regex_tests_tl
+ \tl_build_one:x
{
\if_num:w `#1 = `#3 \exp_stop_f:
\regex_item_equal:n
@@ -2315,1796 +2256,2155 @@
}
\fi:
}
+ {
+ \msg_kernel_warning:nnxx { regex } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_one:x
+ {
+ \regex_item_equal:n { \int_value:w `#1 ~ }
+ \regex_item_equal:n { \int_value:w `- ~ }
+ }
+ #2#3
+ }
}
% \end{macrocode}
% \end{macro}
%
% \subsubsection{Character properties}
%
-% \begin{macro}[aux]{\regex_build_.:}
+% \begin{macro}[aux]{\regex_compile_.:, \regex_prop_.:}
% In a class, the dot has no special meaning. Outside, insert
-% \cs{c_regex_._tl}, which matches any character or control
-% sequence, and refuses $-2$, which marks the end of the token list.
+% \cs{regex_prop_.:}, which matches any character or control
+% sequence, and refuses $-2$ (end-marker).
% \begin{macrocode}
-\cs_new_protected_nopar:cpx { regex_build_.: }
+\cs_new_protected_nopar:cpx { regex_compile_.: }
{
- \exp_not:N \regex_build_if_in_class:TF
- { \regex_build_raw:N . }
- { \regex_build_one:n \exp_not:c { c_regex_._tl } }
+ \exp_not:N \regex_if_in_class:TF
+ { \regex_compile_raw:N . }
+ { \regex_compile_one:x \exp_not:c { regex_prop_.: } }
+ }
+\cs_new_protected_nopar:cpn { regex_prop_.: }
+ {
+ \if_num:w \l_regex_current_char_int > - \c_two
+ \exp_after:wN \regex_break_true:w
+ \fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_/d:,\regex_build_/D:}
-% \begin{macro}[aux]{\regex_build_/h:,\regex_build_/H:}
-% \begin{macro}[aux]{\regex_build_/s:,\regex_build_/S:}
-% \begin{macro}[aux]{\regex_build_/v:,\regex_build_/V:}
-% \begin{macro}[aux]{\regex_build_/w:,\regex_build_/W:}
-% \begin{macro}[aux]{\regex_build_/N:}
-% The constants \cs{c_regex_d_tl}, \emph{etc.} hold
+% \begin{macro}[aux]
+% {
+% \regex_compile_/d:, \regex_compile_/D:,
+% \regex_compile_/h:, \regex_compile_/H:,
+% \regex_compile_/s:, \regex_compile_/S:,
+% \regex_compile_/v:, \regex_compile_/V:,
+% \regex_compile_/w:, \regex_compile_/W:,
+% \regex_compile_/N:,
+% }
+% The constants \cs{regex_prop_d:}, \emph{etc.} hold
% a list of tests which match the corresponding character
% class, and jump to the \cs{regex_break_point:TF} marker.
% As for a normal character, we check for quantifiers.
% \begin{macrocode}
-\tl_map_inline:nn { dDhHsSvVwWN }
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \cs_new_protected_nopar:cpx { regex_build_/#1: }
- { \regex_build_one:n \exp_not:c { c_regex_#1_tl } }
+ \cs_new_protected_nopar:cpx { regex_compile_/#1: }
+ { \regex_compile_one:x \exp_not:c { regex_prop_#1: } }
+ \cs_new_protected_nopar:cpx { regex_compile_/#2: }
+ {
+ \regex_compile_one:x
+ { \regex_item_reverse:n \exp_not:c { regex_prop_#1: } }
+ }
}
+\regex_tmp:w d D
+\regex_tmp:w h H
+\regex_tmp:w s S
+\regex_tmp:w v V
+\regex_tmp:w w W
+\cs_new_protected_nopar:cpn { regex_compile_/N: }
+ { \regex_compile_one:x \regex_prop_N: }
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
%
% \subsubsection{Anchoring and simple assertions}
%
-% \begin{macro}[aux]{\regex_build_simple_assertion:nn}
-% Assertions are only allowed in modes $0$, $-2$ and $-6$. In
-% character classes, or immediately following a category code test,
-% escaped letters cause errors, while the meaning of special
-% characters is ignored: |#1| is used instead. In modes where the
-% assertion is allowed, we insert the test |#2|; if the assertion is
-% successful, move from the \texttt{left} state to the \texttt{right}
-% state.
+% \begin{macro}[aux]{\regex_compile_anchor:NF}
+% \begin{macro}[aux]+\regex_compile_^:+
+% \begin{macro}[aux]{\regex_compile_/A:, \regex_compile_/G:}
+% \begin{macro}[aux]+\regex_compile_$:+
+% \begin{macro}[aux]{\regex_compile_/Z:, \regex_compile_/z:}
+% In modes where assertions are allowed, anchor to the start of the
+% query, the start of the match, or the end of the query, depending on
+% the integer |#1|. In other modes, |#2| treats the character as raw,
+% with an error for escaped letters (|$| is valid in a class, but |\A|
+% is definitely a mistake on the user's part).
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_simple_assertion:nn #1#2
+\cs_new_protected:Npn \regex_compile_anchor:NF #1#2
{
- \regex_build_if_assertions_forbidden:TF {#1}
+ \regex_if_in_class_or_catcode:TF {#2}
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- {
- \exp_not:n {#2}
- {
- \regex_action_free:n
- {
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
- }
- }
- }
- %^^A todo: add \regex_assertion_quantifier:
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_anchor:N #1 } }
}
}
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[aux]+\regex_build_^:+
-% \begin{macro}[aux]{\regex_build_/A:}
-% \begin{macro}[aux]{\regex_build_/G:}
-% \begin{macro}[aux]+\regex_build_$:+
-% \begin{macro}[aux]{\regex_build_/Z:}
-% \begin{macro}[aux]{\regex_build_/z:}
-% Anchoring at the start corresponds to checking that the current
-% character is the first in the token list. Anchoring to the beginning
-% of the match attempt uses \cs{l_regex_start_index_int} instead of
-% \cs{c_zero}. End anchors match the end of the token list, marked by
-% a character code of $-2$.
-% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_^: }
- {
- \regex_build_simple_assertion:nn { \regex_build_raw:N ^ }
- { \int_compare:nNnT \l_regex_min_index_int = \l_regex_current_index_int }
- }
-\cs_new_protected_nopar:cpn { regex_build_/A: }
- {
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N A }
- { \int_compare:nNnT \l_regex_min_index_int = \l_regex_current_index_int }
- }
-\cs_new_protected_nopar:cpn { regex_build_/G: }
- {
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N G }
- { \int_compare:nNnT \l_regex_start_index_int = \l_regex_current_index_int }
- }
-\group_begin:
- \char_set_catcode_other:N \$
- \cs_new_protected_nopar:cpn { regex_build_$: } % $
- {
- \regex_build_simple_assertion:nn { \regex_build_raw:N $ } % $
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
- }
-\group_end:
-\cs_new_protected_nopar:cpn { regex_build_/Z: }
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N Z }
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
+ \cs_new_protected_nopar:cpn { regex_compile_/#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw_error:N #1 } }
}
-\cs_new_protected_nopar:cpn { regex_build_/z: }
+\regex_tmp:w A \l_regex_min_pos_int
+\regex_tmp:w G \l_regex_start_pos_int
+\regex_tmp:w Z \l_regex_max_pos_int
+\regex_tmp:w z \l_regex_max_pos_int
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N z }
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
+ \cs_new_protected_nopar:cpn { regex_compile_#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw:N #1 } }
}
+\exp_args:Nx \regex_tmp:w { \iow_char:N \^ } \l_regex_min_pos_int
+\exp_args:Nx \regex_tmp:w { \iow_char:N \$ } \l_regex_max_pos_int
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[int]{\regex_build_/b:}
-% \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 precedes in the token list, this requires
-% more information, namely, the knowledge of the last character
-% code. Case sensitivity does not change word boundaries.
+% \begin{macro}[aux]{\regex_compile_/b:, \regex_compile_/B:}
+% Contrarily to |^| and |$|, which could be implemented without really
+% knowing what precedes in the token list, this requires more
+% information, namely, the knowledge of the last character code.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_/b: }
- {
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N b }
- { \regex_if_word_boundary:T }
- }
-\cs_new_protected_nopar:cpn { regex_build_/B: }
+\cs_new_protected_nopar:cpn { regex_compile_/b: }
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N B }
- { \regex_if_word_boundary:TF { } }
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N b }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_b_test: } }
+ }
}
-\cs_new_protected_nopar:Npn \regex_if_word_boundary:TF
+\cs_new_protected_nopar:cpn { regex_compile_/B: }
{
- \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 { -2 } }
- { \group_end: \c_regex_w_tl }
- \regex_break_point:TF
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N B }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_false_bool { \regex_b_test: } }
+ }
}
-\cs_new_protected_nopar:Npn \regex_if_word_boundary:T
- { \regex_if_word_boundary:TF \use:n \use_none:n }
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \subsubsection{Entering and exiting character classes}
-%
-% \begin{macro}[aux]{\regex_build_[:}
-% In a class, left brackets mean nothing. Outside a class, this starts
-% a class, whose first characters may have a special meaning.
-% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_[: }
- {
- \regex_build_if_in_class:TF
- { \regex_build_raw:N [ }
- { \regex_class_first:NNNN }
- }
-% \end{macrocode}
-% \end{macro}
+% \subsubsection{Character classes}
%
-% \begin{macro}[aux]{\regex_build_]:}
+% \begin{macro}[aux]{\regex_compile_]:}
% Outside a class, right brackets have no meaning. In a class, change
% the mode ($m\to (m-15)/13$, truncated) to reflect the fact that we
-% are leaving the class. If we are still in a class after leaving one,
-% then this is the case |[...\cL[...]...]|, and we insert the relevant
-% closing material in \cs{l_regex_tests_tl}. Otherwise look for
+% are leaving the class. Look for quantifiers, unless we are still in
+% a class after leaving one (the case of |[...\cL[...]...]|).
% quantifiers.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_]: }
+\cs_new_protected:cpn { regex_compile_]: }
{
- \regex_build_if_in_class:TF
+ \regex_if_in_class:TF
{
- \if_num:w \l_regex_build_mode_int > \c_sixteen
- \tl_set:Nx \l_regex_tests_tl
- {
- \exp_not:o \l_regex_tests_saved_tl
- \if_num:w \l_regex_catcodes_saved_int < \c_regex_catcodes_all_int
- \regex_item_catcode:nT
- { \int_use:N \l_regex_catcodes_saved_int }
- \else:
- \exp_after:wN \use:n
- \fi:
- {
- \exp_not:o \l_regex_tests_tl
- \bool_if:NF \l_regex_tests_bool
- { \regex_break_point:TF { } \regex_break_true:w }
- }
- }
- \bool_set_eq:NN \l_regex_tests_bool \l_regex_tests_saved_bool
+ \if_num:w \l_regex_mode_int > \c_sixteen
+ \tl_build_one:n { \if_false: { \fi: } }
\fi:
- \tex_advance:D \l_regex_build_mode_int - \c_fifteen
- \tex_divide:D \l_regex_build_mode_int \c_thirteen
- \if_int_odd:w \l_regex_build_mode_int \else:
- \exp_after:wN \regex_build_one_quantifier:
+ \tex_advance:D \l_regex_mode_int - \c_fifteen
+ \tex_divide:D \l_regex_mode_int \c_thirteen
+ \if_int_odd:w \l_regex_mode_int \else:
+ \exp_after:wN \regex_compile_quantifier:w
\fi:
}
- { \regex_build_raw:N ] }
+ { \regex_compile_raw:N ] }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_class_first:NNNN}
-% This starts a class. Change the mode by appending $3$ to it, and
-% reset the variables \cs{l_regex_tests_tl} and
-% \cs{l_regex_bool_tl}. In the special case of mode $63$
-% (|[\c[...]]|), we open a group, to avoid overriding the setting of
-% \cs{l_regex_tests_bool} and \cs{l_regex_tests_tl}; the group ends at
-% the matching right bracket. If the first character is |^|, then the
-% class is inverted. We keep track of this in
-% \cs{l_regex_tests_bool}. If the next character is a right bracket,
-% then it should be changed to a raw one (dirty hack here; the |F|
-% argument of \cs{str_if_eq:nnTF} is the trailing |#3|).
+% \begin{macro}[aux]{\regex_compile_[:}
+% In a class, left brackets might introduce a \textsc{posix} character
+% class, or mean nothing. Immediately following |\c|\meta{category},
+% we must insert the apropriate catcode test, then parse the class; we
+% pre-expand the catcode as an optimization. Otherwise (modes $0$,
+% $-2$ and $-6$) just parse the class. The mode is updated later.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_class_first:NNNN #1#2#3#4
+\cs_new_protected_nopar:cpn { regex_compile_[: }
{
- \l_regex_build_mode_int = \int_value:w \l_regex_build_mode_int 3 ~ %
- \if_num:w \l_regex_build_mode_int > \c_sixteen
- \tl_set_eq:NN \l_regex_tests_saved_tl \l_regex_tests_tl
- \bool_set_eq:NN \l_regex_tests_saved_bool \l_regex_tests_bool
- \int_set_eq:NN \l_regex_catcodes_saved_int \l_regex_catcodes_int
- \int_set_eq:NN \l_regex_catcodes_int \c_regex_catcodes_all_int
- \fi:
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
+ \regex_if_in_class:TF
+ { \regex_compile_class_posix_test:w }
{
- \token_if_eq_charcode:NNTF #2 ^
+ \regex_if_within_catcode:TF
{
- \bool_set_false:N \l_regex_tests_bool % [
- \str_if_eq:nnTF {#3#4} { \regex_build_special:N ] }
- { \regex_build_raw:N }
- }
- { % [
- \token_if_eq_charcode:NNTF #2 ]
- { \regex_build_raw:N #2 }
- { #1 #2 }
+ \exp_after:wN \regex_compile_class_catcode:w
+ \int_use:N \l_regex_catcodes_int ;
}
+ { \regex_compile_class_normal:w }
}
- { #1 #2 }
- #3 #4
}
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Catcodes and csnames}
-%
-% \begin{macro}[aux]{\regex_build_/c:}
-% The |\c| escape sequence is only allowed in modes $0$ and $3$,
-% \emph{i.e.}, not within any other |\c| escape sequence.
+% \begin{macro}[aux]{\regex_compile_class_normal:w}
+% In the \enquote{normal} case, we will insert \cs{regex_class:NnnnN}
+% \meta{boolean} in the compiled code. The \meta{boolean} is true for
+% positive classes, and false for negative classes, characterized by a
+% leading |^|. The auxiliary \cs{regex_compile_class:TFNN} also
+% checks for a leading |]| which has a special meaning.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_/c: }
+\cs_new_protected_nopar:Npn \regex_compile_class_normal:w
{
- \if_case:w \l_regex_build_mode_int
- \exp_after:wN \regex_build_c_aux:wNN
- \or: \or: \or: \exp_after:wN \regex_build_c_aux:wNN
- \fi:
- \msg_kernel_error:nn { regex } { c-bad-mode }
- \s_stop
+ \regex_compile_class:TFNN
+ { \regex_class:NnnnN \c_true_bool }
+ { \regex_class:NnnnN \c_false_bool }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_c_aux:wNN}
-% The |\c| escape sequence can be followed by a capital letter
-% representing a character category, by a left bracket which starts a
-% list of categories, or by a brace group holding a regular expression
-% for a control sequence name. Otherwise, raise an error.
+% \begin{macro}[aux]{\regex_compile_class_catcode:w}
+% This function is called for a left bracket in modes $2$ or $6$
+% (catcode test, and catcode test within a class). In mode $2$ the
+% whole construction needs to be put in a class (like single
+% character). Then determine if the class is positive or negative,
+% inserting \cs{regex_item_catcode:nT} or the \texttt{reverse} variant
+% as appropriate, each with the current catcodes bitmap |#1| as an
+% argument, and reset the catcodes.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_c_aux:wNN #1 \s_stop #2#3
+\cs_new_protected:Npn \regex_compile_class_catcode:w #1;
{
- \token_if_eq_meaning:NNTF #2 \regex_build_raw:N
- {
- \cs_if_exist:cTF { c_regex_catcode_#3_int }
- {
- \int_set_eq:Nc \l_regex_catcodes_int { c_regex_catcode_#3_int }
- \l_regex_build_mode_int
- = \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- }
- }
- { \cs_if_exist_use:cF { regex_build_c_#3: } }
- {
- \msg_kernel_error:nnx { regex } { c-missing-category } {#3}
- #2 #3
- }
+ \if_int_compare:w \l_regex_mode_int = \c_two
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ \fi:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_compile_class:TFNN
+ { \regex_item_catcode:nT {#1} }
+ { \regex_item_catcode_reverse:nT {#1} }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}+\regex_build_c_{:+ ^^A }
-% The case of a left brace is easy, based on what we have done so far:
-% in a group, build the regular expression, after changing the mode to
-% forbid nesting |\c|.
+% \begin{macro}[aux]
+% {\regex_compile_class:TFNN, \regex_compile_class_ii:NN}
+% If the first character is |^|, then the class is negative (use
+% |#2|), otherwise it is positive (use |#1|). If the next character
+% is a right bracket, then it should be changed to a raw one.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_c_ \c_lbrace_str : }
+\cs_new_protected:Npn \regex_compile_class:TFNN #1#2#3#4
{
- \group_begin:
- \l_regex_build_mode_int
- = - \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- \regex_build:w
+ \l_regex_mode_int = \int_value:w \l_regex_mode_int 3 \exp_stop_f:
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ^ }
+ {
+ \tl_build_one:n { #2 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN
+ }
+ {
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN #3 #4
+ }
+ }
+\cs_new_protected:Npn \regex_compile_class_ii:NN #1#2
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_raw:N #2 }
+ { #1 #2 }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_c_[:}
-% \begin{macro}[aux]{\regex_build_c_lbrack_loop:NN}
-% \begin{macro}[aux]{\regex_build_c_lbrack_end:}
-% \begin{macro}[aux]{\regex_build_add:N}
-% When encountering |\c[|, the task is to collect uppercase letters
-% representing character categories.
+% \begin{macro}[aux]
+% {
+% \regex_compile_class_posix_test:w,
+% \regex_compile_class_posix:NNNNw,
+% \regex_compile_class_posix_loop:w,
+% \regex_compile_class_posix_end:w
+% }
+% Here we check for a syntax such as |[:alpha:]|. We also detect |[=|
+% and |[.| which have a meaning in \textsc{posix} regular expressions,
+% but are not implemented in \pkg{l3regex}. In case we see |[:|, grab
+% raw characters until hopefully reaching |:]|. If that's missing, or
+% the \textsc{posix} class is unknown, abort. If all is right, add the
+% test to the current class, with an extra \cs{regex_item_reverse:n}
+% for negative classes.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_c_[: } #1#2
+\cs_new_protected:Npn \regex_compile_class_posix_test:w #1#2
{
- \int_zero:N \l_regex_catcodes_int
- \str_if_eq:nnTF { #1 #2 } { \regex_build_special:N ^ }
- {
- \bool_set_false:N \l_regex_catcodes_bool
- \regex_build_c_lbrack_loop:NN
- }
+ \token_if_eq_meaning:NNT \regex_compile_special:N #1
{
- \bool_set_true:N \l_regex_catcodes_bool
- \regex_build_c_lbrack_loop:NN
- #1#2
+ \prg_case_str:nnn { #2 }
+ {
+ : { \regex_compile_class_posix:NNNNw }
+ = { \msg_kernel_warning:nnx { regex } { posix-unsupported } { = } }
+ . { \msg_kernel_warning:nnx { regex } { posix-unsupported } { . } }
+ }
+ { }
}
+ \regex_compile_raw:N [ #1 #2
}
-\cs_new_protected:Npn \regex_build_c_lbrack_loop:NN #1#2
+\cs_new_protected:Npn \regex_compile_class_posix:NNNNw #1#2#3#4#5#6
{
- \token_if_eq_meaning:NNTF #1 \regex_build_raw:N
+ \str_if_eq:nnTF { #5 #6 } { \regex_compile_special:N ^ }
{
- \cs_if_exist:cTF { c_regex_catcode_#2_int }
- {
- \exp_args:Nc \regex_build_c_lbrack_add:N
- { c_regex_catcode_#2_int }
- \regex_build_c_lbrack_loop:NN
- }
+ \bool_set_false:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w
}
- { % [
- \token_if_eq_charcode:NNTF #2 ]
- { \regex_build_c_lbrack_end: }
+ {
+ \bool_set_true:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w #5 #6
}
- {
- \msg_kernel_error:nnx { regex } { c-missing-rbrack } {#2}
- \regex_build_c_lbrack_end:
- }
- }
-\cs_new_protected_nopar:Npn \regex_build_c_lbrack_end:
- {
- \l_regex_build_mode_int
- = \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- \if_meaning:w \c_false_bool \l_regex_catcodes_bool
- \int_set:Nn \l_regex_catcodes_int
- { \c_regex_catcodes_all_int - \l_regex_catcodes_int }
- \fi:
}
-\cs_new_protected:Npn \regex_build_c_lbrack_add:N #1
+\cs_new:Npn \regex_compile_class_posix_loop:w #1#2
{
- \if_int_odd:w \int_eval:w \l_regex_catcodes_int / #1 \int_eval_end:
- \else:
- \tex_advance:D \l_regex_catcodes_int #1
- \fi:
+ \token_if_eq_meaning:NNTF \regex_compile_raw:N #1
+ { #2 \regex_compile_class_posix_loop:w }
+ { \if_false: { \fi: } \regex_compile_class_posix_end:w #1 #2 }
}
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% ^^A{
-% \begin{macro}+\regex_build_}:+
-% Non-escaped right braces are only special if they appear when
-% building the regular expression for a csname. In that case, make
-% sure that any character class and group is closed, anchor the match
-% at the end, add the code for making the match successful, and expand
-% the contents of the regular expression we just built. Otherwise,
-% replace the brace with an escaped brace.
-% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_ \c_rbrace_str : }
+\cs_new_protected:Npn \regex_compile_class_posix_end:w #1#2#3#4
{
- \int_compare:nNnTF \l_regex_build_mode_int < \c_zero
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_special:N : \regex_compile_special:N ] }
{
- \regex_build_exit_class:
- \regex_build_exit_groups:
- \regex_build_escaped:N Z
- \regex_build_end:
- \use:x
+ \cs_if_exist:cTF { regex_posix_ \l_regex_internal_a_tl : }
{
- \group_end:
- \regex_build_one:n
- {
- \regex_item_cs:n
- { \regex_set_aux:N \l_regex_internal_regex }
- }
+ \regex_compile_one:x
+ {
+ \bool_if:NF \l_regex_internal_bool \regex_item_reverse:n
+ \exp_not:c { regex_posix_ \l_regex_internal_a_tl : }
+ }
}
- }
- { \exp_after:wN \regex_build_raw:N \c_rbrace_str }
- }
-% \end{macrocode}
-% \end{macro}
-%
-% \subsubsection{Quantifiers}
-%
-% \begin{macro}[int]{\regex_build_quantifier:w}
-% This looks ahead and finds any quantifier (control character
-% equal to either of |?+*{|). ^^A}
-% When all characters for the quantifier are found, the corresponding
-% function is called.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_quantifier:w #1#2
- {
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
- {
- \cs_if_exist_use:cF { regex_build_quantifier_#2:w }
{
- \regex_build_quantifier_end:nn { } { }
- #1 #2
+ \msg_kernel_warning:nnx { regex } { posix-unknown }
+ { \l_regex_internal_a_tl }
+ \regex_compile_abort_tokens:x
+ {
+ [: \bool_if:NF \l_regex_internal_bool { ^ }
+ \l_regex_internal_a_tl :]
+ }
}
}
{
- \regex_build_quantifier_end:nn { } { }
- #1 #2
+ \msg_kernel_error:nnxx { regex } { posix-missing-close }
+ { [: \l_regex_internal_a_tl } { #2 #4 }
+ \regex_compile_abort_tokens:x { [: \l_regex_internal_a_tl }
+ #1 #2 #3 #4
}
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_quantifier_?:w}
-% \begin{macro}[aux]{\regex_build_quantifier_*:w}
-% \begin{macro}[aux]{\regex_build_quantifier_+:w}
-% For each \enquote{basic} quantifier, |?|, |*|, |+|, feed the correct
-% arguments to \cs{regex_build_quantifier_aux:nnNN}.
-% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_quantifier_?:w }
- { \regex_build_quantifier_aux:nnNN { } { ? } }
-\cs_new_protected_nopar:cpn { regex_build_quantifier_*:w }
- { \regex_build_quantifier_aux:nnNN { } { * } }
-\cs_new_protected_nopar:cpn { regex_build_quantifier_+:w }
- { \regex_build_quantifier_aux:nnNN { } { + } }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
+% \subsubsection{Groups and alternations}
%
-% \begin{macro}[aux]{\regex_build_quantifier_aux:nnNN}
-% Once the \enquote{main} quantifier (\texttt{?}, \texttt{*},
-% \texttt{+} or a braced construction) is found, we check
-% whether it is lazy (followed by a question mark),
-% and calls the appropriate function. Here |#1| holds some extra
-% arguments that the final function needs in the case of braced
-% constructions, and is empty otherwise.
+% \begin{macro}[aux]{\regex_compile_group_begin:N, \regex_compile_group_end:}
+% The contents of a regex group are turned into compiled code in
+% \cs{l_regex_internal_regex}, which ends up with items of the form
+% \cs{regex_branch:n} \Arg{concatenation}. This construction is done
+% using \pkg{l3tl-build} within a \TeX{} group, which automatically
+% makes sure that options (case-sensitivity and default catcode) are
+% reset at the end of the group. The argument |#1| is
+% \cs{regex_group:nnnN} or a variant thereof. A small subtelty to
+% support |\cL(abc)| as a shorthand for |(\cLa\cLb\cLc)|: exit any
+% pending catcode test, save the category code at the start of the
+% group as the default catcode for that group, and make sure that the
+% catcode is restored to the default outside the group.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_quantifier_aux:nnNN #1#2#3#4
+\cs_new_protected:Npn \regex_compile_group_begin:N #1
{
- \str_if_eq:nnTF { #3 #4 } { \regex_build_special:N ? }
- { \regex_build_quantifier_end:nn { #2 #4 } {#1} }
- {
- \regex_build_quantifier_end:nn { #2 } {#1}
- #3 #4
- }
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_mode_quit_c:
+ \tl_set_build:Nw \l_regex_internal_regex
+ \int_set_eq:NN \l_regex_default_catcodes_int \l_regex_catcodes_int
+ \int_incr:N \l_regex_group_level_int
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \regex_compile_group_end:
+ {
+ \if_num:w \l_regex_group_level_int > \c_zero
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \tl_build_one:o \l_regex_internal_regex
+ \exp_after:wN \regex_compile_quantifier:w
+ \else:
+ \msg_kernel_warning:nn { regex } { extra-rparen }
+ \exp_after:wN \regex_compile_raw:N \exp_after:wN )
+ \fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]+\regex_build_quantifier_{:w+ ^^A}
-% \begin{macro}[aux]{\regex_build_quantifier_lbrace:n}
-% \begin{macro}[aux]{\regex_build_quantifier_lbrace:nw}
-% \begin{macro}[aux]{\regex_build_quantifier_lbrace:nnw}
-% Three possible syntaxes: \texttt{\{\meta{int}\}},
-% \texttt{\{\meta{int},\}}, or \texttt{\{\meta{int},\meta{int}\}}.
+% \begin{macro}[aux]{\regex_compile_(:}
+% In a class, parentheses are not special. Outside, check for a |?|,
+% denoting special groups, and run the code for the corresponding
+% special group.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_quantifier_ \c_lbrace_str :w }
- { \regex_get_digits:nw { \regex_build_quantifier_lbrace:n } }
-\cs_new_protected:Npn \regex_build_quantifier_lbrace:n #1
+\cs_new_protected_nopar:cpn { regex_compile_(: }
{
- \tl_if_empty:nTF {#1}
- {
- \regex_build_quantifier_end:nn { } { }
- \exp_after:wN \regex_build_raw:N \c_lbrace_str
- }
- { \regex_build_quantifier_lbrace:nw {#1} }
+ \regex_if_in_class:TF { \regex_compile_raw:N ( }
+ { \regex_compile_lparen:w }
}
-\cs_new_protected:Npx \regex_build_quantifier_lbrace:nw #1#2#3
+\cs_new_protected:Npn \regex_compile_lparen:w #1#2#3#4
{
- \exp_not:N \prg_case_str:nnn { #2 #3 }
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ? }
{
- { \exp_not:N \regex_build_special:N , }
+ \cs_if_exist_use:cF
+ { regex_compile_special_group_\token_to_str:N #4 :w }
{
- \exp_not:N \regex_get_digits:nw
- { \exp_not:N \regex_build_quantifier_lbrace:nnw {#1} }
+ \msg_kernel_warning:nnx { regex } { special-group-unknown }
+ { (? \token_to_str:N #4 }
+ \regex_compile_group_begin:N \regex_group:nnnN
+ \regex_compile_raw:N ? #3 #4
}
- { \exp_not:N \regex_build_special:N \c_rbrace_str }
- { \exp_not:N \regex_build_quantifier_end:nn {n} { {#1} } }
}
{
- \exp_not:N \regex_build_quantifier_end:nn { } { }
- \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2
- }
- }
-\cs_new_protected:Npn \regex_build_quantifier_lbrace:nnw #1#2#3#4
- {
- \str_if_eq:xxTF
- { \exp_not:n {#3#4} }
- { \exp_not:N \regex_build_special:N \c_rbrace_str }
- {
- \tl_if_empty:nTF {#2}
- { \regex_build_quantifier_aux:nnNN { {#1} } { n* } }
- { \regex_build_quantifier_aux:nnNN { {#1} {#2} } { nn } }
- }
- {
- \regex_build_quantifier_end:nn { } { }
- \use:x
- {
- \exp_args:No \tl_map_function:nN
- { \c_lbrace_str #1 , #2 }
- \regex_build_raw:N
- }
- #3 #4
+ \regex_compile_group_begin:N \regex_group:nnnN
+ #1 #2 #3 #4
}
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_quantifier_end:nn}
-% When all quantifiers are found, we will call the relevant
-% \cs{regex_build_one/group_\meta{quantifiers}:} function.
+% \begin{macro}[aux]+\regex_compile_|:+
+% In a class, the pipe is not special. Otherwise, end the current
+% branch and open another one.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_quantifier_end:nn #1#2
+\cs_new_protected_nopar:cpn { regex_compile_|: }
{
- \use:c { regex_build_ \l_regex_one_or_group_tl _ #1 : } #2
- \tl_clear:N \l_regex_tests_tl
- \bool_set_true:N \l_regex_tests_bool
+ \regex_if_in_class:TF { \regex_compile_raw:N | }
+ {
+ \tl_build_one:n
+ { \if_false: { \fi: } \regex_branch:n { \if_false: } \fi: }
+ }
}
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Quantifiers for one character or character class}
-%
-% \begin{macro}[aux]{\regex_build_one_quantifier:}
-% Used for one single character, or a character class.
-% Contrarily to \cs{regex_build_group_quantifier:},
-% we don't need to keep track of submatches, and no thread
-% can be created within one repetition, so things are relatively easy.
+% \begin{macro}[aux]{\regex_compile_):}
+% Within a class, parentheses are not special. Outside, close a group.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_one_quantifier:
+\cs_new_protected_nopar:cpn { regex_compile_): }
{
- \tl_set:Nx \l_regex_one_or_group_tl { one }
- \regex_build_quantifier:w
+ \regex_if_in_class:TF { \regex_compile_raw:N ) }
+ { \regex_compile_group_end: }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_one_n:, \regex_build_one_n?:}
-% \begin{macro}[aux]{\regex_build_one_:}
-% 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. Build one state for each
-% repetition, with a transition controlled by the tests that we have
-% collected. The case where we find no quantifier is the special case
-% $n=1$.
+% \begin{macro}[aux]{\regex_compile_special_group_::w}
+% \begin{macro}[aux]+\regex_compile_special_group_|:w+
+% Non-capturing, and resetting groups are easy to take care of during
+% compilation; for those groups, the harder parts will come when
+% building.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_one_n: #1
- {
- \prg_replicate:nn {#1}
- {
- \regex_build_transition:NN
- \regex_tests_action_cost:n \l_regex_right_state_int
- }
- }
-\cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n:
-\cs_new_protected_nopar:Npn \regex_build_one_:
- { \regex_build_one_n: \c_one }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_::w }
+ { \regex_compile_group_begin:N \regex_group_no_capture:nnnN }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_|:w }
+ { \regex_compile_group_begin:N \regex_group_resetting:nnnN }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_one_n*:, \regex_build_one_n*?:}
-% \begin{macro}[aux]{\regex_build_one_*:, \regex_build_one_*?:}
-% \begin{macro}[aux]{\regex_build_one_+:, \regex_build_one_+?:}
-% Those functions are called in case the syntax is
-% \texttt{\{\meta{int},\}}. The |*| and |+| quantifiers (greedy or
-% lazy) are special cases for $n=0$ or $n=1$. In the case $n=0$,
-% build a costly transition going from the current state to itself,
-% and a free transition moving to a new state. In the case $n\geq 1$,
-% reuse \cs{regex_build_one_n:} to match $n$ repetition, then add free
-% transitions from the last state to the previous one, and from the
-% last state to a new one. The greedy and lazy versions only differ in
-% the order of transitions.
+% \begin{macro}[aux]
+% {\regex_compile_special_group_i:w, \regex_compile_special_group_-:w}
+% The match can be made case-insensitive by setting the option with
+% \texttt{(?i)}; the original behaviour is restored by \texttt{(?-i)}.
+% This is the only supported option.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_one_n*: } #1
+\cs_new_protected:Npn \regex_compile_special_group_i:w #1#2
{
- \int_compare:nNnTF {#1} = \c_zero
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ) }
{
- \regex_build_transitions:NNNN
- \regex_tests_action_cost:n \l_regex_left_state_int
- \regex_action_free:n \l_regex_right_state_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseless_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseless_range:nn }
}
{
- \regex_build_one_n: {#1}
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_internal_a_int
- \regex_action_free:n \l_regex_right_state_int
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?i #2 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N i
+ #1 #2
}
}
-\cs_new_protected:cpn { regex_build_one_n*?: } #1
+\cs_new_protected_nopar:cpn { regex_compile_special_group_-:w } #1#2#3#4
{
- \int_compare:nNnTF {#1} = \c_zero
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_raw:N i \regex_compile_special:N ) }
{
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_right_state_int
- \regex_tests_action_cost:n \l_regex_left_state_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
}
{
- \regex_build_one_n: {#1}
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_right_state_int
- \regex_action_free:n \l_regex_internal_a_int
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?-#2#4 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N -
+ #1 #2 #3 #4
}
}
-\cs_new_protected_nopar:cpx { regex_build_one_*: }
- { \exp_not:c { regex_build_one_n*: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_one_*?: }
- { \exp_not:c { regex_build_one_n*?: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_one_+: }
- { \exp_not:c { regex_build_one_n*: } \c_one }
-\cs_new_protected_nopar:cpx { regex_build_one_+?: }
- { \exp_not:c { regex_build_one_n*?: } \c_one }
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_one_nn:, \regex_build_one_nn?:}
-% \begin{macro}[aux]{\regex_build_one_?:, \regex_build_one_??:}
-% \begin{macro}[aux]{\regex_build_one_nn_aux:nnnn}
-% Those functions are called when the syntax is
-% \texttt{\{\meta{int},\meta{int}\}}, or for the |?| quantifier, with
-% $m=0$ and $n=1$.
+% \subsubsection{Catcodes and csnames}
+%
+% \begin{macro}[aux]{\regex_compile_/c:, \regex_compile_c_test:NN}
+% The |\c| escape sequence can be followed by a capital letter
+% representing a character category, by a left bracket which starts a
+% list of categories, or by a brace group holding a regular expression
+% for a control sequence name. Otherwise, raise an error.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_one_nn:
- {
- \regex_build_one_nn_aux:nnnn { }
- {
- \regex_build_transitions:NNNN
- \regex_tests_action_cost:n \l_regex_right_state_int
- \regex_action_free:n \l_regex_internal_a_int
- }
- }
-\cs_new_protected_nopar:cpn { regex_build_one_nn?: }
+\cs_new_protected:cpn { regex_compile_/c: }
+ { \regex_chk_c_allowed:T { \regex_compile_c_test:NN } }
+\cs_new_protected:Npn \regex_compile_c_test:NN #1#2
{
- \regex_build_one_nn_aux:nnnn {?}
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
{
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_internal_a_int
- \regex_tests_action_cost:n \l_regex_right_state_int
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \int_set_eq:Nc \l_regex_catcodes_int { c_regex_catcode_#2_int }
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ }
}
+ { \cs_if_exist_use:cF { regex_compile_c_#2:w } }
+ {
+ \msg_kernel_error:nnx { regex } { c-missing-category } {#2}
+ #1 #2
+ }
}
-\cs_new_protected:Npn \regex_build_one_nn_aux:nnnn #1#2#3#4
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]
+% {
+% \regex_compile_c_[:w,
+% \regex_compile_c_lbrack_loop:NN,
+% \regex_compile_c_lbrack_add:N,
+% \regex_compile_c_lbrack_end:,
+% }
+% When encountering |\c[|, the task is to collect uppercase letters
+% representing character categories. First check for |^| which negates
+% the list of category codes.
+% \begin{macrocode}
+\cs_new_protected:cpn { regex_compile_c_[:w } #1#2
{
- \regex_build_one_n: {#3}
- \int_compare:nNnTF {#3} > {#4}
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ \int_zero:N \l_regex_catcodes_int
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ^ }
{
- \msg_kernel_error:nnxxx
- { regex } { quantifier-backwards } {#3} {#4} {#1}
+ \bool_set_false:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
}
{
- \int_set:Nn \l_regex_internal_a_int
- { \l_regex_right_state_int + #4 - #3 }
- \prg_replicate:nn { #4 - #3 } { #2 }
+ \bool_set_true:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
+ #1 #2
}
}
-\cs_new_protected_nopar:cpn { regex_build_one_?: }
- { \regex_build_one_nn: \c_zero \c_one }
-\cs_new_protected_nopar:cpx { regex_build_one_??: }
- { \exp_not:c { regex_build_one_nn?: } \c_zero \c_one }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Groups and alternation}
-%
-% 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:}
-% \begin{macro}[aux]+\regex_build_|:+
-% \begin{macro}[aux]{\regex_build_begin_alternation:,
-% \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.
-% \end{itemize}
-% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_(: } #1#2
+\cs_new_protected:Npn \regex_compile_c_lbrack_loop:NN #1#2
{
- \regex_build_if_in_class:TF
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
{
- \regex_build_raw:N (
- #1 #2
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \exp_args:Nc \regex_compile_c_lbrack_add:N
+ { c_regex_catcode_#2_int }
+ \regex_compile_c_lbrack_loop:NN
+ }
}
{
- \str_if_eq:nnTF { #1 #2 } { \regex_build_special:N ? }
- { \regex_build_special_group:NN }
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_c_lbrack_end: }
+ }
{
- \int_incr:N \l_regex_capturing_group_int
- \regex_seq_push_int:NN
- \l_regex_capturing_group_seq \l_regex_capturing_group_int
- \regex_build_open_aux:
+ \msg_kernel_error:nnx { regex } { c-missing-rbrack } {#2}
+ \regex_compile_c_lbrack_end:
#1 #2
}
- }
}
-\cs_new_protected_nopar:Npn \regex_build_open_aux:
+\cs_new_protected:Npn \regex_compile_c_lbrack_add:N #1
{
- \regex_build_new_state:
- \regex_seq_push_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_push_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \bool_if:NTF \l_regex_caseless_bool
- { \seq_push:Nn \l_regex_end_group_seq \regex_build_caseless: }
- { \seq_push:Nn \l_regex_end_group_seq \regex_build_caseful: }
- \seq_push:Nn \l_regex_end_alternation_seq { }
- \regex_build_begin_alternation:
+ \if_int_odd:w \int_eval:w \l_regex_catcodes_int / #1 \int_eval_end:
+ \else:
+ \tex_advance:D \l_regex_catcodes_int #1
+ \fi:
}
-\cs_new_protected_nopar:cpn { regex_build_|: }
+\cs_new_protected_nopar:Npn \regex_compile_c_lbrack_end:
{
- \regex_build_if_in_class:TF { \regex_build_raw:N | }
- {
- \regex_build_end_alternation:
- \regex_build_begin_alternation:
- }
+ \if_meaning:w \c_false_bool \l_regex_catcodes_bool
+ \int_set:Nn \l_regex_catcodes_int
+ { \c_regex_all_catcodes_int - \l_regex_catcodes_int }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\regex_compile_c_{:+
+% The case of a left brace is easy, based on what we have done so far:
+% in a group, compile the regular expression, after changing the mode
+% to forbid nesting |\c|. Additionally, disable submatch tracking
+% since groups don't escape the scope of |\c{...}|.
+% \begin{macrocode}
+\cs_new_protected_nopar:cpn { regex_compile_c_ \c_lbrace_str :w }
+ {
+ \regex_compile:w
+ \regex_disable_submatches:
+ \l_regex_mode_int
+ = - \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
}
-\cs_new_protected_nopar:cpn { regex_build_): }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\regex_compile_}:+
+% Non-escaped right braces are only special if they appear when
+% compiling the regular expression for a csname, but not within a
+% class: |\c{[}{]}| matches the control sequences |\}| and
+% |\{|\ldots{} Admittedly, that would be better done as
+% |\c{[{}]}|. So, end compiling the inner regex (this closes any
+% dangling class or group). Then insert the corresponding test in the
+% outer regex.
+% \begin{macrocode}
+\cs_new_protected:cpn { regex_compile_ \c_rbrace_str : }
{
- \regex_build_if_in_class:TF { \regex_build_raw:N ) }
+ \regex_if_in_cs:TF
{
- \seq_if_empty:NTF \l_regex_capturing_group_seq
- { \msg_kernel_error:nn { regex } { extra-rparen } }
- {
- \regex_build_close_aux:
- \regex_build_group_quantifier:
- }
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
}
- }
-\cs_new_protected_nopar:Npn \regex_build_close_aux:
- {
- \regex_build_end_alternation:
- \regex_seq_pop_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_pop_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \regex_seq_pop_use:N \l_regex_end_group_seq
- \seq_pop:NN \l_regex_end_alternation_seq \l_regex_internal_a_tl
+ { \exp_after:wN \regex_compile_raw:N \c_rbrace_str }
}
% \end{macrocode}
-% Building each branch.
+% \end{macro}
+%
+% \subsubsection{Raw token lists with \cs{u}}
+%
+% \begin{macro}[aux]{\regex_compile_/u:}
+% \begin{macro}[aux, EXP]{\regex_compile_u_loop:NN}
+% The |\u| escape is invalid in classes and directly following a
+% catcode test. Otherwise, it must be followed by a left brace. We
+% then collect the characters for the argument of |\u| within an
+% \texttt{x}-expanding assignment. In principle we could just wait to
+% encounter a right brace, but this is unsafe: if the right brace is
+% missing, then we will reach the end-markers of the regex, and
+% continue, leading to obscure fatal errors. Instead, we only allow
+% raw and special characters, and stop when encountering a special
+% right brace, any escaped character, or the end-marker.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_begin_alternation:
+\cs_new_protected:cpn { regex_compile_/u: } #1#2
{
- \regex_build_new_state:
- \regex_seq_get_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_toks_put_right:Nx \l_regex_left_state_int
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N u #1 #2 }
{
- \regex_action_free:n
+ \str_if_eq:xxTF {#1#2} { \regex_compile_special:N \c_lbrace_str }
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_u_loop:NN
+ }
+ {
+ \msg_kernel_error:nn { regex } { u-missing-lbrace }
+ \regex_compile_raw:N u #1 #2
}
}
}
-\cs_new_protected_nopar:Npn \regex_build_end_alternation:
+\cs_new:Npn \regex_compile_u_loop:NN #1#2
{
- \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
- \regex_seq_get_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \regex_toks_put_right:Nx \l_regex_left_state_int
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
+ { #2 \regex_compile_u_loop:NN }
{
- \regex_action_free:n
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
+ {
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #2
+ { \if_false: { \fi: } \regex_compile_u_end: }
+ { #2 \regex_compile_u_loop:NN }
+ }
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
+ \if_false: { \fi: }
+ \msg_kernel_error:nnx { regex } { u-missing-rbrace } {#2}
+ \regex_compile_u_end:
+ #1 #2
}
}
- \regex_seq_get_use:N \l_regex_end_alternation_seq
}
% \end{macrocode}
% \end{macro}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}{\regex_build_special_group:NN}
-% Same method as elsewhere: if the combination |(?#1| ^^A )
-% is known, then use that. Otherwise, treat the question mark
-% as if it had been escaped.
+% \begin{macro}[aux]{\regex_compile_u_end:}
+% Once we have extracted the variable's name, we store the contents of
+% that variable in \cs{l_regex_internal_a_tl}. The behaviour of |\u|
+% then depends on whether we are within a |\c{...}| escape (in this
+% case, the variable is turned to a string), or not.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_special_group:NN #1#2
+\cs_new_protected:Npn \regex_compile_u_end:
{
- \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : }
- {
- \msg_kernel_error:nnx { regex } { special-group-unknown }
- { (? \token_to_str:N #2 } %)
- \regex_build_special:N ( % )
- \regex_build_raw:N ?
- #1 #2
- }
+ \tl_set:Nv \l_regex_internal_a_tl { \l_regex_internal_a_tl }
+ \if_num:w \l_regex_mode_int = \c_zero
+ \regex_compile_u_not_cs:
+ \else:
+ \regex_compile_u_in_cs:
+ \fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_build_special_group_::}
-% Non-capturing groups are like capturing groups, except that
-% we set the group id to \texttt{-1}, which will then inhibit
-% submatching in \cs{regex_build_group_submatches:NN}.
-% The group number is not increased.
+% \begin{macro}[aux]{\regex_compile_u_in_cs:}
+% When |\u| appears within a control sequence, we convert the variable
+% to a string with escaped spaces. Then for each character insert a
+% class matching exactly that character, once.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_special_group_:: }
+\cs_new_protected:Npn \regex_compile_u_in_cs:
{
- \regex_seq_push_int:NN \l_regex_capturing_group_seq \c_minus_one
- \regex_build_open_aux:
+ \exp_args:NNo \str_gset_other:Nn \g_regex_internal_tl
+ { \l_regex_internal_a_tl }
+ \tl_build_one:x
+ {
+ \tl_map_function:NN \g_regex_internal_tl
+ \regex_compile_u_in_cs_aux:n
+ }
+ }
+\cs_new:Npn \regex_compile_u_in_cs_aux:n #1
+ {
+ \regex_class:NnnnN \c_true_bool
+ { \regex_item_caseful_equal:n { \int_value:w `#1 } }
+ { 1 } { 0 } \c_false_bool
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}+\regex_build_special_group_|:+
-% The special group \verb"(?|..|..)" is non-capturing
-% (hence we set the |capturing_group| to $-1$), and resets
-% the group number in each branch of the alternation.
-% We use a variant of \cs{regex_build_open_aux:}, adding
-% some code to be performed at every alternation, and at
-% the end of the group. Namely, we keep track of
-% the maximal value that \cs{l_regex_capturing_group_int}
-% takes, and restore that value when the group end,
-% and in every branch, we reset the capturing group number.
+% \begin{macro}[aux]{\regex_compile_u_not_cs:}
+% In mode $0$, the |\u| escape adds one state to the NFA for each
+% token in \cs{l_regex_internal_a_tl}. If a given \meta{token} is a
+% control sequence, then insert a string comparison test, otherwise,
+% \cs{regex_item_exact:nn} which compares catcode and character code.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_special_group_|: }
+\cs_new_protected:Npn \regex_compile_u_not_cs:
{
- \regex_seq_push_int:NN \l_regex_capturing_group_seq \c_minus_one
- \regex_build_new_state:
- \regex_seq_push_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_push_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \seq_push:Nx \l_regex_end_alternation_seq
+ \exp_args:No \tl_analysis_map_inline:nn { \l_regex_internal_a_tl }
{
- \exp_not:N \int_compare:nNnT
- \l_regex_capturing_group_int
- > \l_regex_capturing_group_max_int
+ \tl_build_one:n
{
- \int_set_eq:NN
- \l_regex_capturing_group_max_int
- \l_regex_capturing_group_int
+ \regex_class:NnnnN \c_true_bool
+ {
+ \if_num:w "##2 = \c_zero
+ \regex_item_exact_cs:c { \exp_after:wN \cs_to_str:N ##1 }
+ \else:
+ \regex_item_exact:nn { \int_value:w "##2 } { ##3 }
+ \fi:
+ }
+ { 1 } { 0 } \c_false_bool
}
- \int_set:Nn \l_regex_capturing_group_int
- { \int_use:N \l_regex_capturing_group_int }
}
- \seq_push:Nx \l_regex_end_group_seq
- {
- \bool_if:NTF \l_regex_caseless_bool
- \regex_build_caseless:
- \regex_build_caseful:
- \int_set_eq:NN
- \l_regex_capturing_group_int
- \l_regex_capturing_group_max_int
- }
- \regex_build_begin_alternation:
}
% \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)}.
+% \subsubsection{Other}
+%
+% \begin{macro}[aux]{\regex_compile_/K:}
+% The |\K| control sequence is currently the only \enquote{command},
+% which performs some action, rather than matching something. It is
+% allowed in the same contexts as |\b|. At the compilation stage, we
+% leave it as a single control sequence, defined later.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_special_group_i:
+\cs_new_protected_nopar:cpn { regex_compile_/K: }
{
- \regex_build_options:NNN +
- \regex_build_raw:N i
+ \int_compare:nNnTF \l_regex_mode_int = \c_zero
+ { \tl_build_one:n { \regex_command_K: } }
+ { \regex_compile_raw_error:N K }
}
-\cs_new_protected_nopar:cpn { regex_build_special_group_-: }
- {
- \regex_build_options:NNN -
- }
-\cs_new_protected:Npn \regex_build_options:NNN #1#2#3
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Showing regexes}
+%
+% \begin{macro}[aux]{\regex_show_aux:Nx}
+% Within a \cs{tl_set_build:Nw} \ldots{} \cs{tl_build_end:} group, we
+% redefine all the function that can appear in a compiled regex, then
+% run the regex. The result is then shown.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_show_aux:Nx #1#2
{
- \token_if_eq_meaning:NNTF \regex_build_raw:N #2
- {
- \cs_if_exist_use:cF { regex_build_option_#1#3: }
- { \msg_kernel_error:nnx { regex } { unknown-option } { #3 } }
- \regex_build_options:NNN #1
- }
- {
- \prg_case_str:nnn { #3 }
- { % (
- { ) } { }
- { - } { \regex_build_options:NNN - }
- }
- { \msg_kernel_error:nnx { regex } { invalid-in-option } { #3 } }
- }
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \cs_set_protected_nopar:Npn \regex_branch:n
+ {
+ \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \regex_show_one:n { +-branch }
+ \seq_put_right:No \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \use:n
+ }
+ \cs_set_protected_nopar:Npn \regex_group:nnnN
+ { \regex_show_group_aux:nnnnN { } }
+ \cs_set_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_show_group_aux:nnnnN { ~(no~capture) } }
+ \cs_set_protected_nopar:Npn \regex_group_resetting:nnnN
+ { \regex_show_group_aux:nnnnN { ~(resetting) } }
+ \cs_set_eq:NN \regex_class:NnnnN \regex_show_class:NnnnN
+ \cs_set_protected_nopar:Npn \regex_command_K:
+ { \regex_show_one:n { reset~match~start~(\iow_char:N\\K) } }
+ \cs_set_protected:Npn \regex_assertion:Nn ##1##2
+ { \regex_show_one:n { \bool_if:NF ##1 { negative~ } assertion:~##2 } }
+ \cs_set_nopar:Npn \regex_b_test: { word~boundary }
+ \cs_set_eq:NN \regex_anchor:N \regex_show_anchor_to_str:N
+ \cs_set_protected:Npn \regex_item_caseful_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1} } }
+ \cs_set_protected:Npn \regex_item_caseful_range:nn ##1##2
+ { \regex_show_one:n { range~[\int_eval:n{##1}, \int_eval:n{##2}] } }
+ \cs_set_protected:Npn \regex_item_caseless_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \regex_item_caseless_range:nn ##1##2
+ {
+ \regex_show_one:n
+ { Range~[\int_eval:n{##1}, \int_eval:n{##2}]~(caseless) }
+ }
+ \cs_set_protected:Npn \regex_item_catcode:nT
+ { \regex_show_item_catcode:NnT \c_true_bool }
+ \cs_set_protected:Npn \regex_item_catcode_reverse:nT
+ { \regex_show_item_catcode:NnT \c_false_bool }
+ \cs_set_protected:Npn \regex_item_reverse:n
+ { \regex_show_scope:nn { Reversed~match } }
+ \cs_set_protected:Npn \regex_item_exact:nn ##1##2
+ { \regex_show_one:n { char~##2,~catcode~##1 } }
+ \cs_set_protected:Npn \regex_item_exact_cs:c ##1
+ { \regex_show_one:n { control~sequence~\iow_char:N\\##1 } }
+ \cs_set_protected:Npn \regex_item_cs:n
+ { \regex_show_scope:nn { control~sequence } }
+ \cs_set:cpn { regex_prop_.: } { \regex_show_one:n { any~token } }
+ \seq_clear:N \l_regex_show_prefix_seq
+ \regex_show_push:n { ~ }
+ #1
+ \tl_build_end:
+ \msg_aux_show:x { > Compiled~regex~#2: \l_regex_internal_a_tl }
}
-\cs_new_protected_nopar:cpn { regex_build_option_+i: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_show_one:n}
+% Every part of the final message go through this function, which adds
+% one line to the output, with the apropriate prefix.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_show_one:n #1
{
- \regex_build_caseless:
- \cs_set_eq:NN \regex_match_loop_case_hook:
- \regex_match_loop_caseless_hook:
+ \int_incr:N \l_regex_show_lines_int
+ \tl_build_one:x
+ { \iow_newline: \seq_use:N \l_regex_show_prefix_seq #1 }
}
-\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:}
-% Used for one group. We need to keep track of submatches,
-% threads can be created within one repetition, so things are hard.
-% The code for the group that was just built starts
-% at \cs{l_regex_left_state_int} and ends at
-% \cs{l_regex_right_state_int}.
+% \begin{macro}[aux]
+% {\regex_show_push:n, \regex_show_pop:, \regex_show_scope:nn}
+% Enter and exit levels of nesting. The \texttt{scope} function prints
+% its first argument as an \enquote{introduction}, then performs its
+% second argument in a deeper level of nesting.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_group_quantifier:
+\cs_new_protected:Npn \regex_show_push:n #1
+ { \seq_put_right:Nx \l_regex_show_prefix_seq { #1 ~ } }
+\cs_new_protected:Npn \regex_show_pop:
+ { \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl }
+\cs_new_protected:Npn \regex_show_scope:nn #1#2
{
- \tl_set:Nn \l_regex_one_or_group_tl { group }
- \regex_build_quantifier:w
+ \regex_show_one:n {#1}
+ \regex_show_push:n { ~ }
+ #2
+ \regex_show_pop:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_group_submatches:NN}
-% Once the quantifier is found by \cs{regex_build_quantifier:w},
-% we insert the code for tracking submatches.
+% \begin{macro}[aux]{\regex_show_group_aux:nnnnN}
+% We display all groups in the same way, simply adding a message,
+% \texttt{(no capture)} or \texttt{(resetting)}, to special groups.
+% The odd \cs{use_ii:nn} avoids printing a spurious \texttt{+-branch}
+% for the first branch.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_group_submatches:NN #1#2
+\cs_new_protected:Npn \regex_show_group_aux:nnnnN #1#2#3#4#5
{
- \seq_pop:NN \l_regex_capturing_group_seq \l_regex_internal_a_tl
- \int_compare:nNnF { \l_regex_internal_a_tl } < \c_zero
- {
- \regex_toks_put_left:Nx #1
- { \regex_action_submatch:n { \l_regex_internal_a_tl < } }
- \regex_toks_put_left:Nx #2
- { \regex_action_submatch:n { \l_regex_internal_a_tl > } }
- }
+ \regex_show_one:n { ,-group~begin #1 }
+ \regex_show_push:n { | }
+ \use_ii:nn #2
+ \regex_show_pop:
+ \regex_show_one:n
+ { `-group~end \regex_msg_repeated:nnN {#3} {#4} #5 }
}
% \end{macrocode}
% \end{macro}
%
-% Most quantifiers require to add an extra state before the group.
-% This is done by shifting the current contents of the \cs{tex_toks:D}
-% \cs{l_regex_internal_a_int} to a new state.
-%
-% \begin{macro}[aux]{\regex_build_group_qs_aux:NN}
-% \begin{macro}[aux]{\regex_build_group_?:}
-% \begin{macro}[aux]{\regex_build_group_??:}
-% Shift the state at which the group begins using
-% \cs{regex_build_group_shift:N}, then add two transitions.
-% The first transition is taken once the group has been
-% traversed: in the case of \texttt{?} and \texttt{??},
-% we should exit by going to \cs{l_regex_right_state_int},
-% while for \texttt{*} and \texttt{*?} we loop by going to
-% \cs{l_regex_internal_a_int}.
-% The second transition corresponds to skipping the group;
-% it has lower priority (\texttt{put_right}) for greedy
-% operators, and higher priority (\texttt{put_left}) for
-% lazy operators.
-% We shift the left state to avoid interactions with
-% \texttt{action_submatch}.
+% \begin{macro}[aux]{\regex_show_class:NnnnN}
+% I'm entirely unhappy about this function: I couldn't find a way to
+% test if a class is a single test. Instead, collect the
+% representation of the tests in the class. If that had more than one
+% line, write \texttt{Match} or \texttt{Don't match} on its own line,
+% with the repeating information if any. Then the various tests on
+% lines of their own, and finally a line. Otherwise, we need to
+% evaluate the representation of the tests again (since the prefix is
+% incorrect). That's clunky, but not too expensive, since it's only
+% one test.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_group_qs_aux:
+\cs_set:Npn \regex_show_class:NnnnN #1#2#3#4#5
{
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_new_state:
- \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_internal_a_int
- \regex_toks_put_left:Nx \l_regex_right_state_int
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_show_lines_int
+ \regex_show_push:n {~}
+ #2
+ \exp_last_unbraced:Nf
+ \prg_case_int:nnn { \l_regex_show_lines_int }
{
- \int_sub:Nn \l_regex_current_state_int
+ {0}
{
- \int_eval:n % ^^A here we lie!
- { \l_regex_right_state_int - \l_regex_internal_a_int }
+ \tl_build_end:
+ \regex_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
}
- }
- \use:x
- {
- \tex_toks:D \l_regex_internal_a_int
+ {1}
{
- \s_stop
- \regex_action_free:n
+ \tl_build_end:
+ \bool_if:NTF #1
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_internal_a_int }
+ #2
+ \tl_build_one:n { \regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+ {
+ \regex_show_one:n
+ { Don't~match~\regex_msg_repeated:nnN {#3} {#4} #5 }
+ \tl_build_one:o \l_regex_internal_a_tl
}
}
}
- \regex_build_group_submatches:NN
- \l_regex_right_state_int \l_regex_left_state_int
- }
-\cs_new_protected:Npn \regex_build_group_qs_aux:NN #1#2
- {
- \regex_build_group_qs_aux:
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \regex_build_transition:NN \regex_action_free:n #1
- #2 \l_regex_internal_a_int
{
- \regex_action_free:n
- { \int_eval:n { \l_regex_right_state_int - \l_regex_internal_a_int } }
+ \tl_build_end:
+ \regex_show_one:n
+ {
+ \bool_if:NTF #1 { M } { Don't~m } atch
+ \regex_msg_repeated:nnN {#3} {#4} #5
+ }
+ \tl_build_one:o \l_regex_internal_a_tl
}
}
-\cs_new_protected_nopar:cpn { regex_build_group_?: }
- {
- \regex_build_group_qs_aux:NN
- \l_regex_right_state_int \regex_toks_put_right:Nx
- }
-\cs_new_protected_nopar:cpn { regex_build_group_??: }
- {
- \regex_build_group_qs_aux:NN
- \l_regex_right_state_int \regex_toks_put_left:Nx
- }
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_group_n_aux:n}
-% \begin{macro}[aux]{\regex_build_group_transition_right_max:}
-% 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_internal_a_int} points to the last copy of
-% the initial left-most state, \cs{l_regex_left/right_state_int} have
-% their initial values. Furthermore, \cs{l_regex_max_state_int} is set
-% appropriately to the largest allocated \tn{toks} register plus $1$.
+% \begin{macro}[aux, rEXP]{\regex_show_anchor_to_str:N}
+% The argument is an integer telling us where the anchor is. We
+% convert that to the relevant info.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_group_transition_right_max:
+\cs_new:Npn \regex_show_anchor_to_str:N #1
{
- \regex_toks_put_right:Nx \l_regex_right_state_int
+ anchor~at~
+ \prg_case_str:nnn { #1 }
{
- \regex_action_free:n
- { \int_eval:n { \l_regex_max_state_int - \l_regex_right_state_int } }
+ { \l_regex_min_pos_int } { start~(\iow_char:N\\A) }
+ { \l_regex_start_pos_int } { start~of~match~(\iow_char:N\\G) }
+ { \l_regex_max_pos_int } { end~(\iow_char:N\\Z) }
}
+ { <error:~'#1'~not~recognized> }
}
-\cs_new_protected:Npn \regex_build_group_n_aux:n #1
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_show_item_catcode:NnT}
+% Produce a sequence of categories which the catcode bitmap |#2|
+% contains, and show it, indenting the tests on which this catcode
+% constraint applies.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_show_item_catcode:NnT #1#2
{
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \int_set_eq:NN \l_regex_internal_b_int \l_regex_max_state_int
- \int_add:Nn \l_regex_right_state_int
- { ( #1 - 1 ) * ( \l_regex_max_state_int - \l_regex_left_state_int ) }
- \int_set:Nn \l_regex_max_state_int
- {
- \l_regex_left_state_int
- + (#1) * ( \l_regex_max_state_int - \l_regex_left_state_int )
- }
- \int_while_do:nNnn \l_regex_internal_b_int < \l_regex_max_state_int
+ \seq_set_split:Nnn \l_regex_internal_seq { } { CBEMTPUDSLOA }
+ \seq_set_filter:NNn \l_regex_internal_seq \l_regex_internal_seq
+ { \int_if_odd_p:n { #2 / \int_use:c { c_regex_catcode_##1_int } } }
+ \regex_show_scope:nn
{
- \tex_toks:D \l_regex_internal_b_int
- = \tex_toks:D \l_regex_internal_a_int
- \int_incr:N \l_regex_internal_a_int
- \int_incr:N \l_regex_internal_b_int
+ categories~\seq_use:N \l_regex_internal_seq, ~
+ \bool_if:NF #1 { negative~ } class
}
}
% \end{macrocode}
% \end{macro}
+%
+% \subsection{Building}
+%
+% \subsubsection{Variables used while building}
+%
+% \begin{variable}{\l_regex_min_state_int, \l_regex_max_state_int}
+% The last state that was allocated is $\cs{l_regex_max_state_int}-1$,
+% so that \cs{l_regex_max_state_int} always points to a free state.
+% The \texttt{min_state} variable is always $0$, but is included to
+% avoid hard-coding this value.
+% \begin{macrocode}
+\int_new:N \l_regex_min_state_int
+\int_new:N \l_regex_max_state_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_left_state_int, \l_regex_right_state_int}
+% \begin{variable}{\l_regex_left_state_seq, \l_regex_right_state_seq}
+% Alternatives are implemented by branching from a \texttt{left} state
+% into the various choices, then merging those into a \texttt{right}
+% state. We store information about those states in two sequences.
+% Those states are also used to implement group quantifiers. Most
+% often, the left and right pointers only differ by~$1$.
+% \begin{macrocode}
+\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_capturing_group_int}
+% \cs{l_regex_capturing_group_int} is the \textsc{id} number that will
+% be assigned to a capturing group if one was opened now. This starts
+% at $0$ for the group enclosing the full regular expression, and
+% groups are counted in the order of their left parenthesis, except
+% when encountering \texttt{resetting} groups.
+% \begin{macrocode}
+\int_new:N \l_regex_capturing_group_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Framework}
+%
+% This phase is about going from a compiled regex to an \textsc{nfa}.
+% Each state of the \textsc{nfa} is stored in a \tn{toks}. The
+% operations which can appear in the \tn{toks} are
+% \begin{itemize}
+% \item \cs{regex_action_start_wildcard:} inserted at the start
+% of the regular expression to make it unanchored.
+% \item \cs{regex_action_success:} marks the exit state of the
+% \textsc{nfa}.
+% \item \cs{regex_action_cost:n} \Arg{shift} is a transition from the
+% current \meta{state} to $\meta{state}+\meta{shift}$, which
+% consumes the current character: the target state is saved and will
+% be considered again when matching at the next position.
+% \item \cs{regex_action_free:n} \Arg{shift}, and
+% \cs{regex_action_free_group:n} \Arg{shift} are free transitions,
+% which immediately perform the actions for the state
+% $\meta{state}+\meta{shift}$ of the \textsc{nfa}. They differ in
+% how they detect and avoid infinite loops. For now, we just need to
+% know that the \texttt{group} variant must be used for transitions
+% back to the start of a group.
+% \item \cs{regex_action_submatch:n} \Arg{key} where the \meta{key} is
+% a group number followed by |<| or |>| for the beginning or end of
+% group. This causes the current position in the query to be stored
+% as the \meta{key} submatch boundary.
+% \end{itemize}
+%
+% We strive to preserve the following properties while building.
+% \begin{itemize}
+% \item The current capturing group is
+% $\text{\texttt{capturing_group}}-1$, and if a group is opened now,
+% it will be labelled \texttt{capturing_group}.
+% \item The last allocated state is $\text{\texttt{max_state}}-1$, so
+% \texttt{max_state} is a free state.
+% \item The \texttt{left_state} points to a state to the left of the
+% current group or of the last class.
+% \item The \texttt{right_state} points to a newly created,
+% empty state, with some transitions leading to it.
+% \item The \texttt{left/right} sequences hold a list of the
+% corresponding end-points of nested groups.
+% \end{itemize}
+%
+% \begin{macro}[int]{\regex_build:n, \regex_build:N}
+% The \texttt{n}-type function first compiles its argument. Reset some
+% variables. Allocate two states, and put a wildcard in state $0$
+% (transitions to state $1$ and $0$ state). Then build the regex
+% within a (capturing) group, which will be numbered $0$ (current
+% value of \texttt{capturing_group}). Finally, if the match reaches the
+% last state, it is successful.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_build:n #1
+ {
+ \regex_compile:n {#1}
+ \regex_build:N \l_regex_internal_regex
+ }
+\cs_new_protected:Npn \regex_build:N #1
+ {
+%<trace> \trace_push:nnn { regex } { 1 } { regex_build }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_zero:N \l_regex_capturing_group_int
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
+ \regex_build_new_state:
+ \regex_toks_put_right:Nn \l_regex_left_state_int
+ { \regex_action_start_wildcard: }
+ \regex_group:nnnN {#1} { 1 } { 0 } \c_false_bool
+ \regex_toks_put_right:Nn \l_regex_right_state_int
+ { \regex_action_success: }
+%<trace> \regex_trace_states:n { 2 }
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_build }
+ }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_group_n:, \regex_build_group_n?:}
-% \begin{macro}[aux]{\regex_build_group_:}
-% These functions are called in case the syntax is
-% \texttt{\{\meta{int}\}}, or in the absence of quantifier (with
-% $n=1$). Greedy and lazy operators are identical, since the number of
-% repetitions is fixed. We only record the submatch information at
-% the last repetition; and of course, in the case $n=0$, we record
-% nothing.
+% \begin{macro}[int]{\regex_build_for_cs:n}
+% When using a regex to match a cs, we don't insert a wildcard, we
+% anchor at the end, and since we ignore submatches, there is no need
+% to surround the expression with a group. However, for branches to
+% work properly at the outer level, we need to put the appropriate
+% \texttt{left} and \texttt{right} states in their sequence.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_group_n: #1
+\cs_new_protected:Npn \regex_build_for_cs:n #1
{
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#1}
+%<trace> \trace_push:nnn { regex } { 1 } { regex_build_for_cs }
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
\regex_build_new_state:
- \int_compare:nNnF {#1} = \c_zero
+ \regex_push_lr_states:
+ #1
+ \regex_pop_lr_states:
+ \regex_toks_put_right:Nn \l_regex_right_state_int
{
- \regex_build_group_submatches:NN
- \l_regex_internal_a_int \l_regex_left_state_int
+ \if_num:w \l_regex_current_pos_int = \l_regex_max_pos_int
+ \exp_after:wN \regex_action_success:
+ \fi:
}
+%<trace> \regex_trace_states:n { 2 }
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_build_for_cs }
}
-\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n:
-\cs_new_protected_nopar:Npn \regex_build_group_:
- { \regex_build_group_n: \c_one }
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_group_n*:, \regex_build_group_n*?:}
-% \begin{macro}[aux]{\regex_build_group_*:, \regex_build_group_*?:}
-% \begin{macro}[aux]{\regex_build_group_+:, \regex_build_group_+?:}
-% \begin{macro}[aux]{\regex_build_group_n_star_aux:NNn}
-% 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.
+% \subsubsection{Helpers for building an \textsc{nfa}}
+%
+% \begin{macro}[int]{\regex_push_lr_states:, \regex_pop_lr_states:}
+% When building the regular expression, we keep track of pointers to
+% the left-end and right-end of each group without help from \TeX{}'s
+% grouping.
% \begin{macrocode}
-\cs_new_protected_nopar:cpn { regex_build_group_n*: }
+\cs_new_protected_nopar:Npn \regex_push_lr_states:
{
- \regex_build_group_n_star_aux:NNn
- \regex_toks_put_right:Nx \regex_toks_put_left:Nx
+ \seq_push:No \l_regex_left_state_seq
+ { \int_use:N \l_regex_left_state_int }
+ \seq_push:No \l_regex_right_state_seq
+ { \int_use:N \l_regex_right_state_int }
}
-\cs_new_protected_nopar:cpn { regex_build_group_n*?: }
+\cs_new_protected_nopar:Npn \regex_pop_lr_states:
{
- \regex_build_group_n_star_aux:NNn
- \regex_toks_put_left:Nx \regex_toks_put_right:Nx
+ \seq_pop:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \seq_pop:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_right_state_int \l_regex_internal_a_tl
}
-\cs_new_protected:Npn \regex_build_group_n_star_aux:NNn #1#2#3
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\regex_toks_put_left:Nx}
+% \begin{macro}[int]{\regex_toks_put_right:Nx, \regex_toks_put_right:Nn}
+% During the building phase we wish to add \texttt{x}-expanded
+% material to \tn{toks}, either to the left or to the right. The
+% expansion is done \enquote{by hand} for optimization (these
+% operations are used quite a lot). The \texttt{Nn} version of
+% \cs{regex_toks_put_right:Nx} is provided because it is more
+% efficient than \texttt{x}-expanding with \cs{exp_not:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2
{
- \int_compare:nNnTF {#3} = \c_zero
- { \regex_build_group_qs_aux:NN \l_regex_internal_a_int #1 }
- {
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#3}
- \regex_build_new_state:
- #2 \l_regex_left_state_int
- {
- \regex_action_free:n
- {
- \int_eval:n
- { \l_regex_internal_a_int - \l_regex_left_state_int }
- }
- }
- \regex_build_group_submatches:NN
- \l_regex_internal_a_int \l_regex_left_state_int
- }
+ \cs_set_nopar:Npx \regex_tmp:w { #2 }
+ \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 }
}
-\cs_new_protected_nopar:cpx { regex_build_group_*: }
- { \exp_not:c { regex_build_group_n*: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_group_*?: }
- { \exp_not:c { regex_build_group_n*?: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_group_+: }
- { \exp_not:c { regex_build_group_n*: } \c_one }
-\cs_new_protected_nopar:cpx { regex_build_group_+?: }
- { \exp_not:c { regex_build_group_n*?: } \c_one }
+\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2
+ {
+ \cs_set_nopar:Npx \regex_tmp:w {#2}
+ \tex_toks:D #1 \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w }
+ }
+\cs_new_protected:Npn \regex_toks_put_right:Nn #1#2
+ { \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D #1 #2 } }
% \end{macrocode}
% \end{macro}
% \end{macro}
-% \end{macro}
+%
+% \begin{macro}[int]
+% {
+% \regex_build_transition_left:NNN,
+% \regex_build_transition_right:nNn
+% }
+% Add a transition from |#2| to |#3| using the function |#1|. The
+% \texttt{left} function is used for higher priority transitions, and
+% the \texttt{right} function for lower priority transitions (which
+% should be performed later). The signatures differ to reflect the
+% differing usage later on. Both functions could be optimized.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_build_transition_left:NNN #1#2#3
+ { \regex_toks_put_left:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected:Npn \regex_build_transition_right:nNn #1#2#3
+ { \regex_toks_put_right:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_group_nn:, \regex_build_group_nn?:}
-% \begin{macro}[aux]{\regex_build_group_nn_aux:nnnn}
-% \begin{macro}[aux, rEXP]{\regex_build_group_nn_aux_ii:w}
-% These functions are called when the syntax is either
-% \texttt{\{\meta{int},\}} or \texttt{\{\meta{int},\meta{int}\}}.
+% \begin{macro}[int]{\regex_build_new_state:}
+% Add a new empty state to the \textsc{nfa}. Then update the
+% \texttt{left}, \texttt{right}, and \texttt{max} states, so that the
+% \texttt{right} state is the new empty state, and the \texttt{left}
+% state points to the previously \enquote{current} state.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_build_group_nn:
- {
- \regex_build_group_nn_aux:nnnn { }
- \regex_build_group_transition_right_max:
- }
-\cs_new_protected_nopar:cpn { regex_build_group_nn?: }
+\cs_new_protected_nopar:Npn \regex_build_new_state:
{
- \regex_build_group_nn_aux:nnnn { }
+%<*trace>
+ \trace:nnx { regex } { 2 }
{
- \use:x
- {
- \tex_toks:D \l_regex_right_state_int
- {
- \exp_after:wN \regex_build_group_nn_aux_ii:w
- \tex_the:D \tex_toks:D \l_regex_right_state_int
- }
- }
+ regex~new~state~
+ L=\int_use:N \l_regex_left_state_int ~ -> ~
+ R=\int_use:N \l_regex_right_state_int ~ -> ~
+ M=\int_use:N \l_regex_max_state_int ~ -> ~
+ \int_eval:n { \l_regex_max_state_int + \c_one }
}
+%</trace>
+ \tex_toks:D \l_regex_max_state_int { }
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
+ \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int
+ \int_incr:N \l_regex_max_state_int
}
-\cs_new_protected:Npn \regex_build_group_nn_aux:nnnn #1#2#3#4
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\regex_build_transitions_lazyness:NNNNN}
+% This function creates a new state, and puts two transitions starting
+% from the old current state. The order of the transitions is
+% controlled by |#1|, true for lazy quantifiers, and false for greedy
+% quantifiers.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_build_transitions_lazyness:NNNNN #1#2#3#4#5
{
- \int_compare:nNnTF {#3} > {#4}
- {
- \msg_kernel_error:nnxxx
- { regex } { quantifier-backwards } {#3} {#4} {#1}
- }
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
{
- \int_compare:nNnTF {#3} = \c_zero
- {
- \regex_build_group_qs_aux:
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n { #4 - #3 }
- \int_set_eq:NN \l_regex_internal_c_int \l_regex_right_state_int
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \prg_replicate:nn { #4 - #3 }
- {
- #2
- \int_add:Nn \l_regex_right_state_int
- { \l_regex_internal_b_int - \l_regex_internal_a_int }
- }
- \int_set_eq:NN \l_regex_right_state_int \l_regex_internal_c_int
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_new_state:
- }
- {
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#3}
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_group_submatches:NN
- \l_regex_left_state_int \l_regex_right_state_int
- \regex_build_group_n_aux:n { #4 - #3 + \c_one }
- \int_sub:Nn \l_regex_right_state_int
- { \l_regex_internal_a_int - \l_regex_left_state_int }
- \prg_replicate:nn { #4 - #3 }
- {
- #2
- \int_add:Nn \l_regex_right_state_int
- { \l_regex_internal_b_int - \l_regex_internal_a_int }
- }
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_new_state:
- }
+ \if_meaning:w \c_true_bool #1
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ \else:
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ \fi:
}
}
-\cs_new_protected:Npn \regex_build_group_nn_aux_ii:w #1 \regex_action_free:n
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Building classes}
+%
+% \begin{macro}[int]{\regex_class:NnnnN}
+% \begin{macro}[int, rEXP]{\regex_tests_action_cost:n}
+% The arguments are: \meta{boolean} \Arg{tests} \Arg{min} \Arg{more}
+% \meta{lazyness}. First store the tests with a trailing
+% \cs{regex_action_cost:n}, in the true branch of
+% \cs{regex_break_point:TF} for positive classes, or the false branch
+% for negative classes. The integer \meta{more} is $0$ for fixed
+% repetitions, $-1$ for unbounded repetitions, and
+% $\meta{max}-\meta{min}$ for a range of repetitions.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_class:NnnnN #1#2#3#4#5
{
- #1
- \regex_action_free:n
+ \cs_set_nopar:Npx \regex_tests_action_cost:n ##1
{
- \int_eval:n
- { \l_regex_max_state_int - \l_regex_right_state_int }
+ \exp_not:n { \exp_not:n {#2} }
+ \bool_if:NTF #1
+ { \regex_break_point:TF { \regex_action_cost:n {##1} } { } }
+ { \regex_break_point:TF { } { \regex_action_cost:n {##1} } }
}
- \regex_action_free:n
+ \if_case:w - #4 \exp_stop_f:
+ \regex_class_repeat:n {#3}
+ \or: \regex_class_repeat:nN {#3} #5
+ \else: \regex_class_repeat:nnN {#3} {#4} #5
+ \fi:
}
+\cs_new:Npn \regex_tests_action_cost:n { \regex_action_cost:n }
% \end{macrocode}
% \end{macro}
% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Matching raw token lists with \cs{u}}
-%
-%^^A todo: document interactions between caselessness and |\u{...}|
-%^^A todo: clarify when l_regex_catcodes_int is reset.
%
-% \begin{macro}[aux]{\regex_build_/u:}
-% \begin{macro}[aux]{\regex_build_u_loop:NN}
-% The |\u| escape is invalid in classes nad directly following a
-% catcode test. Otherwise, it must be followed by a left brace. We
-% then collect the characters for the argument of |\u| within an
-% \texttt{x}-expanding assignment. In principle we could just wait to
-% encounter a right brace, but this is unsafe: if the right brace is
-% missing, then we will reach the end-markers of the regex, and
-% continue, leading to obscure fatal errors. Instead, we only allow
-% raw and special characters, and stop when encountering a special
-% right brace.
+% \begin{macro}[aux]{\regex_class_repeat:n}
+% This is used for a fixed number of repetitions. Build one state for
+% each repetition, with a transition controlled by the tests that we
+% have collected. That works just fine for |#1|${}=0$ repetitions:
+% nothing is built.
% \begin{macrocode}
-\cs_new_protected:cpn { regex_build_/u: } #1#2
+\cs_new_protected:Npn \regex_class_repeat:n #1
{
- \regex_build_if_assertions_forbidden:TF
- { \regex_build_raw_error:N u #1#2 }
+ \prg_replicate:nn {#1}
{
- \str_if_eq:xxTF {#1#2} { \regex_build_special:N \c_lbrace_str }
- {
- \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
- \regex_build_u_loop:NN
- }
- {
- \msg_kernel_error:nn { regex } { u-missing-lbrace }
- #1#2
- }
+ \regex_build_new_state:
+ \regex_build_transition_right:nNn \regex_tests_action_cost:n
+ \l_regex_left_state_int \l_regex_right_state_int
}
}
-\cs_new:Npn \regex_build_u_loop:NN #1#2
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_class_repeat:nN}
+% This implements unbounded repetitions of a single class (\emph{e.g.}
+% the |*| and |+| quantifiers). If the minimum number |#1| of
+% repetitions is $0$, then build a transition from the current state
+% to itself governed by the tests, and a free transition to a new
+% state (hence skipping the tests). Otherwise, call
+% \cs{regex_class_repeat:n} for the code to match |#1| repetitions,
+% and add free transitions from the last state to the previous one,
+% and to a new one. In both cases, the order of transitions is
+% controlled by the lazyness boolean |#2|.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_class_repeat:nN #1#2
+ {
+ \if_num:w #1 = \c_zero
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_tests_action_cost:n \l_regex_left_state_int
+ \else:
+ \regex_class_repeat:n {#1}
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_action_free:n \l_regex_internal_a_int
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_class_repeat:nnN}
+% We want to build the code to match from |#1| to $|#1|+|#2|$
+% repetitions. Match |#1| repetitions (can be $0$). Compute the final
+% state of the next construction as \texttt{a}. Build $|#2|>0$ states,
+% each with a transition to the next state governed by the tests, and
+% a transition to the final state \texttt{a}. The computation of
+% \texttt{a} is safe because states are allocated in order, starting
+% from \texttt{max_state}.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_class_repeat:nnN #1#2#3
{
- \token_if_eq_meaning:NNTF #1 \regex_build_raw:N
- { #2 \regex_build_u_loop:NN }
+ \regex_class_repeat:n {#1}
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_max_state_int + #2 - \c_one }
+ \prg_replicate:nn { #2 }
{
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
- {
- \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #2
- { \if_false: { \fi: } \regex_build_u_end: }
- { #2 \regex_build_u_loop:NN }
- }
- {
- \if_false: { \fi: }
- \msg_kernel_error:nnx { regex } { u-missing-rbrace } {#2}
- \regex_build_u_end:
- #1#2
- }
+ \regex_build_transitions_lazyness:NNNNN #3
+ \regex_action_free:n \l_regex_internal_a_int
+ \regex_tests_action_cost:n \l_regex_right_state_int
}
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_u_end:}
-% Once we have extracted the variable's name, we store the contents of
-% that variable in \cs{l_regex_internal_a_tl}. The behaviour of |\u|
-% then depends on whether we are within a |\c{...}| escape (in this
-% case, the variable is turned to a string), or not. Afterwards, clean
-% up the value of \cs{l_regex_tests_tl}, but not
-% \cs{l_regex_tests_bool}, because this is guaranteed to be
-% \texttt{true} at the start, and is not affected by anything we do
-% here.
+% \subsubsection{Building groups}
+%
+% \begin{macro}[aux]{\regex_group_aux:nnnnN}
+% Arguments: \Arg{label} \Arg{contents} \Arg{min} \Arg{more}
+% \meta{lazyness}. If \meta{min} is $0$, we need to add a state before
+% building the group, so that the thread which skips the group does
+% not also set the start-point of the submatch. After adding one more
+% state, the \texttt{left_state} is the left end of the group, from
+% which all branches will stem, and the \texttt{right_state} is the
+% right end of the group, and all branches end their course in that
+% state. We store those two integers to be queried for each branch, we
+% build the \textsc{nfa} states for the contents |#2| of the group,
+% and we forget about the two integers. Once this is done, perform the
+% repetition: either exactly |#3| times, or |#3| or more times, or
+% between |#3| and $|#3|+|#4|$ times, with lazyness |#5|. The
+% \meta{label} |#1| is used for submatch tracking. Each of the three
+% auxiliaries expects \texttt{left_state} and \texttt{right_state} to
+% be set properly.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_u_end:
+\cs_new_protected:Npn \regex_group_aux:nnnnN #1#2#3#4#5
{
- \tl_set:Nv \l_regex_internal_a_tl \l_regex_internal_a_tl
- \if_num:w \l_regex_build_mode_int = \c_zero
- \regex_build_u_not_cs:
- \else:
- \regex_build_u_in_cs:
- \fi:
- \tl_clear:N \l_regex_tests_tl
+%<trace> \trace_push:nnn { regex } { 1 } { regex_group }
+ \if_num:w #3 = \c_zero
+ \regex_build_new_state:
+%<assert>\assert_int:n { \l_regex_max_state_int = \l_regex_right_state_int + 1 }
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ \fi:
+ \regex_build_new_state:
+ \regex_push_lr_states:
+ #2
+ \regex_pop_lr_states:
+ \if_case:w - #4 \exp_stop_f:
+ \regex_group_repeat:nn {#1} {#3}
+ \or: \regex_group_repeat:nnN {#1} {#3} #5
+ \else: \regex_group_repeat:nnnN {#1} {#3} {#4} #5
+ \fi:
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_group }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_u_in_cs:}
-% When |\u| appears within a control sequence, the variable is
-% converted to a string, and we add to the NFA one state per
-% character. We use \cs{regex_build_one_:} directly, as this avoids
-% having to search for a quantifier.
+% \begin{macro}[int]{\regex_group:nnnN, \regex_group_no_capture:nnnN}
+% Hand to \cs{regex_group_aux:nnnnnN} the label of that group
+% (expanded), and the group itself, with some extra commands to
+% perform.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_u_in_cs:
+\cs_new_protected:Npn \regex_group:nnnN #1
{
- \exp_args:NNo \str_gset_other:Nn \g_regex_internal_tl
- { \l_regex_internal_a_tl }
- \tl_map_inline:Nn \g_regex_internal_tl
+ \exp_args:No \regex_group_aux:nnnnN
+ { \int_use:N \l_regex_capturing_group_int }
{
- \tl_set:Nx \l_regex_tests_tl { \regex_item_equal:n { `##1 } }
- \regex_build_one_:
+ \int_incr:N \l_regex_capturing_group_int
+ #1
}
}
+\cs_new_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_group_aux:nnnnN { -1 } }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_build_u_not_cs:}
-% In mode $0$, the |\u| escape adds one state to the NFA for each
-% token in \cs{l_regex_internal_a_tl}. The tests corresponding to a
-% given \meta{token} are: first test the category, then, if the
-% \meta{token} is a control sequence, compare the string
-% representations, otherwise compare the character codes. We could
-% compare the string representations in all cases, but that doesn't
-% interact well with caseless matching.
+% \begin{macro}[int]{\regex_group_resetting:nnnN}
+% \begin{macro}[aux]{\regex_group_resetting_loop:nnNn}
+% Again, hand the label $-1$ to \cs{regex_group_aux:nnnnN}, but this
+% time we work a little bit harder to keep track of the maximum group
+% label at the end of any branch, and to reset the group number at
+% each branch. This relies on the fact that a compiled regex always is
+% a sequence of items of the form \cs{regex_branch:n} \Arg{branch}.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_build_u_not_cs:
+\cs_new_protected:Npn \regex_group_resetting:nnnN #1
{
- \exp_args:No \tl_analysis_map_inline:nn { \l_regex_internal_a_tl }
+ \regex_group_aux:nnnnN { -1 }
{
- \tl_set:Nx \l_regex_tests_tl
- {
- \exp_not:N \int_compare:nNnT
- \l_regex_current_catcode_int = { \int_value:w "##2 }
- {
- \int_compare:nNnTF { "##2 } = \c_zero
- {
- \exp_not:n
- { \str_if_eq:xxT { \l_regex_current_token_tl } {##1} }
- { \regex_break_true:w }
- }
- { \regex_item_equal:n {##3} }
- }
- }
- \regex_build_one_:
+ \exp_args:Noo \regex_group_resetting_loop:nnNn
+ { \int_use:N \l_regex_capturing_group_int }
+ { \int_use:N \l_regex_capturing_group_int }
+ #1
+ { ?? \prg_map_break:n } { }
+ \prg_break_point:n { }
}
}
+\cs_new_protected:Npn \regex_group_resetting_loop:nnNn #1#2#3#4
+ {
+ \use_none:nn #3 { \int_set:Nn \l_regex_capturing_group_int {#1} }
+ \int_set:Nn \l_regex_capturing_group_int {#2}
+ #3 {#4}
+ \exp_args:Nf \regex_group_resetting_loop:nnNn
+ { \int_max:nn {#1} { \l_regex_capturing_group_int } }
+ {#2}
+ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \subsection{Matching}
-%
-% At every index, we unpack that array of active states and
-% empty it. Then loop over all active states, and perform the
-% instruction at that state of the NFA. This can involve
-% \enquote{free} transitions to other states, or transitions
-% which \enquote{consume} the current character. For free
-% transitions, the instruction at the new state of the NFA is
-% performed. When a transition consumes a character, the new
-% state is put in the array of \tn{skip} registers: it will
-% be active again when the next character is read.
-%
-% If two paths through the NFA \enquote{collide} in the sense
-% that they reach the same state when reading a given
-% character, then any future execution will be identical for
-% both. Hence, it is indeed enough to keep track of which
-% states are active. [In the presence of back-references, the
-% future execution is affected by how the previous match took
-% place; this is why we cannot support those non-regular
-% features.]
-%
-% Many of the functions require extracting the submatches for
-% the \enquote{best} match. Execution paths through the NFA
-% are ordered by precedence: for instance, the regular
-% expression \texttt{a?} creates two paths, matching either
-% an empty token list or a single \texttt{a}; the path matching
-% an \texttt{a} has higher precedence. When two paths collide,
-% the path with the highest precedence is kept, and the other
-% one is discarded. The submatch information for a given path
-% is stored at the start of the \tn{toks} register which
-% holds the state at which that path currently is.
-%
-% Deciding to store the submatch information in \tn{toks}
-% registers alongside with states of the NFA unfortunately
-% implies some shuffling around. The two other options are to
-% store the submatch information in one control sequence per
-% path, which wastes csnames, or to store all of the submatch
-% information in one property list, which turns out to be too
-% slow. A tricky aspect of submatch tracking is to know when
-% to get rid of submatch information. This naturally happens
-% when submatch information is stored in \tn{toks} registers:
-% if the information is not moved, it will be overwritten
-% later.
-%
-% The presence of $\epsilon$-transitions (transitions which
-% consume no character) leads to potential infinite loops;
-% for instance the regular expression |(a??)*| could lead to
-% an infinite recursion, where |a??| matches no character, |*|
-% loops back to the start of the group, and |a??| matches no
-% character again. Therefore, we need to keep track of the
-% states of the NFA visited at the current step. More
-% precisely, a state is marked as \enquote{visited} if the
-% instructions for that state have been inserted in the input
-% stream, by setting the corresponding \tn{dimen} register to
-% a value which uniquely identifies at which step it was last
-% inserted.
-%
-% \subsubsection{Helpers for running the NFA}
-%
-% \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
-% \cs{l_regex_max_active_int}, and converting the integer
-% to a dimension (suitable for a \tn{skip} assignment) in
-% scaled points.
+% \begin{macro}[int]{\regex_branch:n}
+% Add a free transition from the left state of the current group to a
+% brand new state, starting point of this branch. Once the branch is
+% built, add a transition from its last state to the right state of
+% the group. The left and right states of the group are extracted from
+% the relevant sequences.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_store_state:n #1
+\cs_new_protected:Npn \regex_branch:n #1
{
- \int_incr:N \l_regex_max_active_int
- \tex_skip:D \l_regex_max_active_int #1 sp \scan_stop:
- \regex_store_submatches:n {#1}
+%<trace> \trace_push:nnn { regex } { 1 } { regex_branch }
+ \regex_build_new_state:
+ \seq_get:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ #1
+ \seq_get:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_tl
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_branch }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_state_use:}
-% \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_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_step_int} in \texttt{sp}.
-% The primitive conditional is ended before unpacking
-% the \tn{toks} register.
+% \begin{macro}[int]{\regex_group_repeat:nn}
+% This function is called to repeat a group a fixed number of times
+% |#2|; if this is $0$ we remove the group altogether (but don't reset
+% the \texttt{capturing_group} label). Otherwise, the auxiliary
+% \cs{regex_group_repeat_aux:n} copies |#2| times the \tn{toks} for
+% the group, and leaves \texttt{internal_a} pointing to the left end
+% of the last repetition. We only record the submatch information at
+% the last repetition. Finally, add a state at the end (the transition
+% to it has been taken care of by the replicating auxiliary.
% \begin{macrocode}
-\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 \use_none_delimit_by_s_stop:w } }
-\cs_new_protected:Npn \regex_state_use_aux:n #1
+\cs_new_protected:Npn \regex_group_repeat:nn #1#2
{
- \if_num:w \tex_dimen:D \l_regex_current_state_int < \l_regex_step_int
- \tex_dimen:D \l_regex_current_state_int
- = \l_regex_step_int sp \scan_stop:
- #1 \tex_the:D \tex_toks:D \exp_after:wN \l_regex_current_state_int
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_max_state_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_new_state:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \regex_build_new_state:
\fi:
- \scan_stop:
}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_group_submatches:nNN}
+% This inserts in states |#2| and |#3| the code for tracking
+% submatches of the group |#1|, unless inhibited by a label of $-1$.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_group_submatches:nNN #1#2#3
+ {
+ \if_num:w #1 > \c_minus_one
+ \regex_toks_put_left:Nx #2 { \regex_action_submatch:n { #1 < } }
+ \regex_toks_put_left:Nx #3 { \regex_action_submatch:n { #1 > } }
+ \fi:
+ }
+% \end{macrocode}
% \end{macro}
-% \end{macro}
+%
+% \begin{macro}[aux]{\regex_group_repeat_aux:n}
+% Here we repeat \tn{toks} ranging from \texttt{left_state} to
+% \texttt{max_state}, $|#1|>0$ times. First add a transition so that
+% the copies will \enquote{chain} properly. Compute the shift
+% \texttt{c} between the original copy and the last copy we
+% want. Shift the \texttt{right_state} and \texttt{max_state} to their
+% final values. We then want to perform \texttt{c} copy operations. At
+% the end, \texttt{b} is equal to the \texttt{max_state}, and
+% \texttt{a} points to the left of the last copy of the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_group_repeat_aux:n #1
+ {
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \int_set_eq:NN \l_regex_internal_b_int \l_regex_max_state_int
+ \if_num:w \int_eval:w #1 > \c_one
+ \int_set:Nn \l_regex_internal_c_int
+ {
+ ( #1 - \c_one )
+ * ( \l_regex_internal_b_int - \l_regex_internal_a_int )
+ }
+ \tex_advance:D \l_regex_right_state_int \l_regex_internal_c_int
+ \tex_advance:D \l_regex_max_state_int \l_regex_internal_c_int
+ \prg_replicate:nn \l_regex_internal_c_int
+ {
+ \tex_toks:D \l_regex_internal_b_int
+ = \tex_toks:D \l_regex_internal_a_int
+ \tex_advance:D \l_regex_internal_a_int \c_one
+ \tex_advance:D \l_regex_internal_b_int \c_one
+ }
+ \fi:
+ }
+% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Submatch tracking when running the NFA}
+% \begin{macro}[aux]{\regex_group_repeat:nnN}
+% This function is called to repeat a group at least $n$ times; the
+% case $n=0$ is very different from $n>0$. Assume first that $n=0$.
+% Insert submatch tracking information at the start and end of the
+% group, add a free transition from the right end to the
+% \enquote{true} left state \texttt{a} (remember: in this case we had
+% added an extra state before the left state). This forms the loop,
+% which we break away from by adding a free transition from \texttt{a}
+% to a new state.
%
-% \begin{macro}[int]{\regex_disable_submatches:}
-% Some user functions don't require tracking submatches.
-% We get a performance improvement by simply defining the
-% relevant functions to remove their argument and do nothing
-% with it.
+% Now consider the case $n>0$. Repeat the group $n$ times, chaining
+% various copies with a free transition. Add submatch tracking only to
+% the last copy, then add a free transition from the right end back to
+% the left end of the last copy, either before or after the transition
+% to move on towards the rest of the \textsc{nfa}. This transition can
+% end up before submatch tracking, but that is irrelevant since it
+% only does so when going again through the group, recording new
+% matches. Finally, add a state; we already have a transition pointing
+% to it from \cs{regex_group_repeat_aux:n}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_disable_submatches:
+\cs_new_protected:Npn \regex_group_repeat:nnN #1#2#3
{
- \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
- \regex_protected_use_none:n
+ \if_num:w #2 = \c_zero
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \regex_build_new_state:
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \else:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \fi:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \else:
+ \regex_build_transition_left:NNN \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \fi:
+ \regex_build_new_state:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_group_repeat:nnnN}
+% We wish to repeat the group between |#2| and $|#2|+|#3|$ times, with
+% a lazyness controlled by |#4|. We insert submatch tracking up front:
+% in principle, we could avoid recording submatches for the first |#2|
+% copies of the group, but that forces us to treat specially the case
+% $|#2|=0$. Repeat that group with submatch tracking $|#2|+|#3|$ times
+% (the maximum number of repetitions). Then our goal is to add |#3|
+% transitions from the end of the |#2|-th group, and each subsequent
+% groups, to the end. For a lazy quantifier, we add those transitions
+% to the left states, before submatch tracking. For the greedy case,
+% we add the transitions to the right states, after submatch tracking
+% and the transitions which go on with more repetitions. In the greedy
+% case with $|#2|=0$, the transition which skips over all copies of
+% the group must be added separately, because its starting state does
+% not follow the normal pattern: we had to add it \enquote{by hand}
+% earlier.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_group_repeat:nnnN #1#2#3#4
+ {
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \regex_group_repeat_aux:n { #2 + #3 }
+ \if_meaning:w \c_true_bool #4
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_max_state_int
+ \prg_replicate:nn { #3 }
+ {
+ \int_sub:Nn \l_regex_left_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_left_state_int \l_regex_max_state_int
+ }
+ \else:
+ \prg_replicate:nn { #3 - \c_one }
+ {
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ }
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_right_state_int
+ { \l_regex_left_state_int - \c_one }
+ \else:
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \fi:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ \fi:
+ \regex_build_new_state:
}
-\cs_new_protected:Npn \regex_protected_use_none:n #1 { }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_store_submatches:n}
-% \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.
+% \subsubsection{Others}
+%
+% \begin{macro}[int]{\regex_assertion:Nn, \regex_b_test:, \regex_anchor:N}
+% Usage: \cs{regex_assertion:Nn} \meta{boolean} \Arg{test}, where the
+% \meta{test} is either of the two other functions. Add a free
+% transition to a new state, conditionally to the assertion test. The
+% \cs{regex_b_test:} test is used by the |\b| and |\B| escape: check
+% if the last character was a word character or not, and do the same
+% to the current character. The boundary-markers of the string are
+% non-word characters for this purpose. Anchors at the start or end
+% of match use \cs{regex_anchor:N}, with a position controlled by the
+% integer |#1|.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_store_submatches:n #1
+\cs_new_protected:Npn \regex_assertion:Nn #1#2
{
- \tex_toks:D #1 \exp_after:wN
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
{
- \tex_romannumeral:D
- \exp_after:wN \regex_store_submatches_aux:w
- \tex_the:D \tex_toks:D #1
+ \exp_not:n {#2}
+ \regex_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \regex_action_free:n
+ {
+ \int_eval:n
+ { \l_regex_right_state_int - \l_regex_left_state_int }
+ }
+ }
+ \bool_if:NT #1 { { } }
}
}
-\cs_new_protected:Npn \regex_store_submatches_aux:w #1 \s_stop
+\cs_new_protected:Npn \regex_anchor:N #1
{
- \regex_store_submatches_aux_ii:Nnnw
- #1
- \regex_state_submatches:nn \c_minus_one \q_prop
- \s_stop
+ \if_num:w #1 = \l_regex_current_pos_int
+ \exp_after:wN \regex_break_true:w
+ \fi:
}
-\cs_new_protected:Npn \regex_store_submatches_aux_ii:Nnnw
- \regex_state_submatches:nn #1 #2 #3 \s_stop
+\cs_new_protected_nopar:Npn \regex_b_test:
{
- \exp_after:wN \c_zero
- \exp_after:wN \regex_state_submatches:nn \exp_after:wN
- { \int_value:w \int_eval:w \l_regex_step_int + \c_one \exp_after:wN }
- \exp_after:wN { \l_regex_current_submatches_prop }
- \regex_state_submatches:nn {#1} {#2}
- \s_stop
+ \group_begin:
+ \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int
+ \regex_prop_w:
+ \regex_break_point:TF
+ { \group_end: \regex_item_reverse:n \regex_prop_w: }
+ { \group_end: \regex_prop_w: }
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}[aux]{\regex_state_submatches:nn}
-% This function is inserted by \cs{regex_store_submatches:n}
-% in the \tn{toks} register holding a given state, and it is
-% performed when the state is used.
+% \begin{macro}[int]{\regex_command_K:}
+% Change the starting point of the $0$-th submatch (full match), and
+% transition to a new state, pretending that this is a fresh thread.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_state_submatches:nn #1#2
+\cs_new_protected_nopar:Npn \regex_command_K:
{
- \if_num:w #1 = \l_regex_step_int
- \tl_set:Nn \l_regex_current_submatches_prop { #2 }
- \fi:
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
+ {
+ \regex_action_submatch:n { 0< }
+ \bool_set_true:N \l_regex_fresh_thread_bool
+ \regex_action_free:n
+ { \int_eval:n { \l_regex_right_state_int - \l_regex_left_state_int } }
+ \bool_set_false:N \l_regex_fresh_thread_bool
+ }
}
% \end{macrocode}
% \end{macro}
%
+% \subsection{Matching}
+%
+% We search for matches by running all the execution threads through the
+% \textsc{nfa} in parallel, reading one token of the query at each step.
+% The \textsc{nfa} contains \enquote{free} transitions to other states,
+% and transitions which \enquote{consume} the current token. For free
+% transitions, the instruction at the new state of the \textsc{nfa} is
+% performed immediately. When a transition consumes a character, the
+% new state is appended to a list of \enquote{active states}, stored in
+% \tn{skip} registers: this thread will be active again when the next
+% token is read from the query. At every step (for each token in the
+% query), we unpack that list of active states and the corresponding
+% submatch props, and empty those.
+%
+% If two paths through the \textsc{nfa} \enquote{collide} in the sense
+% that they reach the same state after reading a given token, then they
+% only differ in how they previously matched, and the future execution
+% will be identical for both. (Note that this would be wrong in the
+% presence of back-references.) Hence, we only need to keep one of the
+% two threads: the thread with the highest priority. Our \textsc{nfa} is
+% built in such a way that higher priority actions always come before
+% lower priority actions, which makes things work.
+%
+% The explanation in the previous paragraph may make us think that we
+% simply need to keep track of which states were visited at a given
+% step: after all, the loop generated when matching |(a?)*| against |a|
+% is broken, isn't it? No. The group first matches |a|, as it should,
+% then repeats; it attempts to match |a| again but fails; it skips |a|,
+% and finds out that this state has already been seen at this position
+% in the query: the match stops. The capturing group is (wrongly) |a|.
+% What went wrong is that a thread collided with itself, and the later
+% version, which has gone through the group one more times with an empty
+% match, should have a higher priority than not going through the group.
+%
+% We solve this by distinguishing \enquote{normal} free transitions
+% \cs{regex_action_free:n} from transitions
+% \cs{regex_action_free_group:n} which go back to the start of the
+% group. The former will keep threads unless they have been visited by a
+% \enquote{completed} thread, while the latter kind of transition also
+% prevents going back to a state visited by the current thread.
+%
+% \subsubsection{Variables used when matching}
+%
+% \begin{variable}
+% {
+% \l_regex_min_pos_int,
+% \l_regex_max_pos_int,
+% \l_regex_current_pos_int,
+% \l_regex_start_pos_int,
+% \l_regex_success_pos_int,
+% }
+% The tokens in the query are indexed from \texttt{min_pos} for the
+% first to $\texttt{max_pos}-1$ for the last, and their information is
+% stored in \tn{muskip} and \tn{toks} registers with those numbers. We
+% don't start from $0$ because the \tn{toks} registers with low
+% numbers are used to hold the states of the \textsc{nfa}. We match
+% without backtracking, keeping all threads in lockstep at the
+% \texttt{current_pos} in the query. The starting point of the current
+% match attempt is \texttt{start_pos}, and \texttt{success_pos},
+% updated whenever a thread succeeds, is used as the next starting
+% position.
+% \begin{macrocode}
+\int_new:N \l_regex_min_pos_int
+\int_new:N \l_regex_max_pos_int
+\int_new:N \l_regex_current_pos_int
+\int_new:N \l_regex_start_pos_int
+\int_new:N \l_regex_success_pos_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_regex_current_char_int,
+% \l_regex_current_catcode_int,
+% \l_regex_last_char_int,
+% \l_regex_case_changed_char_int
+% }
+% The character and category codes of the token at the current
+% position; the character code of the token at the previous position;
+% and the character code of the result of changing the case of the
+% current token (|A-Z|$\leftrightarrow$|a-z|). This last integer is
+% only computed when necessary, and is otherwise \cs{c_max_int}. The
+% \texttt{current_char} variable is also used in various other phases
+% to hold a character code.
+% \begin{macrocode}
+\int_new:N \l_regex_current_char_int
+\int_new:N \l_regex_current_catcode_int
+\int_new:N \l_regex_last_char_int
+\int_new:N \l_regex_case_changed_char_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_current_state_int}
+% For every character in the token list, each of the active states is
+% considered in turn. The variable \cs{l_regex_current_state_int}
+% holds the state of the \textsc{nfa} which is currently considered:
+% transitions are then given as shifts relative to the current state.
+% \begin{macrocode}
+\int_new:N \l_regex_current_state_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_regex_current_submatches_prop, \l_regex_success_submatches_prop}
+% The submatches for the thread which is currently active are stored
+% in the \texttt{current_submatches} 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, to be
+% retrieved when matching at the next position. When a thread
+% succeeds, this property list is copied to
+% \cs{l_regex_success_submatches_prop}: 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}
+%
+% \begin{variable}{\l_regex_step_int}
+% This integer, always even, is increased every time a character in
+% the query is read, and not reset when doing multiple matches. For
+% each \meta{state} in the \textsc{nfa} we store in
+% \tn{dimen}\meta{state} the last step in which this state was
+% encountered. This lets us break infinite loops by not visiting the
+% same state twice in the same step. In fact, \tn{dimen}\meta{state}
+% is equal \texttt{step} when we have started performing the
+% operations of \tn{toks}\meta{state}, but not finished yet. However,
+% once we finish, we set \tn{dimen}\meta{state} to
+% $\text{\texttt{step}}+1$. This is needed to track submatches
+% properly (see building phase). The \texttt{step} is also used to
+% attach each set of submatch information to a given iteration (and
+% automatically discard it when it corresponds to a past step).
+% \begin{macrocode}
+\int_new:N \l_regex_step_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_min_active_int, \l_regex_max_active_int}
+% All the currently active states are kept in order of precedence in
+% the \tn{skip} registers, and the corresponding submatches in the
+% \tn{toks}. For our purposes, those serve as an array, indexed from
+% \texttt{min_active} (inclusive) to \texttt{max_active} (excluded).
+% At the start of every step, the whole array is unpacked, so that the
+% space can immediately be reused, and \texttt{max_active} is reset to
+% \texttt{min_active}, effectively clearing the array.
+% \begin{macrocode}
+\int_new:N \l_regex_min_active_int
+\int_new:N \l_regex_max_active_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_every_match_tl}
+% Every time a match is found, this token list is used. For single
+% matching, the token list is empty. For multiple matching, the token
+% list is set to repeat the matching, after performing some operation
+% which depends on the user function. See \cs{regex_single_match:} and
+% \cs{regex_multi_match:n}.
+% \begin{macrocode}
+\tl_new:N \l_regex_every_match_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_fresh_thread_bool, \l_regex_empty_success_bool}
+% \begin{macro}[aux]{\regex_if_two_empty_matches:F}
+% When doing multiple matches, we need to avoid infinite loops where
+% each iteration matches the same empty token list. When an empty
+% token list is matched, the next successful match of the same empty
+% token list is suppressed. We detect empty matches by setting
+% \cs{l_regex_fresh_thread_bool} to \texttt{true} for threads which
+% directly come from the start of the regex or from the |\K| command,
+% and testing that boolean whenever a thread succeeds. The function
+% \cs{regex_if_two_empty_matches:F} is redefined at every match
+% attempt, depending on whether the previous match was empty or not:
+% if it was, then the function must cancel a purported success if it
+% is empty and at the same spot as the previous match; otherwise, we
+% definitely don't have two identical empty matches, so the function
+% is \cs{use:n}.
+% \begin{macrocode}
+\bool_new:N \l_regex_fresh_thread_bool
+\bool_new:N \l_regex_empty_success_bool
+\cs_new_eq:NN \regex_if_two_empty_matches:F \use:n
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_regex_success_bool,
+% \l_regex_saved_success_bool,
+% \l_regex_match_success_bool
+% }
+% The boolean \cs{l_regex_match_success_bool} is true if the current
+% match attempt was successful, and \cs{g_regex_success_bool} is true
+% if there was at least one successful match. This is the only global
+% variable in this whole module, but we would need it to be local when
+% matching a control sequence with |\c{...}|. This is done by saving
+% the global variable into \cs{l_regex_saved_success_bool}, which is
+% local, hence not affected by the changes due to inner regex
+% functions.
+% \begin{macrocode}
+\bool_new:N \g_regex_success_bool
+\bool_new:N \l_regex_saved_success_bool
+\bool_new:N \l_regex_match_success_bool
+% \end{macrocode}
+% \end{variable}
+%
% \subsubsection{Matching: framework}
%
% \begin{macro}[int]{\regex_match:n}
-%^^A todo: update doc.
-% 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
-% when appropriate).
+% First store the query into \tn{toks} and \tn{muskip} registers (see
+% \cs{regex_query_set:nnn}). Then initialize the variables that should
+% be set once for each user function (even for multiple
+% matches). Namely, the overall matching is not yet sucessful; none of
+% the states should be marked as visited (\tn{dimen} registers), and
+% we start at step $0$; we pretend that there was a previous match
+% ending at the start of the query, which was not empty (to avoid
+% smothering an empty match at the start). Once all this is set up, we
+% are ready for the ride. Find the first match.
% \begin{macrocode}
\cs_new_protected:Npn \regex_match:n #1
{
- \int_zero:N \l_regex_nesting_int
- \int_set_eq:NN \l_regex_current_index_int \l_regex_max_state_int
+%<trace> \trace_push:nnx { regex } { 1 } { regex_match }
+%<trace> \trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ \int_zero:N \l_regex_balance_int
+ \int_set:Nn \l_regex_current_pos_int { \c_two * \l_regex_max_state_int }
\regex_query_set:nnn { } { -1 } { -2 }
- \int_set_eq:NN \l_regex_min_index_int \l_regex_current_index_int
+ \int_set_eq:NN \l_regex_min_pos_int \l_regex_current_pos_int
\tl_analysis_map_inline:nn {#1}
- {
- \regex_query_set:nnn {##1} {"##2} {##3}
- \if_case:w "##2 \exp_stop_f:
- \or: \int_incr:N \l_regex_nesting_int
- \or: \int_decr:N \l_regex_nesting_int
- \fi:
- }
- \int_set_eq:NN \l_regex_max_index_int \l_regex_current_index_int
+ { \regex_query_set:nnn {##1} {"##2} {##3} }
+ \int_set_eq:NN \l_regex_max_pos_int \l_regex_current_pos_int
\regex_query_set:nnn { } { -1 } { -2 }
- \regex_match_initial_setup:
+%<trace> \trace:nnx { regex } { 1 } { initializing }
+ \bool_gset_false:N \g_regex_success_bool
+ \prg_stepwise_inline:nnnn
+ \l_regex_min_state_int \c_one { \l_regex_max_state_int - \c_one }
+ { \tex_dimen:D ##1 \c_one sp \scan_stop: }
+ \int_set_eq:NN \l_regex_min_active_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_step_int \c_zero
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_min_pos_int
+ \int_set:Nn \l_regex_submatch_int
+ { \c_two * \l_regex_max_state_int }
+ \bool_set_false:N \l_regex_empty_success_bool
\regex_match_once:
- }
-\cs_new_protected:Npn \regex_query_set:nnn #1#2#3
- {
- \tex_muskip:D \l_regex_current_index_int
- = \etex_gluetomu:D
- #3 sp
- plus #2 sp
- minus \l_regex_nesting_int sp
- \scan_stop:
- \tex_toks:D \l_regex_current_index_int {#1}
- \int_incr:N \l_regex_current_index_int
- }
-\cs_new_protected_nopar:Npn \regex_query_get:
- {
- \tl_set:Nx \l_regex_current_token_tl
- { \tex_the:D \tex_toks:D \l_regex_current_index_int }
- \l_regex_current_char_int
- = \etex_mutoglue:D \tex_muskip:D \l_regex_current_index_int
- \l_regex_current_catcode_int = \etex_gluestretch:D
- \etex_mutoglue:D \tex_muskip:D \l_regex_current_index_int
+%<trace> \trace_pop:nnx { regex } { 1 } { regex_match }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\regex_match_once:}
-% Set up more variables in \cs{regex_match_setup:}.
-% If there was a match, use the token list \cs{l_regex_every_match_tl},
-% which may call \cs{regex_match_once:} again to achieve multiple matches.
+% This function finds one match, then does some action defined by the
+% \texttt{every_match} token list, which may recursively call
+% \cs{regex_match_once:}. First initialize some variables: set the
+% conditional which detects identical empty matches; this match
+% attempt starts at the previous \texttt{success_pos}, is not yet
+% sucessful, and has no submatches yet; clear the array of active
+% threads, and put the starting state $0$ in it. We are then almost
+% ready to read our first token in the query, but we actually start
+% one position earlier than the start, and \texttt{get} that token, so
+% that the \texttt{last_char} will be set properly for word
+% boundaries. Then call \cs{regex_match_loop:}, which runs through the
+% query until the end or until a successful match breaks early.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \regex_match_once:
{
- \regex_match_setup:
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \cs_set_nopar:Npn \regex_if_two_empty_matches:F
+ { \int_compare:nNnF \l_regex_start_pos_int = \l_regex_current_pos_int }
+ \else:
+ \cs_set_eq:NN \regex_if_two_empty_matches:F \use:n
+ \fi:
+ \int_set_eq:NN \l_regex_start_pos_int \l_regex_success_pos_int
+ \bool_set_false:N \l_regex_match_success_bool
+ \prop_clear:N \l_regex_current_submatches_prop
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \regex_store_state:n { \l_regex_min_state_int }
+ \int_set:Nn \l_regex_current_pos_int
+ { \l_regex_start_pos_int - \c_one }
\regex_query_get:
\regex_match_loop:
- \prg_break_point:n { }
- \bool_if:NT \l_regex_success_match_bool
- {
- \bool_gset_true:N \g_regex_success_bool
- \l_regex_every_match_tl
- }
+ \l_regex_every_match_tl
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_match_initial_setup:}
-% This function holds the setup that should be done
-% only once for one given pattern matching.
-% It is called only once for the whole token list.
-% On the other hand, \cs{regex_match_setup:}
-% is called for every match in the token list in case of
-% repeated matches.
+% \begin{macro}[int]{\regex_single_match:, \regex_multi_match:n}
+% For a single match, the overall success is determined by whether the
+% only match attempt is a success. When doing multiple matches, the
+% overall matching is successful as soon as any match
+% succeeds. Perform the action |#1|, then find the next match.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_match_initial_setup:
+\cs_new_protected_nopar:Npn \regex_single_match:
{
- \int_set_eq:NN \l_regex_step_int \c_minus_one
- \prg_stepwise_inline:nnnn
- \c_zero \c_one { \l_regex_max_state_int - \c_one }
- { \tex_dimen:D ##1 \l_regex_step_int sp }
- \int_set:Nn \l_regex_start_index_int
- { \l_regex_min_index_int - \c_one }
- \int_set_eq:NN \l_regex_current_index_int \l_regex_min_index_int
- \int_set_eq:NN \l_regex_success_index_int \l_regex_min_index_int
- \int_set_eq:NN \l_regex_submatch_int \l_regex_max_state_int
- \bool_set_false:N \l_regex_success_empty_bool
- \bool_gset_false:N \g_regex_success_bool
+ \tl_set:Nn \l_regex_every_match_tl
+ { \bool_gset_eq:NN \g_regex_success_bool \l_regex_match_success_bool }
}
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[aux]{\regex_match_setup:}
-% Every time a match starts, \cs{regex_match_setup:} resets
-% a few variables.
-% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_match_setup:
+\cs_new_protected:Npn \regex_multi_match:n #1
{
- \prop_clear:N \l_regex_current_submatches_prop
- \bool_if:NTF \l_regex_success_empty_bool
- { \cs_set_eq:NN \regex_if_two_empty_matches:F \regex_if_match_empty:F }
- { \cs_set_eq:NN \regex_if_two_empty_matches:F \use:n }
- \int_set_eq:NN \l_regex_start_index_int \l_regex_success_index_int
- \int_set:Nn \l_regex_current_index_int
- { \l_regex_start_index_int - \c_one }
- \bool_set_false:N \l_regex_success_match_bool
- \int_zero:N \l_regex_max_active_int
- \regex_store_state:n {0} %^^A _state_int!
+ \tl_set:Nn \l_regex_every_match_tl
+ {
+ \if_meaning:w \c_true_bool \l_regex_match_success_bool
+ \bool_gset_true:N \g_regex_success_bool
+ #1
+ \exp_after:wN \regex_match_once:
+ \fi:
+ }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\regex_match_loop:}
-% \begin{macro}[aux]{\regex_match_one_active:n}
-% \begin{macro}[aux]{\regex_match_one_active_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 token that is read
-% (and $-1$ for the end of the token list), and loop
-% over the elements of the \tn{skip} array. Then repeat.
-% There are a couple of tests to stop reading the token list
-% when no active state is left, or when the end is reached.
-% At every step in reading the token list, 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{macro}[aux, rEXP]{\regex_match_one_active:w}
+% At each new position, set some variables and get the new character
+% and category from the query. Then unpack the array of active
+% threads, and clear it by resetting its length
+% (\texttt{max_active}). This results in a sequence of
+% \cs{regex_use_state_and_submatches:nn} \Arg{state} \Arg{prop}, and
+% we consider those states one by one in order. As soon as a thread
+% succeeds, exit the step, and, if there are threads to consider at the
+% next position, and we have not reached the end of the string,
+% repeat the loop. Otherwise, the last thread that succeeded is what
+% \cs{regex_match_once:} matches. We explain the \texttt{fresh_thread}
+% business when describing \cs{regex_action_wildcard:}.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \regex_match_loop:
{
- \int_incr:N \l_regex_current_index_int
- \int_incr:N \l_regex_step_int
- \bool_set_false:N \l_regex_fresh_thread_bool
+ \tex_advance:D \l_regex_step_int \c_two
+ \int_incr:N \l_regex_current_pos_int
\int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int
+ \int_set_eq:NN \l_regex_case_changed_char_int \c_max_int
\regex_query_get:
- \regex_match_loop_case_hook:
\use:x
{
- \int_zero:N \l_regex_max_active_int
- \regex_match_one_active:w 1 ; \prg_break_point:n { }
- \exp_not:N \prg_break_point:n { }
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \l_regex_min_active_int ;
}
- \if_num:w \l_regex_current_char_int < \c_minus_one
- \exp_after:wN \prg_map_break:
- \fi:
- \if_num:w \l_regex_max_active_int = \c_zero
- \exp_after:wN \prg_map_break:
+ \prg_break_point:n { \bool_set_false:N \l_regex_fresh_thread_bool }
+ \if_num:w \l_regex_max_active_int > \l_regex_min_active_int
+ \if_num:w \l_regex_current_pos_int < \l_regex_max_pos_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_match_loop:
+ \fi:
\fi:
- \regex_match_loop:
}
\cs_new:Npn \regex_match_one_active:w #1;
{
- \if_num:w #1 > \l_regex_max_active_int
- \exp_after:wN \prg_map_break:
+ \if_num:w #1 < \l_regex_max_active_int
+ \regex_use_state_and_submatches:nn
+ { \int_value:w \tex_skip:D #1 }
+ { \tex_the:D \tex_toks:D #1 }
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \int_eval:w #1 + \c_one \exp_after:wN ;
\fi:
- \regex_match_one_active_aux:n
- { \int_value:w \tex_skip:D #1 }
- \exp_after:wN \regex_match_one_active:w
- \int_use:N \int_eval:w #1 + \c_one ;
}
-\cs_new_protected:Npn \regex_match_one_active_aux:n #1
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_query_set:nnn}
+% The arguments are: tokens that \texttt{o} and \texttt{x} expand to
+% one token of the query, the catcode, and the character code. Store
+% those, and the current brace balance (used later to check for
+% overall brace balance) in a \tn{muskip} register and a \tn{toks},
+% then update the \texttt{balance}.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_query_set:nnn #1#2#3
{
- \int_set:Nn \l_regex_current_state_int {#1}
- \prop_clear:N \l_regex_current_submatches_prop
- \regex_state_use_with_submatches:
+ \tex_muskip:D \l_regex_current_pos_int
+ = \etex_gluetomu:D
+ #3 sp
+ plus #2 sp
+ minus \l_regex_balance_int sp
+ \scan_stop:
+ \tex_toks:D \l_regex_current_pos_int {#1}
+ \int_incr:N \l_regex_current_pos_int
+ \if_case:w #2 \exp_stop_f:
+ \or: \int_incr:N \l_regex_balance_int
+ \or: \int_decr:N \l_regex_balance_int
+ \fi:
}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_query_get:}
+% Extract the current character and category codes from the
+% \tn{muskip} register of the current position: those are the main and
+% the stretch components, and we need a conversion to avoid \TeX{}'s
+% \enquote{incompatible glue units} error.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_query_get:
+ {
+ \l_regex_current_char_int
+ = \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ \l_regex_current_catcode_int = \etex_gluestretch:D
+ \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ }
+% \end{macrocode}
% \end{macro}
+%
+% \subsubsection{Using states of the \textsc{nfa}}
+%
+% \begin{macro}[int]{\regex_use_state:}
+% Use the current \textsc{nfa} instruction. The state is initially
+% marked as belonging to the current \texttt{step}: this allows normal
+% free transition to repeat, but group-repeating transitions
+% won't. Once we are done exploring all the branches it spawned, the
+% state is marked as $\texttt{step}+1$: any thread hitting it at that
+% point will be terminated.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_use_state:
+ {
+%<*trace>
+ \trace:nnx { regex } { 2 } { state~\int_use:N \l_regex_current_state_int }
+%</trace>
+ \tex_dimen:D \l_regex_current_state_int
+ = \l_regex_step_int sp \scan_stop:
+ \tex_the:D \tex_toks:D \l_regex_current_state_int
+ \tex_dimen:D \l_regex_current_state_int
+ = \int_eval:w \l_regex_step_int + \c_one \int_eval_end: sp \scan_stop:
+ }
+% \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{macro}[int]{\regex_use_state_and_submatches:nn}
+% This function is called as one item in the array of active threads
+% after that array has been unpacked for a new step. Update the
+% \texttt{current_state} and \texttt{current_submatches} and use the
+% state if it has not yet been encountered at this step.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_match_loop_case_hook: { }
-\cs_new_protected_nopar:Npn \regex_match_loop_caseless_hook:
+\cs_new_protected:Npn \regex_use_state_and_submatches:nn #1 #2
{
- \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:
+ \int_set:Nn \l_regex_current_state_int {#1}
+ \if_num:w \tex_dimen:D \l_regex_current_state_int < \l_regex_step_int
+ \tl_set:Nn \l_regex_current_submatches_prop {#2}
+ \exp_after:wN \regex_use_state:
\fi:
+ \scan_stop:
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
% \subsubsection{Actions when matching}
%
% \begin{macro}[int]{\regex_action_start_wildcard:}
-%: 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
-% next token in the query. This combination
-% could be reused (with some changes). We sometimes need
-% to know that the match for a given thread starts at
-% this character. For that, we use the boolean
-% \cs{l_regex_fresh_thread_bool}.
+% For an unanchored match, state $0$ has a free transition to the next
+% and a costly one to itself, to repeat at the next position. To catch
+% repeated identical empty matches, we need to know if a successful
+% thread corresponds to an empty match. The instruction resetting
+% \cs{l_regex_fresh_thread_bool} may be skipped by a successful
+% thread, hence we had to add it to \cs{regex_match_loop:} too.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \regex_action_start_wildcard:
{
@@ -4116,176 +4416,348 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[int]{\regex_action_free:n, \regex_action_free_group:n}
+% \begin{macro}[aux]{\regex_action_free_aux:nn}
+% These functions copy a thread after checking that the \textsc{nfa}
+% state has not already been used at this position. If not, store
+% submatches in the new state, and insert the instructions for that
+% state in the input stream. Then restore the old value of
+% \cs{l_regex_current_state_int} and of the current submatches. The
+% two types of free transitions differ by how they test that the state
+% has not been encountered yet: the \texttt{group} version is
+% stricter, and will not use a state if it was used earlier in the
+% current thread, hence forcefully breaking the loop, while the
+% \enquote{normal} version will revisit a state when within the thread
+% itself.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_action_free:n
+ { \regex_action_free_aux:nn { > \l_regex_step_int \else: } }
+\cs_new_protected_nopar:Npn \regex_action_free_group:n
+ { \regex_action_free_aux:nn { < \l_regex_step_int } }
+\cs_new_protected:Npn \regex_action_free_aux:nn #1#2
+ {
+ \use:x
+ {
+ \int_add:Nn \l_regex_current_state_int {#2}
+ \exp_not:n
+ {
+ \if_num:w \tex_dimen:D \l_regex_current_state_int #1
+ \exp_after:wN \regex_use_state:
+ \fi:
+ }
+ \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 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
% \begin{macro}[int]{\regex_action_cost:n}
-% A transition which consumes the current character and moves
-% to state |#1|.
+% A transition which consumes the current character and shifts the
+% state by |#1|. The resulting state is stored in the \tn{skip} array
+% for use at the next position, and we also store the current
+% submatches.
% \begin{macrocode}
\cs_new_protected:Npn \regex_action_cost:n #1
{
- \exp_args:Nf \regex_store_state:n
- { \int_eval:n { \l_regex_current_state_int + #1 } }
+ \exp_args:No \regex_store_state:n
+ { \int_use:N \int_eval:w \l_regex_current_state_int + #1 }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_action_success:}
-% There is a successful match when an execution path reaches
-% the end of the regular expression. Then store the current
-% step and submatches. The current step is then interrupted
-% with \cs{prg_map_break:},
-% and only paths with higher precedence are pursued further.
-% The values stored here may be overwritten by a later success
-% of a path with higher precedence.
+% \begin{macro}[int]{\regex_store_state:n}
+% \begin{macro}[aux]{\regex_store_submatches:}
+% Put the given state in the array of \tn{skip} registers (converted
+% to a dimension in scaled points), and increment the length of the
+% array. Then store the current submatch in the This is done by
+% increasing the pointer \cs{l_regex_max_active_int}, and converting
+% the integer to a dimension (suitable for a \tn{skip} assignment) in
+% scaled points.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_action_success:
+\cs_new_protected:Npn \regex_store_state:n #1
{
- \regex_if_two_empty_matches:F
- {
- \bool_set_true:N \l_regex_success_match_bool
- \bool_set_eq:NN \l_regex_success_empty_bool
- \l_regex_fresh_thread_bool
- \int_set_eq:NN \l_regex_success_index_int \l_regex_current_index_int
- \prop_set_eq:NN \l_regex_success_submatches_prop
- \l_regex_current_submatches_prop
- \prg_map_break:
- }
+ \regex_store_submatches:
+ \tex_skip:D \l_regex_max_active_int = #1 sp \scan_stop:
+ \int_incr:N \l_regex_max_active_int
+ }
+\cs_new_protected_nopar:Npn \regex_store_submatches:
+ {
+ \tex_toks:D \l_regex_max_active_int \exp_after:wN
+ { \l_regex_current_submatches_prop }
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \begin{macro}[int]{\regex_action_free:n}
-% To copy a thread, check whether the program state has already
-% been used at this character. If not, store submatches in the
-% new state, and insert the instructions for that state in the
-% input stream.
-% Then restore the old value of \cs{l_regex_current_state_int}
-% and of the current submatches.
+% \begin{macro}[int]{\regex_disable_submatches:}
+% Some user functions don't require tracking submatches.
+% We get a performance improvement by simply defining the
+% relevant functions to remove their argument and do nothing
+% with it.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_action_free:n #1
+\cs_new_protected_nopar:Npn \regex_disable_submatches:
{
- \use:x
- {
- \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 }
- }
+ \cs_set_protected_nopar:Npn \regex_store_submatches: { }
+ \cs_set_protected:Npn \regex_action_submatch:n ##1 { }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\regex_action_submatch:n}
-% Update the current submatches with the information
-% from the current index.
+% Update the current submatches with the information from the current
+% position. Maybe a bottleneck.
% \begin{macrocode}
\cs_new_protected:Npn \regex_action_submatch:n #1
{
\prop_put:Nno \l_regex_current_submatches_prop {#1}
- { \int_use:N \l_regex_current_index_int }
+ { \int_use:N \l_regex_current_pos_int }
}
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{??}%^^A todo: title
-%
-% \begin{macro}{\regex_if_match_empty:T, \regex_if_match_empty:F}
-%
+% \begin{macro}[int]{\regex_action_success:}
+% There is a successful match when an execution path reaches the last
+% state in the \textsc{nfa}, unless this marks a second identical
+% empty match. Then mark that there was a successful match; it is
+% empty if it is \enquote{fresh}; and we store the current position
+% and submatches. The current step is then interrupted with
+% \cs{prg_map_break:}, and only paths with higher precedence are
+% pursued further. The values stored here may be overwritten by a
+% later success of a path with higher precedence.
% \begin{macrocode}
-\cs_new:Npn \regex_if_match_empty:T
- { \int_compare:nNnT \l_regex_start_index_int = \l_regex_current_index_int }
-\cs_new:Npn \regex_if_match_empty:F
- { \int_compare:nNnF \l_regex_start_index_int = \l_regex_current_index_int }
+\cs_new_protected_nopar:Npn \regex_action_success:
+ {
+ \regex_if_two_empty_matches:F
+ {
+ \bool_set_true:N \l_regex_match_success_bool
+ \bool_set_eq:NN \l_regex_empty_success_bool
+ \l_regex_fresh_thread_bool
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_current_pos_int
+ \prop_set_eq:NN \l_regex_success_submatches_prop
+ \l_regex_current_submatches_prop
+ \prg_map_break:
+ }
+ }
% \end{macrocode}
% \end{macro}
%
% \subsection{Replacement}
%
-% \begin{macro}[rEXP]{\regex_submatch_nesting_aux:n}
+% \subsubsection{Variables and helpers used in replacement}
+%
+% \begin{variable}{\l_regex_replacement_csnames_int}
+% The behaviour of closing braces inside a replacement text depends on
+% whether a sequences |\c{| or |\u{| has been encountered. The number
+% of \enquote{open} such sequences that should be closed by |}| is
+% stored in \cs{l_regex_replacement_csnames_int}, and decreased by
+% $1$ by each |}|.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_submatch_nesting_aux:n #1
- {
- + \etex_glueshrink:D \etex_mutoglue:D \etex_muexpr:D
- \tex_muskip:D \etex_gluestretch:D \tex_skip:D #1
- - \tex_muskip:D \tex_skip:D #1
- \scan_stop:
+\int_new:N \l_regex_replacement_csnames_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_regex_balance_tl}
+% This token list holds the replacement text for
+% \cs{regex_replacement_balance_one_match:n} while it is being built
+% incrementally.
+% \begin{macrocode}
+\tl_new:N \l_regex_balance_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[aux, rEXP]{\regex_replacement_balance_one_match:n}
+% This expects as an argument the first index of a range of \tn{skip}
+% registers which hold the submatch information for a given match. It
+% can be used within an integer expression to obtain the brace balance
+% incurred by performing the replacement on that match. This combines
+% the braces lost by removing the match, braces added by all the
+% submatches appearing in the replacement, and braces appearing
+% explicitly in the replacement. Even though it is always redefined
+% before use, we initialize it as for an empty replacement. An
+% important property is that concatenating several calls to that
+% function must result in a valid integer expression (hence a leading
+% |+| in the actual definition).
+% \begin{macrocode}
+\cs_new:Npn \regex_replacement_balance_one_match:n #1
+ { - \regex_submatch_balance:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, rEXP]{\regex_replacement_do_one_match:n}
+% The input is the same as \cs{regex_replacement_balance_one_match:n}.
+% This function is redefined to expand to the part of the token list
+% from the end of the previous match to a given match, followed by the
+% replacement text. Hence concatenating the result of this function
+% with all possible arguments (one call for each match), as well as
+% the range from the end of the last match to the end of the string,
+% will produce the fully replaced token list. The initialization does
+% not matter, but we set it as for an empty replacement.
+% \begin{macrocode}
+\cs_new:Npn \regex_replacement_do_one_match:n #1
+ {
+ \regex_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D #1 }
+ { \tex_skip:D #1 }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_replacement:n}
-% Our goal here is to analyse the replacement text. First take care
-% of detecting escaped and non-escaped characters using
-% \cs{regex_escape_use:nnnn} with three protected arguments.
-% This inserts in the input stream a token list of the form
-% \meta{fn 1} \meta{char 1} \ldots{} \meta{fn $N$} \meta{char $N$},
-% where \meta{fn $i$} is one of the three functions, and \meta{char $i$}
-% a character in the string |#1|.
-% \begin{macrocode}
-\cs_new:Npn \regex_nesting:n #1 { } %^^A move. Rename?
-\tl_new:N \l_regex_nesting_tl
-\cs_new_protected:Npn \regex_replacement:n #1
+% \begin{macro}[aux]{\regex_replacement_exp_not:N}
+% This function lets us navigate around the fact that the primitive
+% \cs{exp_not:n} requires a braced argument. As far as I can tell, it
+% is only needed if the user tries to include in the replacement text
+% a control sequence set equal to a macro parameter character, such as
+% \cs{c_parameter_token}. Indeed, within an \texttt{x}-expanding
+% assignment, \cs{exp_not:N}~|#| behaves as a single |#|, whereas
+% \cs{exp_not:n}~|{#}| behaves as a doubled |##|.
+% \begin{macrocode}
+\cs_new:Npn \regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Query and brace balance}
+%
+% \begin{macro}[int, rEXP]{\regex_query_range:nn}
+% \begin{macro}[aux, rEXP]{\regex_query_range_loop:ww}
+% When it is time to extract submatches from the token list, the
+% various tokens are stored in \tn{toks} registers numbered from
+% \cs{l_regex_min_pos_int} inclusive to \cs{l_regex_max_pos_int}
+% exclusive. The function \cs{regex_query_range:nn} \Arg{min}
+% \Arg{max} unpacks registers from the position \meta{min} to the
+% position $\meta{max}-1$ included. Once this is expanded, a second
+% \texttt{x}-expansion will result in the actual tokens from the
+% query. That second expansion is only done by user functions at the
+% very end of their operation, after checking (and correcting) the
+% brace balance first.
+% \begin{macrocode}
+\cs_new:Npn \regex_query_range:nn #1#2
{
- \int_zero:N \l_regex_replacement_int
- \int_zero:N \l_regex_nesting_int
- \tl_clear:N \l_regex_nesting_tl
- \regex_escape_use:nnnn
- { \regex_replacement_unescaped:N ##1 }
- { \regex_replacement_escaped:N ##1 }
- { \regex_replacement_raw:N ##1 }
- {#1}
- \prg_do_nothing: \prg_do_nothing:
- \cs_set:Npx \regex_nesting:n ##1
- {
- + \int_use:N \l_regex_nesting_int
- \l_regex_nesting_tl
- - \regex_submatch_nesting_aux:n {##1}
- }
- \use:x
- {
- \exp_not:n { \cs_set:Npn \regex_replacement_tl:n ##1 }
- { \regex_toks_range:nn \c_zero \l_regex_replacement_int }
- }
- % ^^A rename!
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 \exp_after:wN ;
+ \int_use:N \int_eval:w #2 ;
+ \prg_break_point:n { }
+ }
+\cs_new:Npn \regex_query_range_loop:ww #1 ; #2 ;
+ {
+ \if_num:w #1 < #2 \exp_stop_f:
+ \else:
+ \exp_after:wN \prg_map_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \exp_stop_f:
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 + \c_one ; #2 ;
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \begin{macro}[aux]{\regex_replacement_raw:N}
+% \begin{macro}[int]{\regex_query_submatch:n}
+% When this function is called, \tn{skip}$i$ holds the start and end
+% positions for the $i$-th overall submatch as its main and stretch
+% components. In the case of repeated matches, submatches from all the
+% matches are put one after the other in blocks of
+% \cs{l_regex_capturing_group_int} \tn{skip} registers.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_replacement_raw:N #1
- { \regex_replacement_put:n {#1} }
+\cs_new:Npn \regex_query_submatch:n #1
+ {
+ \regex_query_range:nn
+ { \tex_skip:D \int_eval:w #1 }
+ { \etex_gluestretch:D \tex_skip:D \int_eval:w #1 }
+ }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_replacement_put:n}
-% Raw characters are stored in a toks register.
+% \begin{macro}[rEXP]{\regex_submatch_balance:n}
+% Every user function must result in a balanced token list (unbalanced
+% token lists cannot be stored by TeX). When we unpacked the query, we
+% kept track of the brace balance as the shrink component of
+% \tn{muskip} registers, hence the contribution from a given range is
+% the difference between the shrink components of
+% \tn{muskip}\meta{max~pos} and \tn{muskip}\meta{min~pos}. For the
+% $i$-th submatch, the end-points of the range are the main and
+% stretch components of \tn{skip}$i$. The trailing \cs{scan_stop:} is
+% gobbled by \cs{etex_muexpr:D}, and the whole expression can be cast
+% safely to an integer (no trailing expansion).
% \begin{macrocode}
-\cs_new_protected:Npn \regex_replacement_put:n #1
+\cs_new_protected:Npn \regex_submatch_balance:n #1
{
- \tex_toks:D \l_regex_replacement_int {#1}
- \int_incr:N \l_regex_replacement_int
+ \etex_glueshrink:D \etex_mutoglue:D \etex_muexpr:D
+ \tex_muskip:D \etex_gluestretch:D \tex_skip:D #1
+ - \tex_muskip:D \tex_skip:D #1
+ \scan_stop:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_replacement_escaped:N}
-% \begin{macro}[aux]{\regex_replacement_submatch:w}
-% \begin{macro}[aux]{\regex_replacement_submatch_aux:nN}
+% \subsubsection{Framework}
+%
+% \begin{macro}[int]{\regex_replacement:n}
+% \begin{macro}[aux]{\regex_replacement_aux:n}
+% The replacement text is built incrementally by abusing \tn{toks}
+% within a group (see \pkg{l3tl-build}). We keep track in
+% \cs{l_regex_balance_int} of the balance of explicit begin- and
+% end-group tokens and \cs{l_regex_balance_tl} will consist of some
+% code to compute the brace balance from submatches (see its
+% description). Detect unescaped right braces, and escaped characters,
+% with trailing \cs{prg_do_nothing:} because some of the later
+% function look-ahead. Once the whole replacement text has been
+% parsed, make sure that there is no open csname. Finally, define the
+% \texttt{balance_one_match} and \texttt{do_one_match} functions.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_replacement_unescaped:N #1
+\cs_new_protected:Npn \regex_replacement:n #1
{
- \if_charcode:w \c_rbrace_str #1
- \if_num:w \l_regex_replacement_csnames_int > \c_zero
- \regex_replacement_put:n \cs_end:
- \else:
- \regex_replacement_put:n #1
+%<trace> \trace_push:nnn { regex } { 1 } { regex_replacement:n }
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_balance_int
+ \tl_clear:N \l_regex_balance_tl
+ \regex_escape_use:nnnn
+ {
+ \if_charcode:w \c_rbrace_str ##1
+ \regex_replacement_rbrace:N \else: \tl_build_one:n \fi: ##1
+ }
+ { \regex_replacement_escaped:N ##1 }
+ { \tl_build_one:n ##1 }
+ {#1}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \msg_kernel_error:nnx { regex } { replacement-missing-rbrace }
+ { \int_use:N \l_regex_replacement_csnames_int }
+ \tl_build_one:x
+ { \prg_replicate:nn \l_regex_replacement_csnames_int \cs_end: }
\fi:
- \else:
- \regex_replacement_put:n #1
- \fi:
+ \cs_gset:Npx \regex_replacement_balance_one_match:n ##1
+ {
+ + \int_use:N \l_regex_balance_int
+ \l_regex_balance_tl
+ - \regex_submatch_balance:n {##1}
+ }
+ \tl_build_end:
+ \exp_args:No \regex_replacement_aux:n \l_regex_internal_a_tl
+%<trace> \trace_pop:nnn { regex } { 1 } { regex_replacement:n }
}
+\cs_new_protected:Npn \regex_replacement_aux:n #1
+ {
+ \cs_set:Npn \regex_replacement_do_one_match:n ##1
+ {
+ \regex_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D ##1 }
+ { \tex_skip:D ##1 }
+ #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_escaped:N}
+% As in parsing a regular expression, we use an auxiliary built from
+% |#1| if defined. Otherwise, check for escaped digits (standing from
+% submatches from $0$ to $9$): anything else is a raw character.
+% \begin{macrocode}
\cs_new_protected:Npn \regex_replacement_escaped:N #1
{
\cs_if_exist_use:cF { regex_replacement_#1:w }
@@ -4293,34 +4765,45 @@
\if_num:w \c_one < 1#1 \exp_stop_f:
\regex_replacement_put_submatch:n {#1}
\else:
- \regex_replacement_put:n #1
+ \tl_build_one:n #1
\fi:
}
}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Submatches}
+%
+% \begin{macro}[aux]{\regex_replacement_put_submatch:n}
+% Insert a submatch in the replacement text. This is dropped if the
+% submatch number is larger than the number of capturing groups.
+% Unless the submatch appears inside a |\c{...}| or |\u{...}|
+% construction, it must be taken into account in the brace balance.
+% Here, |##1| will receive a pointer to the $0$-th submatch for a
+% given match. We cannot use \cs{int_eval:n} because it is
+% expandable, and would be expanded too early (short of adding
+% \cs{exp_not:N}, making the code messy again).
+% \begin{macrocode}
\cs_new_protected:Npn \regex_replacement_put_submatch:n #1
{
- \regex_replacement_put:n
- { \regex_query_submatch:nn {#1} {##1} }
- \if_num:w \l_regex_replacement_csnames_int = \c_zero
- \tl_put_right:Nn \l_regex_nesting_tl
- {
- \exp_not:N \if_num:w #1 < \l_regex_capturing_group_int
- \regex_submatch_nesting_aux:n { \int_eval:w #1+##1 \int_eval_end: }
- \exp_not:N \fi:
- }
+ \if_num:w #1 < \l_regex_capturing_group_int
+ \tl_build_one:n { \regex_query_submatch:n { #1 + ##1 } }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \tl_put_right:Nn \l_regex_balance_tl
+ { + \regex_submatch_balance:n { \int_eval:w #1+##1 \int_eval_end: } }
+ \fi:
\fi:
}
-\cs_new_protected:Npn \regex_replacement_error:NNN #1#2#3
- {
- \msg_kernel_error:nnxx { regex } { #1-command }
- { replacement~text } {#3}
- #2 #3
- }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_g:w, \regex_replacement_g_digits:NN}
+% An ugly method to grab digits for the |\g| escape sequence. At the
+% end of the run of digits, check that it ends with a right brace.
+% \begin{macrocode}
\cs_new_protected:Npn \regex_replacement_g:w #1#2
{
- \str_if_eq:xxTF
- { \exp_not:n { #1#2 } }
- { \regex_replacement_unescaped:N \c_lbrace_str }
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
{
\int_zero:N \l_regex_internal_a_int
\regex_replacement_g_digits:NN
@@ -4329,40 +4812,48 @@
}
\cs_new_protected:Npn \regex_replacement_g_digits:NN #1#2
{
- \token_if_eq_meaning:NNTF #1 \regex_replacement_unescaped:N
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
{
\if_num:w \c_one < 1#2 \exp_stop_f:
- \int_set:Nn \l_regex_internal_a_int { \c_ten * \l_regex_internal_a_int + #2 }
+ \int_set:Nn \l_regex_internal_a_int
+ { \c_ten * \l_regex_internal_a_int + #2 }
\exp_after:wN \use_i:nnn
\exp_after:wN \regex_replacement_g_digits:NN
\else:
- \if_charcode:w \c_rbrace_str #2
- \exp_args:No \regex_replacement_put_submatch:n
- { \int_use:N \l_regex_internal_a_int }
- \exp_after:wN \exp_after:wN \exp_after:wN \use_none:nn
- \else:
- \exp_after:wN \exp_after:wN
- \exp_after:wN \regex_replacement_error:NNN
- \exp_after:wN \exp_after:wN \exp_after:wN g
- \fi:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
\fi:
}
- { \regex_replacement_error:NNN g }
- #1 #2
- }
-\cs_new_protected:Npn \regex_replacement_u:w #1#2
- {
- \str_if_eq:xxTF { #1#2 } { \regex_replacement_unescaped:N \c_lbrace_str }
{
- \int_incr:N \l_regex_replacement_csnames_int
- \regex_replacement_put:n
- { \exp_not:n { \exp_after:wN \exp_not:V \cs:w } }
+ \if_meaning:w \regex_replacement_rbrace:N #1
+ \exp_args:No \regex_replacement_put_submatch:n
+ { \int_use:N \l_regex_internal_a_int }
+ \exp_after:wN \use_none:nn
+ \else:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
}
- { \regex_replacement_error:NNN u #1#2 }
+ #1 #2
}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Csnames in replacement}
+%
+% \begin{macro}[aux]{\regex_replacement_c:w}
+% \begin{macro}[aux]+\regex_replacement_c_{:w+
+% |\c| can be followed by a left brace, or by a letter for which we
+% have defined a way to produce that category of characters. The
+% appropriate definitions for catcodes are introduced later. For
+% control sequences, if we are within a control sequence, convert
+% the token list to a string, otherwise simply prevent expansion,
+% with a weird cross-over between \cs{exp_not:n} and \cs{exp_not:N}
+% (see this helper's description for an explanation).
+% \begin{macrocode}
\cs_new_protected:Npn \regex_replacement_c:w #1#2
{
- \token_if_eq_meaning:NNTF #1 \regex_replacement_unescaped:N
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
{
\cs_if_exist_use:cF { regex_replacement_c_#2:w }
{ \regex_replacement_error:NNN c #1#2 }
@@ -4371,245 +4862,341 @@
}
\cs_new_protected_nopar:cpn { regex_replacement_c_ \c_lbrace_str :w }
{
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n
+ { \exp_not:n { \exp_after:wN \regex_replacement_exp_not:N \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:N \cs:w } }
+ \fi:
\int_incr:N \l_regex_replacement_csnames_int
- \regex_replacement_put:n
- { \exp_not:n { \exp_after:wN \regex_replacement_exp_not:N \cs:w } }
}
-\cs_new:Npn \regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_u:w}
+% Check that |\u| is followed by a left brace. If so, start a control
+% sequence with \cs{cs:w}, which is then unpacked either with
+% \cs{exp_not:V} or \cs{tl_to_str:V} depending on the current context.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_replacement_u:w #1#2
+ {
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
+ {
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \exp_not:V \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:V \cs:w } }
+ \fi:
+ \int_incr:N \l_regex_replacement_csnames_int
+ }
+ { \regex_replacement_error:NNN u #1#2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_rbrace:N}
+% Within a |\c{...}| or |\u{...}| construction, end the control
+% sequence, and decrease the brace count. Otherwise, this is a raw
+% right brace.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_replacement_rbrace:N #1
+ {
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \tl_build_one:n \cs_end:
+ \int_decr:N \l_regex_replacement_csnames_int
+ \else:
+ \tl_build_one:n #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Characters in replacement}
+%
+% We will need to change the category code of the null character many
+% times, hence work in a group. The catcode-specific macros below are
+% defined in alphabetical order; if you are trying to understand the
+% code, start from the end of the alphabet as those categories are
+% simpler than active or begin-group.
+% \begin{macrocode}
\group_begin:
-
- \char_set_catcode_math_superscript:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_U:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_alignment:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_T:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \cs_new_protected:Npn \regex_replacement_c_S:w #1#2
+% \end{macrocode}
+%
+% \begin{macro}[aux]{\regex_replacement_char:nNN}
+% The only way to produce an arbitrary character--catcode pair is to
+% use the \tn{lowercase} or \tn{uppercase} primitives. This is a
+% wrapper for our purposes. The first argument is the null character
+% with various catcodes. The second and third arguments are grabbed
+% from the input stream: |#3| is the character whose character code to
+% reproduce.
+% \begin{macrocode}
+ \cs_new_protected:Npn \regex_replacement_char:nNN #1#2#3
{
- \int_compare:nNnTF { `#2 } = \c_zero
- { \regex_replacement_error:NNN c #1#2 }
- {
- \char_set_lccode:nn {32} { `#2 }
- \tl_to_lowercase:n { \regex_replacement_put:n {~} }
- }
+ \if_meaning:w \prg_do_nothing: #3
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \tex_lccode:D \c_zero = `#3 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {#1} }
+ \fi:
}
-
- \char_set_catcode_parameter:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_P:w
- { \regex_replacement_char:nNN { ^^@^^@^^@^^@^^@^^@^^@^^@ } }
-
- \char_set_catcode_other:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_O:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_math_toggle:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_M:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_letter:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_L:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_group_end:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_E:w
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_A:w}
+% For an active character, expansion must be avoided, twice because we
+% later do two \texttt{x}-expansions, to unpack \tn{toks} for the
+% query, and to expand their contents to tokens of the query.
+% \begin{macrocode}
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_A:w
+ { \regex_replacement_char:nNN { \exp_not:n { \exp_not:N ^^@ } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_B:w}
+% An explicit begin-group token increases the balance, unless within a
+% |\c{...}| or |\u{...}| construction. Add the desired begin-group
+% character, using the standard \cs{if_false:} trick. We eventually
+% \texttt{x}-expand twice. The first time must yield a balanced token
+% list, and the second one gives the bare begin-group token. The
+% \cs{exp_after:wN} is not strictly needed, but is more consistent
+% with \pkg{l3tl-analysis}.
+% \begin{macrocode}
+ \char_set_catcode_group_begin:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_B:w
{
- \int_decr:N \l_regex_nesting_int
- \regex_replacement_char:nNN { \if_false: { \fi: ^^@ }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_incr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_after:wN ^^@ \if_false: } \fi: } }
}
-
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_C:w}
+% This is not quite catcode-related: when the user requests a
+% character with category \enquote{control sequence}, the
+% one-character control symbol is returned. As for the active
+% character, we prepare for two \texttt{x}-expansions.
+% \begin{macrocode}
+ \cs_new_protected:Npn \regex_replacement_c_C:w #1#2
+ { \tl_build_one:n { \exp_not:N \exp_not:N \exp_not:c {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_D:w}
+% Subscripts fit the mould: \tn{lowercase} the null byte with the
+% correct category.
+% \begin{macrocode}
\char_set_catcode_math_subscript:N \^^@
\cs_new_protected_nopar:Npn \regex_replacement_c_D:w
{ \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_group_begin:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_B:w
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_E:w}
+% Similar to the begin-group case, the second \texttt{x}-expansion
+% produces the bare end-group token.
+% \begin{macrocode}
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_E:w
{
- \int_incr:N \l_regex_nesting_int
- \regex_replacement_char:nNN { \exp_after:wN ^^@ \if_false: } \fi: }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_decr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \if_false: { \fi: ^^@ } }
}
-
- \char_set_catcode_active:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_A:w
- { \regex_replacement_char:nNN { \exp_not:N ^^@ } }
-
-\group_end:
-
-\cs_new_protected:Npn \regex_replacement_char:nNN #1#2#3
- {
- \char_set_lccode:nn \c_zero { `#3 }
- \tl_to_lowercase:n
- { \regex_replacement_put:n { \exp_not:n {#1} } }
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \subsection{User commands}
-%
-% \subsubsection{Precompiled pattern}
-%
-% A given pattern is often reused to match many different query token lists.
-% We thus give a means of storing the NFA corresponding to a given
-% pattern in a token list variable of the form
-% \begin{quote}
-% \cs{regex_nfa:Nw} \meta{variable~name} \\
-% \meta{assignments} \\
-% \cs{tex_toks:D} 0 \{ \meta{instruction0} \} \\
-% \ldots{} \\
-% \cs{tex_toks:D} $n$ \{ \meta{instruction$\sb{n}$} \} \\
-% \cs{s_stop}
-% \end{quote}
-% where $n$ is the number of states in the NFA,
-% and the various \meta{instruction$\sb{i}$} control
-% how the NFA behaves in state $i$. The \cs{regex_nfa:Nw}
-% function removes the whole NFA from the input stream
-% and produces an error: the \meta{nfa var} should only be
-% accessed through dedicated functions. This rather drastic
-% approach is taken because assignments triggered by the
-% contents of \meta{nfa var} may overwrite data which is used
-% elsewhere, unless everything is done carefully in a group.
-%
-% \begin{macro}{\regex_gset:Nn}
-% \begin{macro}{\regex_const:Nn}
-% \begin{macro}{\regex_set:Nn}
-% \begin{macro}[aux]{\regex_set_aux:NNn}
-% The three user functions only differ with which function is used
-% to assign the pre-compiled regular expression to the user's variable.
-% Internally, they all first build the NFA corresponding to the regex,
-% then store the contents of all the necessary \tn{toks} registers
-% in the user's variable.
-% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_set:Nn
- { \regex_set_aux:NNn \tl_set:Nn }
-\cs_new_protected_nopar:Npn \regex_gset:Nn
- { \regex_set_aux:NNn \tl_gset:Nn }
-\cs_new_protected_nopar:Npn \regex_const:Nn
- { \regex_set_aux:NNn \tl_const:Nn }
-\cs_new_protected:Npn \regex_set_aux:NNn #1#2#3
- {
- \group_begin:
- \regex_build:n {#3}
- \use:x
- {
- \group_end:
- #1 \exp_not:N #2 { \regex_set_aux:N #2 }
- }
- }
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_L:w}
+% Simply \tn{lowercase} a letter null byte to produce an arbitrary letter.
+% \begin{macrocode}
+ \char_set_catcode_letter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_L:w
+ { \regex_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_M:w}
+% No surprise here, we lowercase the null math toggle.
+% \begin{macrocode}
+ \char_set_catcode_math_toggle:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_M:w
+ { \regex_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_O:w}
+% Lowercase an other null byte.
+% \begin{macrocode}
+ \char_set_catcode_other:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_O:w
+ { \regex_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int, rEXP]{\regex_set_aux:N}
-% \begin{macro}[aux, rEXP]{\regex_set_aux:n}
-% \begin{macro}[aux, EXP]{\regex_nfa:Nw}
-% Within a group, build the NFA corresponding to the given regular
-% expression, with submatch tracking. Then save the contents of all
-% relevant \tn{toks} registers into the variable outside the group.
-% The auxiliary \cs{regex_nfa:Nw} is not protected: this ensures that
-% the NFA will properly be replaced by an error message in expansion
-% contexts.
+% \begin{macro}[aux]{\regex_replacement_c_P:w}
+% For macro parameters, expansion is a tricky issue. We need to
+% prepare for two \texttt{x}-expansions and passing through various
+% macro definitions. Note that we cannot replace one \cs{exp_not:n} by
+% doubling the macro parameter characters because this would misbehave
+% if a mischievous user asks for |\c{\cP\#}|, since that macro
+% parameter character would be doubled.
% \begin{macrocode}
-\cs_new:Npn \regex_set_aux:N #1
- {
- \exp_not:n { \regex_nfa:Nw #1 }
- \l_regex_max_state_int
- = \int_use:N \l_regex_max_state_int
- \l_regex_capturing_group_int
- = \int_use:N \l_regex_capturing_group_int
- \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
- \c_zero \c_one {\l_regex_max_state_int - \c_one }
- \regex_set_aux:n
- \s_stop
- }
-\cs_new:Npn \regex_set_aux:n #1
- { \tex_toks:D #1 { \tex_the:D \tex_toks:D #1 } }
-\cs_new:Npn \regex_nfa:Nw #1
- {
- \msg_expandable_kernel_error:nnn { regex } { nfa-misused } {#1}
- \use_none_delimit_by_s_stop:w
- }
+ \char_set_catcode_parameter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_P:w
+ {
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_not:n { ^^@^^@^^@^^@ } } }
+ }
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_S:w}
+% Spaces are normalized on input by \TeX{} to have character code
+% $32$. It is in fact impossible to get a token with character code
+% $0$ and category code $10$. Hence we use $32$ instead of $0$ as our
+% base character.
+% \begin{macrocode}
+ \cs_new_protected:Npn \regex_replacement_c_S:w #1#2
+ {
+ \if_meaning:w \prg_do_nothing: #2
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \if_num:w `#2 = \c_zero
+ \msg_kernel_error:nn { regex } { replacement-null-space }
+ \fi:
+ \tex_lccode:D 32 = `#2 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {~} }
+ \fi:
+ }
+% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_replacement_c_T:w}
+% No surprise for alignment tabs here. Those are surrounded by the
+% appropriate braces whenever necessary, hence they don't cause
+% trouble in alignment settings.
+% \begin{macrocode}
+ \char_set_catcode_alignment:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_T:w
+ { \regex_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\regex_use:N}
-% No error-checking.
+% \begin{macro}[aux]{\regex_replacement_c_U:w}
+% Simple call to \cs{regex_replacement_char:nNN} which lowercases the
+% math superscript |^^@|.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_use:N
- { \exp_last_unbraced:No \use_none:nn }
+ \char_set_catcode_math_superscript:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_U:w
+ { \regex_replacement_char:nNN { ^^@ } }
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Generic auxiliary functions}
+% Restore the catcode of the null byte.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
%
-% Most of \pkg{l3regex}'s work is done within a group.
+% \subsubsection{An error}
%
-% \begin{macro}[aux]{\regex_aux_return:}
-% This function triggers either \cs{prg_return_false:}
-% or \cs{prg_return_true:} as appropriate to whether a
-% match was found or not.
+% \begin{macro}[aux]{\regex_replacement_error:NNN}
+% Simple error reporting by calling one of the messages
+% \texttt{replacement-c}, \texttt{replacement-g}, or
+% \texttt{replacement-u}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_aux_return:
+\cs_new_protected:Npn \regex_replacement_error:NNN #1#2#3
{
- \if_meaning:w \c_true_bool \g_regex_success_bool
- \prg_return_true:
- \else:
- \prg_return_false:
- \fi:
+ \msg_kernel_error:nnx { regex } { replacement-#1 } {#3}
+ #2 #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{User functions}
+%
+% \begin{macro}{\regex_new:N}
+% Before being assigned a sensible value, a regex variable matches
+% nothing.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_new:N #1
+ { \cs_new_eq:NN #1 \c_regex_no_match_regex }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\regex_set:Nn, \regex_gset:Nn, \regex_const:Nn}
+% Compile, then store the result in the user variable with the
+% appropriate assignment function.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_set:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_set_eq:NN #1 \l_regex_internal_regex
+ }
+\cs_new_protected_nopar:Npn \regex_gset:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_gset_eq:NN #1 \l_regex_internal_regex
+ }
+\cs_new_protected_nopar:Npn \regex_const:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_const:Nx #1 { \exp_not:o \l_regex_internal_regex }
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_aux_build_match:nn}
-% This auxiliary is used by user functions whose \meta{regex} argument
-% is given as an explicit regular expression within braces. In that
-% case, we need to build the automaton corresponding to that regular
-% expression, then perform the matching on the given token list |#2|.
+% \begin{macro}{\regex_show:N, \regex_show:n}
+% User functions: the \texttt{n} variant requires compilation first.
+% Then show the variable with some appropriate text. The auxiliary
+% \cs{regex_show_aux:Nx} is defined in a different section.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_aux_build_match:nn #1#2
+\cs_new_protected:Npn \regex_show:n #1
{
- \regex_build:n {#1}
- \regex_match:n {#2}
+ \regex_compile:n {#1}
+ \regex_show_aux:Nx \l_regex_internal_regex
+ { { \tl_to_str:n {#1} } }
}
+\cs_new_protected:Npn \regex_show:N #1
+ { \regex_show_aux:Nx #1 { variable~\token_to_str:N #1 } }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_aux_use_match:Nn}
-% This auxiliary is used by user functions whose \meta{regex} argument
-% is given as a pre-compiled regex variable. We make sure that
-% the token list variable indeed is an automaton (by testing the
-% first token). If not, the match is deemed unsuccessful, after
-% raising an error. If we have an automaton, \enquote{use} it,
-% then perform the matching on the given token list |#2|.
+% \begin{macro}[TF]{\regex_match:nn, \regex_match:Nn}
+% Those conditionals are based on a common auxiliary defined
+% later. Its first argument builds the \textsc{nfa} corresponding to
+% the regex, and the second argument is the query token list. Once we
+% have performed the match, convert the resulting boolean to
+% \cs{prg_return_true:} or \texttt{false}.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_aux_use_match:Nn #1#2
+\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
{
- \exp_args:No \tl_if_head_eq_meaning:nNTF {#1} \regex_nfa:Nw
- {
- \regex_use:N #1
- \regex_match:n {#2}
- }
- {
- \msg_kernel_error:nnx { regex } { not-nfa } { \token_to_str:N #1 }
- \bool_gset_false:N \g_regex_success_bool
- }
+ \regex_match_aux:nn { \regex_build:n {#1} } {#2}
+ \regex_aux_return:
+ }
+\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \regex_match_aux:nn { \regex_build:N #1 } {#2}
+ \regex_aux_return:
}
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}{\regex_count:nnN, \regex_count:NnN}
+% Again, use an auxiliary whose first argument builds the \textsc{nfa}.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_count:nnN #1
+ { \regex_count_aux:nnN { \regex_build:n {#1} } }
+\cs_new_protected:Npn \regex_count:NnN #1
+ { \regex_count_aux:nnN { \regex_build:N #1 } }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}
% {
% \regex_extract_once:nnN, \regex_extract_once:NnN,
@@ -4626,92 +5213,206 @@
% \regex_replace_all:nnN, \regex_replace_all:NnN,
% \regex_split:nnN, \regex_split:NnN
% }
-% We define here $40$ user functions, following a common pattern
-% in terms of an auxiliary such as \cs{regex_extract_once_aux:NnnN}
-% (those auxiliaries are defined in the coming sections).
-% The arguments handed to the auxiliary are
-% \cs{regex_aux_build_match:nn} or \cs{regex_aux_use_match:Nn},
-% followed by the three arguments of the user function.
-% The conditionals call \cs{regex_aux_return:} to return
-% either \texttt{true} or \texttt{false} once matching
-% has been performed.
+% We define here $40$ user functions, following a common pattern in
+% terms of \texttt{_aux:nnN} auxiliaries, defined in the coming
+% subsections. The auxiliary is handed \cs{regex_build:n} or
+% \cs{regex_build:N} with the appropriate regex argument, then all
+% other necessary arguments (replacement text, token list, \emph{etc.}
+% The conditionals call \cs{regex_aux_return:} to return either
+% \texttt{true} or \texttt{false} once matching has been performed.
% \begin{macrocode}
\cs_set_protected:Npn \regex_tmp:w #1#2#3
{
- \cs_new_protected_nopar:Npn #1
- { #3 \regex_aux_build_match:nn }
- \cs_new_protected_nopar:Npn #2
- { #3 \regex_aux_use_match:Nn }
- \prg_new_protected_conditional:Npnn #1 ##1##2##3 { T , F , TF }
- {
- #3 \regex_aux_build_match:nn {##1} {##2} ##3
- \regex_aux_return:
- }
+ \cs_new_protected:Npn #2 ##1 { #1 { \regex_build:n {##1} } }
+ \cs_new_protected:Npn #3 ##1 { #1 { \regex_build:N ##1 } }
\prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
- {
- #3 \regex_aux_use_match:Nn {##1} {##2} ##3
- \regex_aux_return:
- }
- }
-\tl_map_inline:nn
- {
- { extract_once } { extract_all }
- { replace_once } { replace_all }
- { split }
- }
- {
- \exp_args:Nccc \regex_tmp:w
- { regex_#1:nnN } { regex_#1:NnN } { regex_#1_aux:NnnN }
+ { #1 { \regex_build:n {##1} } {##2} ##3 \regex_aux_return: }
+ \prg_new_protected_conditional:Npnn #3 ##1##2##3 { T , F , TF }
+ { #1 { \regex_build:N ##1 } {##2} ##3 \regex_aux_return: }
}
+\regex_tmp:w \regex_extract_once_aux:nnN
+ \regex_extract_once:nnN \regex_extract_once:NnN
+\regex_tmp:w \regex_extract_all_aux:nnN
+ \regex_extract_all:nnN \regex_extract_all:NnN
+\regex_tmp:w \regex_replace_once_aux:nnN
+ \regex_replace_once:nnN \regex_replace_once:NnN
+\regex_tmp:w \regex_replace_all_aux:nnN
+ \regex_replace_all:nnN \regex_replace_all:NnN
+\regex_tmp:w \regex_split_aux:nnN \regex_split:nnN \regex_split:NnN
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \subsubsection{Submatches, once the correct match is found}
+% \subsubsection{Variables and helpers for user functions}
%
-% \begin{macro}[int]{\regex_extract:}
-% \begin{macro}[aux]{\regex_extract_aux_b:wn}
-% \begin{macro}[aux]{\regex_extract_aux_e:wn}
+% \begin{variable}{\l_regex_match_count_int}
+% The number of matches found so far is stored
+% in \cs{l_regex_match_count_int}. This is only used
+% in the \cs{regex_count:nnN} functions.
+% \begin{macrocode}
+\int_new:N \l_regex_match_count_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{regex_begin, regex_end}
+% Those flags are raised to indicate extra begin-group
+% or end-group tokens when extracting submatches.
+% \begin{macrocode}
+\flag_new:n { regex_begin }
+\flag_new:n { regex_end }
+% \end{macrocode}
+% \end{variable}
%
+% \begin{variable}{\l_regex_submatch_int, \l_regex_zeroth_submatch_int}
+% The end-points of each submatch are stored as main and stretch
+% components of \tn{skip}\meta{submatch}, where \meta{submatch} ranges
+% from \cs{l_regex_max_state_int} (inclusive) to
+% \cs{l_regex_submatch_int} (exclusive). Each successful match comes
+% with a $0$-th submatch (the full match), and one match for each
+% capturing group: submatches corresponding to the last successful
+% match are labelled starting at
+% \texttt{zeroth_submatch}. Additionally, the shrink component of this
+% $0$-th submatch is the position at which that match attempt started:
+% this is used for splitting and replacements.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_extract:
+\int_new:N \l_regex_submatch_int
+\int_new:N \l_regex_zeroth_submatch_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[aux]{\regex_aux_return:}
+% This function triggers either \cs{prg_return_false:} or
+% \cs{prg_return_true:} as appropriate to whether a match was found or
+% not. It is used by all user conditionals.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_aux_return:
{
- \int_set_eq:NN \l_regex_submatch_start_int \l_regex_submatch_int
\if_meaning:w \c_true_bool \g_regex_success_bool
- \prg_replicate:nn \l_regex_capturing_group_int
- {
- \tex_skip:D \l_regex_submatch_int \c_zero sp \scan_stop:
- \int_incr:N \l_regex_submatch_int
- }
- \prop_map_inline:Nn \l_regex_success_submatches_prop
- {
- \if_num:w ##1 \c_max_int
- \exp_after:wN \regex_extract_aux_b:wn \int_use:N
- \else:
- \exp_after:wN \regex_extract_aux_e:wn \int_use:N
- \fi:
- \int_eval:w \l_regex_submatch_start_int + ##1 {##2}
- }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
\fi:
}
-\cs_new_protected:Npn \regex_extract_aux_b:wn #1 < #2
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Matching}
+%
+% \begin{macro}[aux]{\regex_match_aux:nn}
+% We don't track submatches, and stop after a single match. Build the
+% \textsc{nfa} with |#1|, and perform the match on the query |#2|.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_match_aux:nn #1#2
{
- \tex_skip:D #1 = #2 sp
- plus \etex_gluestretch:D \tex_skip:D #1 \scan_stop:
+ \group_begin:
+ \regex_disable_submatches:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \group_end:
}
-\cs_new_protected:Npn \regex_extract_aux_e:wn #1 > #2
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_count_aux:nnN}
+% Again, we don't care about submatches. Instead of aborting after the
+% first \enquote{longest match} is found, we search for multiple
+% matches, incrementing \cs{l_regex_match_count_int} every time to
+% record the number of matches. Build the \textsc{nfa} and match. At
+% the end, store the result in the user's variable.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_count_aux:nnN #1#2#3
{
- \tex_skip:D #1
- = 1 \tex_skip:D #1 plus #2 sp \scan_stop:
+ \group_begin:
+ \regex_disable_submatches:
+ \int_zero:N \l_regex_match_count_int
+ \regex_multi_match:n { \int_incr:N \l_regex_match_count_int }
+ #1
+ \regex_match:n {#2}
+ \exp_args:NNNo
+ \group_end:
+ \int_set:Nn #3 { \int_use:N \l_regex_match_count_int }
}
% \end{macrocode}
% \end{macro}
+%
+% \subsubsection{Extracting submatches}
+%
+% \begin{macro}[aux]{\regex_extract_once_aux:nnN, \regex_extract_all_aux:nnN}
+% Match once or multiple times. After each match (or after the only
+% match), extract the submatches using \cs{regex_extract:}. At the
+% end, store the sequence containing all the submatches into the user
+% variable |#3| after closing the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_extract_once_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \regex_extract:
+ \regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \regex_extract_all_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_match:n {#2}
+ \regex_group_end_extract_seq:N #3
+ }
+% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[aux]{\regex_split_aux:nnN}
+% Splitting at submatches is a bit more tricky. For each match,
+% extract all submatches, and replace the zeroth submatch by the part
+% of the query between the start of the match attempt and the start of
+% the zeroth submatch. This is inhibited if the delimiter matched an
+% empty token list at the start of this match attempt. After the last
+% match, store the last part of the token list, which ranges from the
+% start of the match attempt to the end of the query. This step is
+% inhibited if the last match was empty and at the very end: decrement
+% \cs{l_regex_submatch_int}, which controls which \tn{skip} registers
+% will be used.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_split_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_multi_match:n
+ {
+ \if_num:w \l_regex_start_pos_int < \l_regex_success_pos_int
+ \regex_extract:
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \l_regex_start_pos_int sp
+ plus \tex_skip:D \l_regex_zeroth_submatch_int \scan_stop:
+ \fi:
+ }
+ #1
+ \regex_match:n {#2}
+%<assert>\assert_int:n { \l_regex_current_pos_int = \l_regex_max_pos_int }
+ \tex_skip:D \l_regex_submatch_int
+ = \l_regex_start_pos_int sp plus \l_regex_max_pos_int sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \if_num:w \l_regex_start_pos_int = \l_regex_max_pos_int
+ \int_decr:N \l_regex_submatch_int
+ \fi:
+ \fi:
+ \regex_group_end_extract_seq:N #3
+ }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\regex_group_end_extract_seq:N}
-% \begin{macro}[aux]{\regex_extract_seq_aux:n}
-% \begin{macro}[aux]{\regex_extract_seq_aux:ww}
+% \begin{macro}[aux]{\regex_group_end_extract_seq:N}
+% The end-points of submatches are stored as the main and stretch
+% components of \tn{skip} registers from \cs{l_regex_max_state_int} to
+% \cs{l_regex_submatch_int} (exclusive). Extract the relevant ranges
+% into \cs{l_regex_internal_a_tl}. We detect unbalanced results using
+% the two flags \texttt{regex_begin} and \texttt{regex_end}, raised
+% whenever we see too many begin-group or end-group tokens in a
+% submatch. We disable \cs{seq_item:n} to prevent two
+% \texttt{x}-expansions.
% \begin{macrocode}
\cs_new_protected:Npn \regex_group_end_extract_seq:N #1
{
@@ -4721,7 +5422,7 @@
\tl_set:Nx \l_regex_internal_a_tl
{
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\c_one
{ \l_regex_submatch_int - \c_one }
\regex_extract_seq_aux:n
@@ -4729,17 +5430,33 @@
\int_compare:nNnF
{ \flag_height:n { regex_begin } + \flag_height:n { regex_end } }
= \c_zero
- { \msg_kernel_error:nn { regex } { sequence-unbalanced } }
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #1 \l_regex_internal_a_tl
+ {
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \flag_height:n { regex_end } }
+ { \flag_height:n { regex_begin } }
+ }
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1 { \l_regex_internal_a_tl }
+ }
}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]{\regex_extract_seq_aux:n, \regex_extract_seq_aux:ww}
+% The \texttt{:n} auxiliary builds one item of the sequence of
+% submatches. First compute the brace balance of the submatch, then
+% extract the submatch from the query, adding the appropriate braces
+% and raising a flag if the submatch is not balanced.
+% \begin{macrocode}
\cs_new:Npn \regex_extract_seq_aux:n #1
{
\seq_item:n
{
\exp_after:wN \regex_extract_seq_aux:ww
- \int_value:w \regex_submatch_nesting_aux:n {#1} ; #1;
+ \int_value:w \regex_submatch_balance:n {#1} ; #1;
}
}
\cs_new:Npn \regex_extract_seq_aux:ww #1; #2;
@@ -4748,7 +5465,7 @@
\flag_raise:n { regex_end }
\prg_replicate:nn {-#1} { \exp_not:n { { \if_false: } \fi: } }
\fi:
- \regex_query_submatch:w #2;
+ \regex_query_submatch:n {#2}
\if_num:w #1 > \c_zero
\flag_raise:n { regex_begin }
\prg_replicate:nn {#1} { \exp_not:n { \if_false: { \fi: } } }
@@ -4756,278 +5473,182 @@
}
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Matching}
-%
-% \begin{macro}[TF]{\regex_match:nn}
-% \begin{macro}[TF]{\regex_match:Nn}
-% \begin{macro}{\regex_match_aux:n}
-% We don't track submatches. Then either build the NFA corresponding
-% to the regular expression, or use a precompiled pattern. Then match,
-% using the internal \cs{regex_match:n}. Finally return the result
-% after closing the group.
-% \begin{macrocode}
-\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
- {
- \regex_match_aux:n
- { \regex_aux_build_match:nn {#1} {#2} }
- }
-\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
- {
- \regex_match_aux:n
- { \regex_aux_use_match:Nn #1 {#2} }
- }
-\cs_new_protected:Npn \regex_match_aux:n #1
- {
- \group_begin:
- \tl_clear:N \l_regex_every_match_tl
- \regex_disable_submatches:
- #1
- \group_end:
- \regex_aux_return:
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
%
-% \begin{macro}{\regex_count:nnN}
-% \begin{macro}{\regex_count:NnN}
-% \begin{macro}{\regex_count_aux:n}
-% Instead of aborting once the first \enquote{longest match} is found,
-% we repeat the search. The code is such that the search will not
-% start on the same character, hence avoiding infinite loops.
+% \begin{macro}[aux]
+% {\regex_extract:, \regex_extract_aux_b:wn, \regex_extract_aux_e:wn}
+% Our task here is to extract from the property list
+% \cs{l_regex_success_submatches_prop} the list of end-points of
+% submatches, and store them in \tn{skip} registers, from
+% \cs{l_regex_zeroth_submatch_int} upwards. We begin by emptying those
+% \tn{skip} registers. Then for each \meta{key}--\meta{value} pair in
+% the property list update the appropriate \tn{skip} component. This
+% is somewhat a hack: the \meta{key} is a non-negative integer
+% followed by |<| or |>|, which we use in a comparison to $-1$. At the
+% end, store the information about the position at which the match
+% attempt started, as a shrink component.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \regex_count:nnN
- { \regex_count_aux:NnnN \regex_aux_build_match:nn }
-\cs_new_protected_nopar:Npn \regex_count:NnN
- { \regex_count_aux:NnnN \regex_aux_use_match:Nn }
-\cs_new_protected:Npn \regex_count_aux:NnnN #1#2#3#4
+\cs_new_protected_nopar:Npn \regex_extract:
{
- \group_begin:
- \regex_disable_submatches:
- \int_zero:N \l_regex_match_count_int
- \tl_set:Nn \l_regex_every_match_tl
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \int_set_eq:NN \l_regex_zeroth_submatch_int \l_regex_submatch_int
+ \prg_replicate:nn \l_regex_capturing_group_int
{
- \int_incr:N \l_regex_match_count_int
- \regex_match_once:
+ \tex_skip:D \l_regex_submatch_int \c_zero sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
}
- #1 {#2} {#3}
- \exp_args:NNNo
- \group_end:
- \int_set:Nn #4 { \int_use:N \l_regex_match_count_int }
- }
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \subsubsection{Submatch extraction}
-%
-% \begin{macro}[aux]{\regex_extract_once_aux:NnnN}
-% As announced, here comes the auxiliary for extracting one match.
-% Since we only want one match, \cs{l_regex_every_match_tl}
-% is empty, and does not trigger the matching code again.
-% After matching, \cs{regex_extract:} extracts submatches
-% into various \tn{skip} registers, and those are then concatenated
-% into a sequence by \cs{regex_group_end_extract_seq:N}. That function
-% is also responsible for closing the group.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_extract_once_aux:NnnN #1#2#3#4
- {
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl { \regex_extract: }
- #1 {#2} {#3}
- \regex_group_end_extract_seq:N #4
+ \prop_map_inline:Nn \l_regex_success_submatches_prop
+ {
+ \if_num:w ##1 \c_minus_one
+ \exp_after:wN \regex_extract_aux_e:wn \int_value:w
+ \else:
+ \exp_after:wN \regex_extract_aux_b:wn \int_value:w
+ \fi:
+ \int_eval:w \l_regex_zeroth_submatch_int + ##1 {##2}
+ }
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \tex_the:D \tex_skip:D \l_regex_zeroth_submatch_int
+ minus \l_regex_start_pos_int sp \scan_stop:
+ \fi:
}
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[aux]{\regex_extract_all_aux:NnnN}
-%^^A todo: update doc
-% The set of submatches will be built progressively
-% in \cs{l_regex_result_seq}. For each match, extract
-% the submatches, and concatenate that to the right of
-% the result sequence, then start matching again.
-% Finally, copy the result in the user's sequence variable.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_extract_all_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_extract_aux_b:wn #1 < #2
{
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- { \regex_extract: \regex_match_once: }
- #1 {#2} {#3}
- \regex_group_end_extract_seq:N #4
+ \tex_skip:D #1 = #2 sp
+ plus \etex_gluestretch:D \tex_skip:D #1 \scan_stop:
}
-% \end{macrocode}
-% \end{macro}
-%
-% \subsubsection{Splitting a token list by matches of a regex}
-%
-% \begin{macro}[aux]{\regex_split_aux:NnnN}
-% Recurse through the matches, and for each, do the following.
-% Extract the submatches into various \tn{skip} registers,
-% then replace the match |\0|, which should not be kept in the
-% final result, and replace it by the part of the token list
-% before the match.
-% This process must be inhibited to avoid creating empty items
-% if the regex matched an empty token list at the place where
-% the match attempt started.
-% After the last successful match, we need to add to the result
-% the part of the token list after the last match, unless the
-% last match was empty and at the very end.
-% Finally, \cs{regex_group_end_extract_seq:N} builds a sequence
-% from all the \tn{skip} registers, and assigns it to |#4|
-% after closing the group.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_split_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_extract_aux_e:wn #1 > #2
{
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- {
- \if_num:w \l_regex_start_index_int < \l_regex_success_index_int
- \regex_extract:
- \tex_skip:D \l_regex_submatch_start_int
- = \l_regex_start_index_int sp
- plus \tex_skip:D \l_regex_submatch_start_int \scan_stop:
- \fi:
- \regex_match_once:
- }
- #1 {#2} {#3}
- \tex_skip:D \l_regex_submatch_int
- = \l_regex_start_index_int sp
- plus \l_regex_current_index_int sp \scan_stop:
- \int_incr:N \l_regex_submatch_int
- \if_num:w \l_regex_start_index_int = \l_regex_current_index_int
- \if_meaning:w \c_true_bool \l_regex_success_empty_bool
- \int_decr:N \l_regex_submatch_int
- \fi:
- \fi:
- \regex_group_end_extract_seq:N #4
+ \tex_skip:D #1
+ = 1 \tex_skip:D #1 plus #2 sp \scan_stop:
}
% \end{macrocode}
% \end{macro}
%
% \subsubsection{Replacement}
%
-% \begin{macro}[aux]{\regex_replace_once_aux:NnnN}
-% The replacement text is analysed by \cs{regex_replacement:n},
-% which defines \cs{regex_replacement_tl:n} to expand to the
-% replaced token list, assuming that submatches are stored in
-% various \tn{skip} registers, as done by \cs{regex_extract:}.
-% If there is a match, we grab
-% the parts before and after it, and get the result
-% by \texttt{x}-expanding twice.
-% \begin{macrocode}
-\cs_new_protected:Npn \regex_replace_once_aux:NnnN #1#2#3#4
+% \begin{macro}[aux]{\regex_replace_once_aux:nnN}
+% Build the \textsc{nfa} and the replacement functions, then find a
+% single match. If the match failed, simply exit the
+% group. Otherwise, we do the replacement. Extract submatches. Compute
+% the brace balance corresponding to replacing this match by the
+% replacement (this depends on submatches). Prepare the replaced token
+% list: the replacement function produces the tokens from the start of
+% the query to the start of the match and the replacement text for
+% this match; we need to add the tokens from the end of the match to
+% the end of the query. Finally, store the result in the user's
+% variable after closing the group: this step involves an additional
+% \texttt{x}-expansion, and checks that braces are balanced in the
+% final result.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_replace_once_aux:nnN #1#2#3
{
\group_begin:
- \tl_clear:N \l_regex_every_match_tl
- \regex_replacement:n {#3}
- \exp_args:Nno #1 {#2} #4
- \if_meaning:w \c_true_bool \g_regex_success_bool
+ \regex_single_match:
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n { #3 }
+ \if_meaning:w \c_false_bool \g_regex_success_bool
+ \group_end:
+ \else:
\regex_extract:
- \int_set:Nn \l_regex_internal_a_int
- { \regex_nesting:n { \l_regex_submatch_start_int } }
- \if_num:w \l_regex_internal_a_int = \c_zero
- \else:
- \msg_kernel_error:nnx { regex } { replace-unbalanced }
- { \l_regex_internal_a_int }
- \fi:
+ \int_set:Nn \l_regex_balance_int
+ {
+ \regex_replacement_balance_one_match:n
+ { \l_regex_zeroth_submatch_int }
+ }
\tl_set:Nx \l_regex_internal_a_tl
{
- \if_num:w \l_regex_internal_a_int < \c_zero
- \prg_replicate:nn { - \l_regex_internal_a_int }
- { \exp_not:n { { \if_false: } \fi: } }
- \fi:
- \regex_toks_range:nn
- { \l_regex_min_index_int }
- { \tex_skip:D \l_regex_submatch_start_int }
- \regex_replacement_tl:n { \l_regex_submatch_start_int }
- \regex_toks_range:nn
- { \etex_gluestretch:D \tex_skip:D \l_regex_submatch_start_int }
- { \l_regex_max_index_int }
- \if_num:w \l_regex_internal_a_int > \c_zero
- \prg_replicate:nn { \l_regex_internal_a_int }
- { \exp_not:n { \if_false: { \fi: } } }
- \fi:
+ \regex_replacement_do_one_match:n { \l_regex_zeroth_submatch_int }
+ \regex_query_range:nn
+ { \etex_gluestretch:D \tex_skip:D \l_regex_zeroth_submatch_int }
+ { \l_regex_max_pos_int }
}
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #4 \l_regex_internal_a_tl
- \else:
- \group_end:
+ \regex_group_end_replace:N #3
\fi:
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\regex_replace_all_aux:NnnN}
-% For every match, extract submatches, and add the part before
-% the beginning of the match, as well as the replacement,
-% to the result. After the last match, extract the end
-% of the token list, and add it to the replaced token list.
+% \begin{macro}[aux]{\regex_replace_all_aux:nnN}
+% Match multiple times, and for every match, extract submatches and
+% additionally store the position at which the match attempt started
+% (as the shrink component of a \tn{skip} register). The \tn{skip}
+% registers from \cs{l_regex_max_state_int} to
+% \cs{l_regex_submatch_int} hold information about submatches of every
+% match in order; each match corresponds to
+% \cs{l_regex_capturing_group_int} consecutive \tn{skip} registers.
+% Compute the brace balance corresponding to doing all the
+% replacements: this is the sum of brace balances for replacing each
+% match. Join together the replacement texts for each match (including
+% the part of the query before the match), and the end of the query.
% \begin{macrocode}
-\cs_new_protected:Npn \regex_replace_all_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_replace_all_aux:nnN #1#2#3
{
\group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- {
- \regex_extract:
- \tex_skip:D \l_regex_submatch_start_int
- = \tex_the:D \tex_skip:D \l_regex_submatch_start_int
- minus \l_regex_start_index_int sp \scan_stop:
- \regex_match_once:
- }
- \regex_replacement:n {#3}
- \exp_args:Nno #1 {#2} #4
- \int_set:Nn \l_regex_internal_a_int
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n {#3}
+ \int_set:Nn \l_regex_balance_int
{
0
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\l_regex_capturing_group_int
{ \l_regex_submatch_int - \c_one }
- \regex_nesting:n
+ \regex_replacement_balance_one_match:n
}
- \if_num:w \l_regex_internal_a_int = \c_zero
- \else:
- \msg_kernel_error:nnx { regex } { replace-unbalanced }
- { \l_regex_internal_a_int }
- \fi:
\tl_set:Nx \l_regex_internal_a_tl
{
- \if_num:w \l_regex_internal_a_int < \c_zero
- \prg_replicate:nn { - \l_regex_internal_a_int }
- { \exp_not:n { { \if_false: } \fi: } }
- \fi:
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\l_regex_capturing_group_int
{ \l_regex_submatch_int - \c_one }
- \regex_replace_all_aux:n
- \regex_toks_range:nn
- \l_regex_start_index_int \l_regex_max_index_int
- \if_num:w \l_regex_internal_a_int > \c_zero
- \prg_replicate:nn { \l_regex_internal_a_int }
- { \exp_not:n { \if_false: { \fi: } } }
- \fi:
+ \regex_replacement_do_one_match:n
+ \regex_query_range:nn
+ \l_regex_start_pos_int \l_regex_max_pos_int
}
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #4 \l_regex_internal_a_tl
+ \regex_group_end_replace:N #3
}
-\cs_new:Npn \regex_replace_all_aux:n #1
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\regex_group_end_replace:N}
+% If the brace balance is not $0$, raise an error. Then set the user's
+% variable |#1| to the \texttt{x}-expansion of
+% \cs{l_regex_internal_a_tl}, adding the appropriate braces to produce
+% a balanced result. And end the group.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \regex_group_end_replace:N #1
{
- \regex_toks_range:nn
- { \etex_glueshrink:D \tex_skip:D #1 } { \tex_skip:D #1 }
- \regex_replacement_tl:n {#1}
+ \if_num:w \l_regex_balance_int = \c_zero
+ \else:
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { replacing }
+ { \int_max:nn { - \l_regex_balance_int } { \c_zero } }
+ { \int_max:nn { \l_regex_balance_int } { \c_zero } }
+ \fi:
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1
+ {
+ \if_int_compare:w \l_regex_balance_int < \c_zero
+ \prg_replicate:nn { - \l_regex_balance_int }
+ { { \if_false: } \fi: }
+ \fi:
+ \l_regex_internal_a_tl
+ \if_int_compare:w \l_regex_balance_int > \c_zero
+ \prg_replicate:nn { \l_regex_balance_int }
+ { \if_false: { \fi: } }
+ \fi:
+ }
+ }
}
% \end{macrocode}
% \end{macro}
%
+% \subsubsection{Storing and showing compiled patterns}
+%
% \subsection{Messages}
%
% Messages for the preparsing phase.
@@ -5054,6 +5675,17 @@
}
% \end{macrocode}
%
+% Invalid quantifier.
+% \begin{macrocode}
+\msg_kernel_new:nnnn { regex } { invalid-quantifier }
+ { Braced~quantifier~'#1'~may~not~be~followed~by~'#2'. }
+ {
+ The~character~'#2'~is~invalid~in~the~braced~quantifier~'#1'.~
+ The~only~valid~quantifiers~are~'*',~'?',~'+',~'{<int>}',~
+ '{<min>,}'~and~'{<min>,<max>}',~followed~or~not~by~'?'.
+ }
+% \end{macrocode}
+%
% Messages for missing or extra closing brackets and parentheses, with
% some fancy singular/plural handling for the case of parentheses.
% \begin{macrocode}
@@ -5081,21 +5713,31 @@
}
% \end{macrocode}
%
-% Sometimes escaped alphanumerics are not allowed everywhere.
+% Some escaped alphanumerics are not allowed everywhere.
% \begin{macrocode}
-\msg_kernel_new:nnnn { regex } { class-bad-escape }
- { Invalid~escape~\c_backslash_str #1~in~character~class. }
+\msg_kernel_new:nnnn { regex } { bad-escape }
{
- The~escape~sequence~\iow_char:N\\#1~may~not~appear~within~
- a~character~class.~For~instance,~assertions~(which~match~zero~
- characters)~would~not~make~sense~there.
+ Invalid~escape~\c_backslash_str #1~
+ \regex_if_in_cs:TF { within~a~control~sequence. }
+ {
+ \regex_if_in_class:TF
+ { in~a~character~class. }
+ { following~a~category~test. }
+ }
}
-\msg_kernel_new:nnnn { regex } { catcode-bad-escape }
- { Invalid~escape~\c_backslash_str #1~following~category~test. }
{
- The~escape~sequence~\iow_char:N\\#1~may~not~appear~following~
- a~category~test~such~as~\iow_char:N\\cL~or~
- \iow_char:N\\c[\iow_char:N\^BE].
+ The~escape~sequence~\iow_char:N\\#1~may~not~appear~
+ \regex_if_in_cs:TF
+ {
+ within~a~control~sequence~test~introduced~by~
+ \iow_char:N\\c\iow_char:N\{.
+ }
+ {
+ \regex_if_in_class:TF
+ { within~a~character~class~ }
+ { following~a~category~test~such~as~\iow_char:N\\cL~ }
+ because~it~does~not~match~exactly~one~character.
+ }
}
% \end{macrocode}
%
@@ -5141,7 +5783,7 @@
regular~expression,~but~the~closing~']'~is~not~present.
}
\msg_kernel_new:nnnn { regex } { c-missing-category }
- { Invalid~character~'#1'~following~\iow_char\\c~escape. }
+ { Invalid~character~'#1'~following~\iow_char:N\\c~escape. }
{
In~regular~expressions,~the~\iow_char:N\\c~escape~sequence~
may~only~be~followed~by~a~left~brace,~a~left~bracket,~or~a~
@@ -5165,91 +5807,155 @@
}
% \end{macrocode}
%
-% In various cases, the result of a \pkg{l3regex} operation can leave us
-% with an unbalanced token list, which we must re-balance by adding
-% begin-group or end-group character tokens.
+% Errors when encountering the \textsc{posix} syntax |[:...:]|.
% \begin{macrocode}
-\msg_kernel_new:nnnn { regex } { sequence-unbalanced }
+\msg_kernel_new:nnnn { regex } { posix-unsupported }
+ { POSIX~collating~element~'[#1 ~ #1]'~not~supported. }
{
- Missing~
- \flag_if_raised:nTF { regex_end }
- {
- left~
- \flag_if_raised:nTF { regex_begin }
- { and~right~braces } { brace }
- }
- { right~brace }
- \ inserted~in~extracted~match.
+ The~[.foo.]~and~[=bar=]~syntaxes~have~a~special~meaning~in~POSIX~
+ regular~expressions.~This~is~not~supported~by~LaTeX.~Maybe~you~
+ forgot~to~escape~a~left~bracket~in~a~character~class?
}
+\msg_kernel_new:nnnn { regex } { posix-unknown }
+ { POSIX~class~[:#1:]~unknown. }
{
- LaTeX~was~asked~to~extract~submatches~or~split~a~token~list~
- according~to~a~given~regular~expression,~but~some~of~the~resulting~
- items~were~not~balanced.
+ [:#1:]~is~not~among~the~known~POSIX~classes~
+ [:alnum:],~[:alpha:],~[:ascii:],~[:blank:],~
+ [:cntrl:],~[:digit:],~[:graph:],~[:lower:],~
+ [:print:],~[:punct:],~[:space:],~[:upper:],~
+ [:word:],~and~[:xdigit:].
}
-\msg_kernel_new:nnnn { regex } { replace-unbalanced }
- { The~result~of~a~replacement~does~not~have~balanced~braces. }
+\msg_kernel_new:nnnn { regex } { posix-missing-close }
+ { Missing~closing~':]'~for~POSIX~class. }
+ { The~POSIX~syntax~'#1'~must~be~followed~by~':]',~not~'#2'. }
+% \end{macrocode}
+%
+% In various cases, the result of a \pkg{l3regex} operation can leave us
+% with an unbalanced token list, which we must re-balance by adding
+% begin-group or end-group character tokens.
+% \begin{macrocode}
+\msg_kernel_new:nnnn { regex } { result-unbalanced }
+ { Missing~brace~inserted~when~#1. }
{
- LaTeX~was~asked~to~do~some~regular~expression~replacement,~
+ LaTeX~was~asked~to~do~some~regular~expression~operation,~
and~the~resulting~token~list~would~not~have~the~same~number~
- of~begin-group~and~end-group~tokens. \\ \\
- \ \ \ \
- \prg_case_int:nnn {#1}
- {
- { -1 } { A~left~brace~was }
- { 1 } { A~right~brace~was }
- }
- {
- \int_abs:n {#1} ~
- \int_compare:nNnTF {#1} < \c_zero { left } { right } ~
- braces ~ were
- }
- \ inserted.
+ of~begin-group~and~end-group~tokens.~Braces~were~inserted:~
+ #2~left,~#3~right.
}
% \end{macrocode}
%
-% Messages related to NFA variables.
+% Error message for unknown options.
% \begin{macrocode}
-\msg_kernel_new:nnnn { regex } { not-nfa }
- { This~is~not~a~regular~expression~variable. }
+\msg_kernel_new:nnnn { regex } { unknown-option }
+ { Unknown~option~`#1'~for~regular~expressions. }
{
- LaTeX~was~expecting~`#1'~to~be~a~regular~expression~variable.\\
- This~control~sequence~is~not~a~regex~variable.~It's~current~meaning~
- is~\\\\
- \ \ \ \ \token_to_str:N #1 = \token_to_meaning:N #1 .
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
}
-\msg_kernel_new:nnn { regex } { nfa-misused }
- { Automaton~#1 used~incorrectly. }
% \end{macrocode}
%
+% Errors in the replacement text.
% \begin{macrocode}
-\msg_kernel_new:nnnn { regex } { c-command }
- { Misused~\iow_char:N\\c~or\iow_char:N\\C~command~in~a~#1. }
+\msg_kernel_new:nnnn { regex } { replacement-c }
+ { Misused~\iow_char:N\\c~command~in~a~replacement~text. }
{
- In~a~#1,~the~\iow_char:N\\C~escape~sequence~
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~
- or~a~brace~group,~not~by~'#2'.
- }
-\msg_kernel_new:nnnn { regex } { unknown-option }
- { Unknown~option~`#1'~for~regular~expressions. }
- {
- LaTeX~came~across~something~like~`(?#1)'~in~a~regular~expression,~
- but~the~option~`#1'~is~not~known.~It~will~be~ignored.
+ or~a~brace~group,~not~by~'#1'.
}
-\msg_kernel_new:nnnn { regex } { invalid-in-option }
- { Invalid~character~in~option~of~a~regular~expression. }
+\msg_kernel_new:nnnn { regex } { replacement-u }
+ { Misused~\iow_char:N\\u~command~in~a~replacement~text. }
{
- The~character~or~escape~sequence~`#1'~is~not~defined~
- as~an~option~within~regular~expressions.
+ In~a~replacement~text,~the~\iow_char:N\\u~escape~sequence~
+ must~be~~followed~by~a~brace~group~holding~the~name~of~the~
+ variable~to~use.
}
-\msg_kernel_new:nnnn { regex } { g-command }
+\msg_kernel_new:nnnn { regex } { replacement-g }
{ Missing~brace~for~the~\iow_char:N\\g~construction~in~a~replacement~text. }
{
In~the~replacement~text~for~a~regular~expression~search,~
submatches~are~represented~either~as~\iow_char:N \\g{dd..d},~
or~\\d,~where~`d'~are~single~digits.~Here,~a~brace~is~missing.
}
+\msg_kernel_new:nnnn { regex } { replacement-catcode-end }
+ {
+ Missing~character~for~the~\iow_char:N\\c<category><character>~
+ construction~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
+ can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~representing~
+ the~character~category.~Then,~a~character~must~follow.~LaTeX~
+ reached~the~end~of~the~replacement~when~looking~for~that.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-null-space }
+ { TeX~cannot~build~a~space~token~with~character~code~0. }
+ {
+ You~asked~for~a~character~token~with~category~'space',~
+ and~character~code~0,~for~instance~through~
+ '\iow_char:N\\cS\iow_char:N\\x00'.~
+ This~specific~case~is~impossible~and~will~be~replaced~
+ by~a~normal~space.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-missing-rbrace }
+ { Missing~right~brace~inserted~in~replacement~text. }
+ {
+ There~were~\int_use:N \l_regex_replacement_csnames_int \
+ missing~right~braces.
+ }
% \end{macrocode}
%
+% \begin{macro}[aux]{\regex_msg_repeated:nnN}
+% This is not technically a message, but seems related enough to go
+% there. The arguments are: |#1| is the minimum number of repetitions;
+% |#2| is the number of allowed extra repetitions ($-1$ for infinite
+% number), and |#3| tells us aboud lazyness.
+% \begin{macrocode}
+\cs_new:Npn \regex_msg_repeated:nnN #1#2#3
+ {
+ \str_if_eq:xxF { #1 #2 } { 1 0 }
+ {
+ , ~ repeated ~
+ \prg_case_int:nnn {#2}
+ {
+ { -1 } { #1~or~more~times,~\bool_if:NTF #3 { lazy } { greedy } }
+ { 0 } { #1~times }
+ }
+ {
+ between~#1~and~\int_eval:n {#1+#2}~times,~
+ \bool_if:NTF #3 { lazy } { greedy }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Code for tracing}
+%
+% The tracing code is still very experimental, and is meant to be used
+% with the \pkg{l3trace} package, currently in \texttt{l3trial}.
+%
+% \begin{macro}[int]{\regex_trace_states:n}
+% This function lists the contents of all states of the \textsc{nfa},
+% stored in \tn{toks} from $0$ to \cs{l_regex_max_state_int}
+% (excluded).
+% \begin{macrocode}
+%<*trace>
+\cs_new_protected:Npn \regex_trace_states:n #1
+ {
+ \prg_stepwise_inline:nnnn
+ \l_regex_min_state_int
+ \c_one
+ { \l_regex_max_state_int - 1 }
+ {
+ \trace:nnx { regex } { #1 }
+ { \iow_char:N \\toks ##1 = { \tex_the:D \tex_toks:D ##1 } }
+ }
+ }
+%</trace>
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macrocode}
%</package>
% \end{macrocode}
@@ -5257,84 +5963,27 @@
% \end{implementation}
%
% \endinput
-%^^A
-%^^A \msg_new:nnn { regex } { 4 }
-%^^A { Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier. }
-%^^A \msg_new:nnn { regex } { 44 } { Invalid~UTF-8~string }
-%^^A \msg_new:nnn { regex } { 46 }
-%^^A { Malformed~\iow_char:N\\P~or\iow_char:N\\p~sequence }
-%^^A \msg_new:nnn { regex } { 47 }
-%^^A { Unknown~property~after~\iow_char:N\\P~or\iow_char:N\\p }
-%^^A
%^^A NOT IMPLEMENTED
-%^^A \cx "control-x", where x is any ASCII character
-%^^A \C one byte, even in UTF-8 mode (best avoided)
%^^A \p{xx} a character with the xx property
%^^A \P{xx} a character without the xx property
+%^^A [[:xxx:]] positive POSIX named set
+%^^A [[:^xxx:]] negative POSIX named set
+%^^A (?=...) positive look ahead
+%^^A (?!...) negative look ahead
+%^^A (?<=...) positive look behind
+%^^A (?<!...) negative look behind
+%^^A (?<name>...) or (?'name'...) or (?P<name>...)
+%^^A named capturing group
%^^A \R a newline sequence
%^^A \X an extended Unicode sequence
-%^^A [[:xxx:]] positive POSIX named set
-%^^A [[:^xxx:]] negative POSIX named set
-%^^A ?+ 0 or 1, possessive
-%^^A *+ 0 or more, possessive
-%^^A ++ 1 or more, possessive
-%^^A {n,m}+ at least n, no more than m, possessive
-%^^A {n,}+ n or more, possessive
-%^^A \K reset start of match
-%^^A (?<name>...) named capturing group (Perl)
-%^^A (?'name'...) named capturing group (Perl)
-%^^A (?P<name>...) named capturing group (Python)
-%^^A (?:...) non-capturing group
-%^^A (?|...) non-capturing group; reset group numbers for
-%^^A capturing groups in each alternative
-%^^A (?>...) atomic, non-capturing group
-%^^A (?#....) comment (not nestable)
-%^^A (?i) caseless
-%^^A (?J) allow duplicate names
-%^^A (?m) multiline
-%^^A (?s) single line (dotall)
-%^^A (?U) default ungreedy (lazy)
-%^^A (?x) extended (ignore white space)
-%^^A (?-...) unset option(s)
-%^^A (*NO_START_OPT) no start-match optimization (PCRE_NO_START_OPTIMIZE)
-%^^A (*UTF8) set UTF-8 mode (PCRE_UTF8)
-%^^A (*UCP) set PCRE_UCP (use Unicode properties for \d etc)
-%^^A (?=...) positive look ahead
-%^^A (?!...) negative look ahead
-%^^A (?<=...) positive look behind
-%^^A (?<!...) negative look behind
-%^^A \n reference by number (can be ambiguous)
-%^^A \gn reference by number
-%^^A \g{n} reference by number
-%^^A \g{-n} relative reference by number
-%^^A \k<name> reference by name (Perl)
-%^^A \k'name' reference by name (Perl)
-%^^A \g{name} reference by name (Perl)
-%^^A \k{name} reference by name (.NET)
-%^^A (?P=name) reference by name (Python)
+%^^A (?C) or (?Cn) callout with data n
%^^A (?R) recurse whole pattern
-%^^A (?n) call subpattern by absolute number
-%^^A (?+n) call subpattern by relative number
-%^^A (?-n) call subpattern by relative number
-%^^A (?&name) call subpattern by name (Perl)
-%^^A (?P>name) call subpattern by name (Python)
-%^^A \g<name> call subpattern by name (Oniguruma)
-%^^A \g'name' call subpattern by name (Oniguruma)
-%^^A \g<n> call subpattern by absolute number (Oniguruma)
-%^^A \g'n' call subpattern by absolute number (Oniguruma)
-%^^A \g<+n> call subpattern by relative number (PCRE extension)
-%^^A \g'+n' call subpattern by relative number (PCRE extension)
-%^^A \g<-n> call subpattern by relative number (PCRE extension)
-%^^A \g'-n' call subpattern by relative number (PCRE extension)
-%^^A (?(n)... absolute reference condition
-%^^A (?(+n)... relative reference condition
-%^^A (?(-n)... relative reference condition
-%^^A (?(<name>)... named reference condition (Perl)
-%^^A (?('name')... named reference condition (Perl)
-%^^A (?(name)... named reference condition (PCRE)
-%^^A (?(R)... overall recursion condition
-%^^A (?(Rn)... specific group recursion condition
-%^^A (?(R&name)... specific recursion condition
+%^^A (?[+-]n) or \g<[+-]n> or (?&name) or (?P>name) or \g<name>
+%^^A call subpattern
+%^^A (?([+-]n)... or (?(<name>)...
+%^^A reference condition
+%^^A (?(R)... or (?(Rn)... or (?(R&name)...
+%^^A recursion condition
%^^A (?(DEFINE)... define subpattern for reference
%^^A (?(assert)... assertion condition
%^^A (*ACCEPT) force successful match
@@ -5343,12 +5992,21 @@
%^^A (*PRUNE) advance to next starting character
%^^A (*SKIP) advance start to current matching position
%^^A (*THEN) local failure, backtrack to next alternation
-%^^A (*CR) carriage return only
-%^^A (*LF) linefeed only
-%^^A (*CRLF) carriage return followed by linefeed
-%^^A (*ANYCRLF) all three of the above
-%^^A (*ANY) any Unicode newline sequence
-%^^A (*BSR_ANYCRLF) CR, LF, or CRLF
-%^^A (*BSR_UNICODE) any Unicode newline sequence
-%^^A (?C) callout
-%^^A (?Cn) callout with data n \ No newline at end of file
+%^^A (*CR) or (*LF) or (*CRLF) or (*ANYCRLF) or (*ANY)
+%^^A newline convention
+%^^A (*BSR_ANYCRLF) or (*BSR_UNICODE)
+%^^A change what \R matches.
+%^^A
+%^^A \cx "control-x", where x is any ASCII character
+%^^A \C one byte, even in UTF-8 mode (best avoided)
+%^^A + possessive quantifiers
+%^^A (?>...) atomic, non-capturing group
+%^^A (?#....) comment (not nestable)
+%^^A (?JmsUx) options (duplicate names; multiline; single line;
+%^^A ungreedy; extended)
+%^^A (*NO_START_OPT) no start-match optimization (PCRE_NO_START_OPTIMIZE)
+%^^A (*UTF8) set UTF-8 mode (PCRE_UTF8)
+%^^A (*UCP) set PCRE_UCP (use Unicode properties for \d etc)
+%^^A \n or \gn or \g{[-]n} or \g{name} or (?P=name)
+%^^A or \k<name> or \k'name' or \k{name}
+%^^A back-references
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx
index 3d73ed10b8f..ef2bc9b5460 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 3257 2012-01-29 13:37:03Z bruno $
+\GetIdInfo$Id: l3str.dtx 3460 2012-02-26 12:16:41Z will $
{L3 Experimental Strings}
%</driver|package>
%<*driver>
@@ -189,7 +189,7 @@
% var} are not automatically converted to a string.
% \end{function}
%
-% \section{Characters given by their position}
+% \section{Accessing the contents of a string}
%
% \begin{function}[EXP]
% {\str_length:N, \str_length:n, \str_length_ignore_spaces:n}
@@ -291,8 +291,8 @@
% \str_if_eq:nV, \str_if_eq:VV, \str_if_eq:xx
% }
% \begin{syntax}
-% \cs{str_if_eq_p:nn} \Arg{tl1} \Arg{tl2}
-% \cs{str_if_eq:nnTF} \Arg{tl1} \Arg{tl2} \Arg{true code} \Arg{false code}
+% \cs{str_if_eq_p:nn} \Arg{tl_1} \Arg{tl_2}
+% \cs{str_if_eq:nnTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
% \end{syntax}
% Compares the two \meta{token lists} on a character by character
% basis, and is \texttt{true} if the two lists contain the same
@@ -305,6 +305,15 @@
% expansion).
% \end{function}
%
+% \section{Viewing strings}
+%
+% \begin{function}{\str_show:N, \str_show:c, \str_show:n}
+% \begin{syntax}
+% \cs{str_show:N} \meta{tl~var}
+% \end{syntax}
+% Displays the content of the \meta{str~var} on the terminal.
+% \end{function}
+%
% \section{Encoding functions}
%
% Traditionally, string encodings only specify how strings of characters
@@ -571,7 +580,7 @@
% \begin{macrocode}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
-\RequirePackage{l3tl-analysis,l3flag}
+\RequirePackage{l3tl-analysis,l3tl-build,l3flag}
% \end{macrocode}
%
% The following string-related functions are currently defined in
@@ -601,6 +610,14 @@
%
% \subsubsection{Assigning strings}
%
+% \begin{macro}{\str_new:N, \str_new:c}
+% A string is simply a token list.
+% \begin{macrocode}
+\cs_new_eq:NN \str_new:N \tl_new:N
+\cs_generate_variant:Nn \str_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}
% {
% \str_set:Nn, \str_set:Nx,
@@ -1337,31 +1354,38 @@
% \end{macrocode}
% \end{macro}
%
+% \subsection{Viewing strings}
+%
+% \begin{macro}{\str_show:n, \str_show:N, \str_show:c}
+% Displays a string on the terminal.
+% \begin{macrocode}
+\cs_new_eq:NN \str_show:n \tl_show:n
+\cs_new_eq:NN \str_show:N \tl_show:N
+\cs_generate_variant:Nn \str_show:N { c }
+% \end{macrocode}
+% \end{macro}
+%
% \subsection{Conversions}
%
% \subsubsection{Producing one byte or character}
%
% \begin{variable}{\c_str_byte_0_tl, \c_str_byte_1_tl, \c_str_byte_255_tl}
% \begin{variable}{\c_str_byte_-1_tl}
-% \begin{variable}{\c_str_positive_bytes_tl}
% For each integer $N$ in the range $[0,255]$, we create a constant
% token list which holds three character tokens with category code
% other: the character with character code $N$, followed by the
% representation of $N$ as two hexadecimal digits. The value $-1$ is
% given a default token list which ensures that later functions give
-% an empty result for the input $-1$. Simultaneously, we build a list
-% of all bytes (from which we remove the null byte) in
-% \cs{c_str_positive_bytes_tl}.
+% an empty result for the input $-1$.
% \begin{macrocode}
\group_begin:
- \tl_clear:N \l_str_internal_tl
\char_set_catcode_other:n { \c_zero }
- \tl_gset:Nx \g_str_result_tl { \tl_to_str:n { 0123456789ABCDEF } }
- \exp_args:No \tl_map_inline:nn { \g_str_result_tl " }
+ \tl_set:Nx \l_str_internal_tl { \tl_to_str:n { 0123456789ABCDEF } }
+ \exp_args:No \tl_map_inline:nn { \l_str_internal_tl " }
{ \char_set_lccode:nn {`#1} { \c_zero } }
- \tl_map_inline:Nn \g_str_result_tl
+ \tl_map_inline:Nn \l_str_internal_tl
{
- \tl_map_inline:Nn \g_str_result_tl
+ \tl_map_inline:Nn \l_str_internal_tl
{
\char_set_lccode:nn { \c_zero } {"#1##1}
\tl_to_lowercase:n
@@ -1369,18 +1393,14 @@
\tl_const:cx
{ c_str_byte_ \int_eval:n {"#1##1} _tl }
{ ^^@ #1 ##1 }
- \tl_put_right:Nn \l_str_internal_tl { ^^@ }
}
}
}
- \tl_const:Nx \c_str_positive_bytes_tl
- { \exp_after:wN \use_none:n \l_str_internal_tl }
\group_end:
\tl_const:cn { c_str_byte_-1_tl } { { } \use_none:n { } }
% \end{macrocode}
% \end{variable}
% \end{variable}
-% \end{variable}
%
% \begin{macro}[int, EXP]{\str_output_byte:n}
% \begin{macro}[int, EXP]{\str_output_byte:w}
@@ -1943,68 +1963,34 @@
% \begin{macro}[aux, rEXP]{\str_encode_native_filter:N}
% In Unicode-aware engines, since building particular characters
% cannot be done expandably in \TeX{}, we cannot hope to get a
-% linear-time function. However, we get quite close by building the
-% result one block of $255$ characters at a time. Set the lowercase
-% code of the \textsc{ascii} character $1$ to the first character code
-% in the block, \emph{etc.}, then lowercase the contents of
-% \cs{c_str_positive_bytes_tl}. Unfortunately, this is complicated by
-% the fact that \TeX{} won't lowercase a character to the null
-% character, thus the need for filtering: whenever the lowercase code
-% of a given character is zero, it is replaced \enquote{by hand} by
-% the null character. Unicode-aware engines will never incur an
-% overflow because the internal string is guaranteed to only contain
-% code points in $[0,\hexnum{10FFFF}]$.
+% linear-time function. However, we get quite close using the
+% \pkg{l3tl-build} module, which abuses \tn{toks} to reach an almost
+% linear time. Use the standard lowercase trick to produce an
+% arbitrary character from the null character, and add that character
+% to the end of the token list being built. At the end of the loop,
+% put the token list together with \cs{tl_build_end:}. Note that we
+% use an \texttt{x}-expanding assignment because it is slightly
+% faster. Unicode-aware engines will never incur an overflow because
+% the internal string is guaranteed to only contain code points in
+% $[0,\hexnum{10FFFF}]$.
% \begin{macrocode}
{
\cs_new_protected_nopar:Npn \str_convert_encode_:
{
- \tex_lccode:D \c_zero \c_zero
- \int_zero:N \l_str_internal_int
- \tl_set_eq:NN \l_str_internal_tl \g_str_result_tl
- \tl_gclear:N \g_str_result_tl
- \exp_after:wN \str_encode_native_loop:w
- \l_str_internal_tl \s_tl { \q_stop \prg_map_break: } \s_tl
- \prg_break_point:n { \str_encode_native_flush: }
+ \int_zero:N \l_tl_build_offset_int
+ \tl_gset_build_x:Nw \g_str_result_tl
+ \exp_after:wN \str_encode_native_loop:w
+ \g_str_result_tl \s_tl { \q_stop \prg_map_break: } \s_tl
+ \prg_break_point:n { }
+ \tl_build_end:
}
\cs_new_protected:Npn \str_encode_native_loop:w #1 \s_tl #2 \s_tl
{
\use_none_delimit_by_q_stop:w #2 \q_stop
- \if_num:w \l_str_internal_int = \c_two_hundred_fifty_five
- \str_encode_native_flush:
- \l_str_internal_int \c_zero
- \fi:
- \tex_advance:D \l_str_internal_int \c_one
\tex_lccode:D \l_str_internal_int \int_eval:w #2 \int_eval_end:
+ \tl_to_lowercase:n { \tl_build_one:n { ^^@ } }
\str_encode_native_loop:w
}
- \cs_new_protected_nopar:Npn \str_encode_native_flush:
- {
- \use:x
- {
- \tl_to_lowercase:n
- {
- \tl_gput_right:Nx \exp_not:N \g_str_result_tl
- {
- \exp_after:wN \str_encode_native_filter:N
- \c_str_positive_bytes_tl
- { ? = \c_zero \fi: \prg_map_break: }
- \prg_break_point:n { }
- }
- }
- }
- }
- \cs_new:Npn \str_encode_native_filter:N #1
- {
- \if_num:w `#1 > \l_str_internal_int
- \exp_after:wN \prg_map_break:
- \fi:
- \if_num:w \tex_lccode:D `#1 = \c_zero
- ^^@
- \else:
- #1
- \fi:
- \str_encode_native_filter:N
- }
}
% \end{macrocode}
% \end{macro}
@@ -3044,7 +3030,8 @@
% character;
% \item $[\hexnum{E000}, \hexnum{FFFF}]$: converted to two bytes;
% \item $[\hexnum{10000}, \hexnum{10FFFF}]$: converted to a pair of
-% surrogates, each two bytes.
+% surrogates, each two bytes. The magic \hexnum{D7C0} is
+% $\hexnum{D800}-\hexnum{10000}/\hexnum{400}$.
% \end{itemize}
% For the duration of this operation, \cs{str_tmp:w} is defined as a
% function to convert a number in the range $[0, \hexnum{FFFF}]$ to a
@@ -3083,7 +3070,7 @@
\str_tmp:w {#1}
\fi:
\else:
- \exp_args:Nf \str_tmp:w { \int_div_truncate:nn {#1} {"400} + "D800 }
+ \exp_args:Nf \str_tmp:w { \int_div_truncate:nn {#1} {"400} + "D7C0 }
\exp_args:Nf \str_tmp:w { \int_mod:nn {#1} {"400} + "DC00 }
\fi:
\fi:
@@ -3257,6 +3244,10 @@
% looping with \cs{str_decode_utf_xvi_pair:NN}. Otherwise, of course,
% complain about the missing surrogate.
%
+% The magic number \hexnum{D7F7} is such that
+% $\hexnum{D7F7}*\hexnum{400} = \hexnum{D800}*\hexnum{400} +
+% \hexnum{DC00} - \hexnum{10000}$.
+%
% Every time we read a pair of bytes, we test for the end-marker
% \cs{q_nil}. When reaching the end, we additionally check that the
% string had an even length. Also, if the end is reached when
@@ -3292,8 +3283,8 @@
#1 #2 #4 #5 \s_tl
\int_eval:n
{
- ( "100 * \str_tmp:w #1#2 + \str_tmp:w #2#1 - "D800 ) * "400
- + "100 * \str_tmp:w #4#5 + \str_tmp:w #5#4 - "DC00
+ ( "100 * \str_tmp:w #1#2 + \str_tmp:w #2#1 - "D7F7 ) * "400
+ + "100 * \str_tmp:w #4#5 + \str_tmp:w #5#4
}
\s_tl
\exp_after:wN \use_i:nnn
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins
index 676a39bea7b..7dc0c00357d 100644
--- a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins
+++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins
@@ -39,7 +39,8 @@ Do not distribute a modified version of this file.
\generate{\file{l3str.sty} {\from{l3str.dtx} {package}}}
\generate{\file{l3regex.sty} {\from{l3regex.dtx} {package}}}
\generate{\file{l3tl-analysis.sty} {\from{l3tl-analysis.dtx} {package}}}
-%\generate{\file{l3regex-trace.sty} {\from{l3regex.dtx} {package,trace}}}
+\generate{\file{l3tl-build.sty} {\from{l3tl-build.dtx} {package}}}
+\generate{\file{l3regex-trace.sty} {\from{l3regex.dtx} {package,trace}}}
% Escapings.
\generate{%
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-analysis.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-analysis.dtx
index f9294dbb87c..89ce9c22f3d 100644
--- a/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-analysis.dtx
+++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-analysis.dtx
@@ -82,8 +82,8 @@
% \end{syntax}
% Displays to the terminal the detailed decomposition of the
% \meta{token list} into tokens, showing the category code of each
-% character token, and the meaning of control sequences and active
-% characters.
+% character token, the meaning of control sequences and active
+% characters, and the value of registers.
% \end{function}
%
% \subsection{Internal functions}
@@ -203,6 +203,14 @@
% \end{macrocode}
% \end{variable}
%
+% \begin{variable}{\l_tl_analysis_internal_tl}
+% This token list variable is used to hand the argument of
+% \cs{tl_show_analysis:n} to \cs{tl_show_analysis:N}.
+% \begin{macrocode}
+\tl_new:N \l_tl_analysis_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
% \begin{variable}{\l_tl_analysis_token}
% \begin{variable}{\l_tl_analysis_char_token}
% The tokens in the token list are probed with the \TeX{} primitive
@@ -953,8 +961,8 @@
}
\cs_new_protected:Npn \tl_show_analysis:n #1
{
- \tl_set:Nn \l_tl_tmpa_tl {#1}
- \tl_show_analysis:N \l_tl_tmpa_tl
+ \tl_set:Nn \l_tl_analysis_internal_tl {#1}
+ \tl_show_analysis:N \l_tl_analysis_internal_tl
}
% \end{macrocode}
% \end{macro}
@@ -1004,10 +1012,31 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[EXP]{\tl_show_analysis_value:N}
+% This expands to the value of |#1| if it has any.
+% \begin{macrocode}
+\cs_new:Npn \tl_show_analysis_value:N #1
+ {
+ \token_if_expandable:NF #1
+ {
+ \token_if_chardef:NTF #1 \prg_map_break: { }
+ \token_if_mathchardef:NTF #1 \prg_map_break: { }
+ \token_if_dim_register:NTF #1 \prg_map_break: { }
+ \token_if_int_register:NTF #1 \prg_map_break: { }
+ \token_if_skip_register:NTF #1 \prg_map_break: { }
+ \token_if_toks_register:NTF #1 \prg_map_break: { }
+ \use_none:nnnn
+ \prg_break_point:n { }
+ \use:n { = \tex_the:D #1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}[aux, rEXP]{\tl_show_analysis_cs:n}
% \begin{macro}[aux, rEXP]{\tl_show_analysis_active:n}
% \begin{macro}[aux, rEXP]{\tl_show_analysis_long:nn}
-% \begin{macro}[aux, rEXP]{\tl_show_analysis_long_aux:nnn}
+% \begin{macro}[aux, rEXP]{\tl_show_analysis_long_aux:nnnn}
% Control sequences and active characters are printed in the same way,
% making sure not to go beyond the \cs{l_iow_line_length_int}. In case
% of an overflow, we replace the last characters by
@@ -1019,25 +1048,27 @@
{ \exp_args:No \tl_show_analysis_long:nn {#1} { active~character= } }
\cs_new:Npn \tl_show_analysis_long:nn #1
{
- \exp_args:Noo \tl_show_analysis_long_aux:nnn
+ \tl_show_analysis_long_aux:oofn
{ \token_to_str:N #1 }
{ \token_to_meaning:N #1 }
+ { \tl_show_analysis_value:N #1 }
}
-\cs_new:Npn \tl_show_analysis_long_aux:nnn #1#2#3
+\cs_new:Npn \tl_show_analysis_long_aux:nnnn #1#2#3#4
{
\int_compare:nNnTF
- { \str_length:n { #1 ~ ( #3 #2 ) } }
+ { \str_length:n { #1 ~ ( #4 #2 #3 ) } }
> { \l_iow_line_length_int - \c_three }
{
- \str_substr:nnn { #1 ~ ( #3 #2 ) } \c_zero
+ \str_substr:nnn { #1 ~ ( #4 #2 #3 ) } \c_zero
{
\l_iow_line_length_int - \c_three
- \str_length:N \c_tl_show_analysis_etc_str
}
\c_tl_show_analysis_etc_str
}
- { #1 ~ ( #3 #2 ) }
+ { #1 ~ ( #4 #2 #3 ) }
}
+\cs_generate_variant:Nn \tl_show_analysis_long_aux:nnnn { oof }
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -1060,7 +1091,8 @@
\msg_kernel_new:nnn { tl-analysis } { show }
{
The~token~list~
- \str_if_eq:nnF {#1} { \l_tl_tmpa_tl } { \token_to_str:N #1 ~ }
+ \str_if_eq:nnF {#1} { \l_tl_analysis_internal_tl }
+ { \token_to_str:N #1 ~ }
\tl_if_empty:NTF #1
{ is~empty }
{ contains~the~tokens: }
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-build.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-build.dtx
new file mode 100644
index 00000000000..d3fa2d4e366
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3tl-build.dtx
@@ -0,0 +1,301 @@
+% \iffalse meta-comment
+%
+%% File: l3tl-build.dtx Copyright (C) 2011-2012 The LaTeX3 Project
+%%
+%% It may be distributed and/or modified under the conditions of the
+%% LaTeX Project Public License (LPPL), either version 1.3c of this
+%% license or (at your option) any later version. The latest version
+%% of this license is in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3experimental bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX3 Project.
+%%
+%% -----------------------------------------------------------------------
+%
+%<*driver|package>
+\RequirePackage{expl3}
+\GetIdInfo$Id: l3tl-build.dtx 3039 2011-12-08 09:22:35Z bruno $
+ {L3 Experimental Build of Token lists}
+%</driver|package>
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+% \title{^^A
+% The \textsf{l3tl-build} package: building token lists^^A
+% \thanks{This file describes v\ExplFileVersion,
+% last revised \ExplFileDate.}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released \ExplFileDate}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3tl-build} documentation}
+%
+% This module provides no user function.
+%
+% There are two main ways of building token lists from individual
+% tokens. Either in one go within an \texttt{x}-expanding assignment, or
+% by repeatedly using \cs{tl_put_right:Nn}. The first method takes a
+% linear time, but only allows expandable operations. The second method
+% takes a time quadratic in the length of the token list, but allows
+% expandable and non-expandable operations.
+%
+% The goal of this module is to provide functions to build a token list
+% piece by piece in linear time, while allowing non-expandable
+% operations. This is achieved by abusing \tn{toks}: adding some tokens
+% to the token list is done by storing them in a free token register
+% (time $O(1)$ for each such operation). Those token registers are only
+% put together at the end, within an \texttt{x}-expanding assignment,
+% which takes a linear time.\footnote{If we run out of token registers,
+% then the currently filled-up \tn{toks} are put together in a
+% temporary token list, and cleared, and we ultimately use
+% \cs{tl_put_right:Nx} to put those chunks together. Hence the true
+% asymptotic is quadratic, with a very small constant.} Of course,
+% all this must be done in a group: we can't go and clobber the values
+% of legitimate \tn{toks} used by \LaTeXe{}.
+%
+% Since none of the current applications need the ability to insert
+% material on the left of the token list, I have not implemented
+% that. This could be done for instance by using odd-numbered \tn{toks}
+% for the left part, and even-numbered \tn{toks} for the right part.
+%
+% \subsection{Internal functions}
+%
+% \begin{function}
+% {
+% \tl_set_build:Nw, \tl_gset_build:Nw,
+% \tl_set_build_x:Nw, \tl_gset_build_x:Nw
+% }
+% \begin{syntax}
+% \cs{tl_set_build:Nw} \meta{tl~var} \texttt{\ldots{}}
+% \cs{tl_build_one:n} \Arg{tokens_1} \texttt{\ldots{}}
+% \cs{tl_build_one:n} \Arg{tokens_2} \texttt{\ldots{}}
+% \ldots{}
+% \cs{tl_build_end:}
+% \end{syntax}
+% Defines the \meta{tl~var} to contain the contens of \meta{tokens1}
+% followed by \meta{tokens2}, \emph{etc.} This is built in such a way
+% to be more efficient than repeatedly using \cs{tl_put_right:Nn}. The
+% code in \enquote{\texttt{\ldots{}}} does not need to be
+% expandable. The commands \cs{tl_set_build:Nw} and \cs{tl_build_end:}
+% start and end a group. The assignment to the \meta{tl~var} occurs
+% just after the end of that group, using \cs{tl_set:Nn},
+% \cs{tl_gset:Nn}, \cs{tl_set:Nx}, or \cs{tl_gset:Nx}.
+% \end{function}
+%
+% \begin{function}{\tl_build_one:n, \tl_build_one:o, \tl_build_one:x}
+% \begin{syntax}
+% \cs{tl_build_one:n} \Arg{tokens}
+% \end{syntax}
+% This function may only be used within the scope of a
+% \cs{tl_set_build:Nw} function. It adds the \meta{tokens} on the
+% right of the current token list.
+% \end{function}
+%
+% \begin{function}{\tl_build_end:}
+% Ends the scope started by \cs{tl_set_build:Nw}, and performs the
+% relevant assignment.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3tl-build} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ProvidesExplPackage
+ {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+% \end{macrocode}
+%
+% \subsection{Variables and helper functions}
+%
+% \begin{variable}{\l_tl_build_start_index_int, \l_tl_build_index_int}
+% Integers pointing to the starting index (currently always starts at
+% zero), and the current index. The corresponding \tn{toks} are
+% accessed directly by number.
+% \begin{macrocode}
+\int_new:N \l_tl_build_start_index_int
+\int_new:N \l_tl_build_index_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_build_result_tl}
+% The resulting token list is normally built in one go by unpacking
+% all \tn{toks} in some range. In the rare cases where there are too
+% many \cs{tl_build_one:n} commands, leading to the depletion of
+% registers, the contents of the current set of \tn{toks} is unpacked
+% into \cs{l_tl_build_result_tl}. This prevents overflow from
+% affecting the end-user (beyond an obvious performance hit).
+% \begin{macrocode}
+\tl_new:N \l_tl_build_result_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\tl_build_unpack:}
+% \begin{macro}[aux, EXP]{\tl_build_unpack_loop:w}
+% The various pieces of the token list are built in \tn{toks} from the
+% \texttt{start_index} (inclusive) to the (current) \texttt{index}
+% (excluded). Those \tn{toks} are unpacked and stored in order in the
+% \texttt{result} token list. Optimizations would be possible here,
+% for instance, unpacking $10$ \tn{toks} at a time with a macro
+% expanding to |\the\toks#10...\the\toks#19|, but this should be kept
+% for much later.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \tl_build_unpack:
+ {
+ \tl_put_right:Nx \l_tl_build_result_tl
+ {
+ \exp_after:wN \tl_build_unpack_loop:w
+ \int_use:N \l_tl_build_start_index_int ;
+ \prg_break_point:n { }
+ }
+ }
+\cs_new:Npn \tl_build_unpack_loop:w #1 ;
+ {
+ \if_num:w #1 = \l_tl_build_index_int
+ \exp_after:wN \prg_map_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \exp_stop_f:
+ \exp_after:wN \tl_build_unpack_loop:w
+ \int_use:N \int_eval:w #1 + \c_one ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Building the token list}
+%
+% \begin{macro}
+% {
+% \tl_set_build:Nw , \tl_set_build_x:Nw ,
+% \tl_gset_build:Nw , \tl_gset_build_x:Nw
+% }
+% \begin{macro}[aux]{\tl_set_build_aux:NNw}
+% Similar to what is done for coffins: redefine some command, here
+% \cs{tl_build_end_aux:n} to hold the relevant assignment (see
+% \cs{tl_build_end:} for details). Then initialize the start index and
+% the current index at zero, and empty the \texttt{result} token list.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \tl_set_build:Nw
+ { \tl_set_build_aux:NNw \tl_set:Nn }
+\cs_new_protected_nopar:Npn \tl_set_build_x:Nw
+ { \tl_set_build_aux:NNw \tl_set:Nx }
+\cs_new_protected_nopar:Npn \tl_gset_build:Nw
+ { \tl_set_build_aux:NNw \tl_gset:Nn }
+\cs_new_protected_nopar:Npn \tl_gset_build_x:Nw
+ { \tl_set_build_aux:NNw \tl_gset:Nx }
+\cs_new_protected:Npn \tl_set_build_aux:NNw #1#2
+ {
+ \group_begin:
+ \cs_set_nopar:Npn \tl_build_end_assignment:n
+ { \group_end: #1 #2 }
+ \int_zero:N \l_tl_build_start_index_int
+ \int_zero:N \l_tl_build_index_int
+ \tl_clear:N \l_tl_build_result_tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_end:}
+% \begin{macro}[aux]{\tl_build_end_assignment:n}
+% When we are done building a token list, unpack all \tn{toks} into
+% the \texttt{result} token list, and expand this list before closing
+% the group. The \cs{tl_build_end_assignment:n} function is defined by
+% \cs{tl_set_build_aux:NNw} to end the group and hold the relevant
+% assignment. Its value outside is irrelevant, but just in case, we
+% set it to a function which would clean up the contents of
+% \cs{l_tl_build_result_tl}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \tl_build_end:
+ {
+ \tl_build_unpack:
+ \exp_args:No
+ \tl_build_end_assignment:n \l_tl_build_result_tl
+ }
+\cs_new_eq:NN \tl_build_end_assignment:n \use_none:n
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_one:n, \tl_build_one:o, \tl_build_one:x}
+% Store the tokens in a free \tn{toks}, then move the pointer to the
+% next one. If we overflow, unpack the current \tn{toks}, and reset
+% the current index, preparing to fill more \tn{toks}. This could be
+% optimized by avoiding to read |#1|, using \tn{afterassignment}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_one:n #1
+ {
+ \tex_toks:D \l_tl_build_index_int {#1}
+ \tex_advance:D \l_tl_build_index_int \c_one
+ \if_num:w \l_tl_build_index_int > \c_max_register_int
+ \tl_build_unpack:
+ \l_tl_build_index_int \l_tl_build_start_index_int
+ \fi:
+ }
+\cs_new_protected:Npn \tl_build_one:o #1
+ {
+ \tex_toks:D \l_tl_build_index_int \exp_after:wN {#1}
+ \tex_advance:D \l_tl_build_index_int \c_one
+ \if_num:w \l_tl_build_index_int > \c_max_register_int
+ \tl_build_unpack:
+ \l_tl_build_index_int \l_tl_build_start_index_int
+ \fi:
+ }
+\cs_new_protected:Npn \tl_build_one:x #1
+ { \use:x { \tl_build_one:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx
index 30a58c18817..be9e4e24f75 100644
--- a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx
+++ b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx
@@ -1,5 +1,5 @@
% \iffalse meta-comment
-%
+%
%% File: xcoffins.dtx Copyright(C) 2010,2011 The LaTeX3 Project
%%
%% It may be distributed and/or modified under the conditions of the
@@ -36,7 +36,7 @@
%
%<*driver|package>
\RequirePackage{xparse}
-\GetIdInfo$Id: xcoffins.dtx 2963 2011-11-15 22:06:41Z joseph $
+\GetIdInfo$Id: xcoffins.dtx 3385 2012-02-12 20:37:09Z bruno $
{L3 Experimental design-level coffins}
%</driver|package>
%<*driver>
@@ -56,13 +56,13 @@
\end{document}
%</driver>
% \fi
-%
+%
% \title{^^A
% The \textsf{xcoffins} package\\ Design-level coffins^^A
% \thanks{This file describes v\ExplFileVersion,
% last revised \ExplFileDate.}^^A
% }
-%
+%
% \author{^^A
% The \LaTeX3 Project\thanks
% {^^A
@@ -89,7 +89,7 @@
% positions, for example the top or horizontal centre. The points
% where these poles intersect are called \enquote{handles}. Two
% coffins can then be aligned by describing the relationship between
-% a handle on one coffin with a handle on the second. In words, an
+% a handle on one coffin with a handle on the second. In words, an
% example might then read
% \begin{quote}
% Align the top-left handle of coffin A with the bottom-right
@@ -97,20 +97,20 @@
% \end{quote}
%
% The locations of coffin handles are much easier to understand
-% visually. Figure~\ref{fgr:handles} shows the standard handle
-% positions for a coffin typeset in horizontal mode (left) and in
+% visually. Figure~\ref{fgr:handles} shows the standard handle
+% positions for a coffin typeset in horizontal mode (left) and in
% vertical mode (right). Notice that the later case results in a greater
% number of handles being available. As illustrated, each handle
% results from the intersection of two poles. For example, the centre
% of the coffin is marked |(hc,vc)|, \emph{i.e.}~it is the
-% point of intersection of the horizontal centre pole with the
+% point of intersection of the horizontal centre pole with the
% vertical centre pole. New handles are generated automatically when
% poles are added to a coffin: handles are \enquote{dynamic} entities.
%
% \begin{figure}
% \hfil
% \begin{minipage}{0.4\textwidth}
-% \SetHorizontalCoffin\ExampleCoffin
+% \SetHorizontalCoffin\ExampleCoffin
% {\color{black!10!white}\rule{1 in}{1 in}}
% \DisplayCoffinHandles\ExampleCoffin{blue}
% \end{minipage}
@@ -123,9 +123,9 @@
% \hfil
% \caption{Standard coffin handles: left, horizontal coffin; right,
% vertical coffin}
-% \label{fgr:handles}
+% \label{fgr:handles}
% \end{figure}
-%
+%
%\section{Creating and setting coffins}
%
% Before any alignment can take place, coffins must be created and
@@ -139,7 +139,7 @@
% \begin{syntax}
% \cs{NewCoffin} \meta{coffin}
% \end{syntax}
-% Before a \meta{coffin} can be used, it must be allocated using
+% Before a \meta{coffin} can be used, it must be allocated using
% \cs{NewCoffin}. The name of the \meta{coffin} should be a
% control sequence (starting with the escape character, usually
% |\|), for example
@@ -175,7 +175,7 @@
% \end{syntax}
% Typesets the \meta{material} in vertical mode constrained to the
% given \meta{width} and stores the result in the \meta{coffin}. The
-% standard poles for the \meta{coffin} are then set up based on the
+% standard poles for the \meta{coffin} are then set up based on the
% size of the typeset material.
% \end{function}
%
@@ -194,7 +194,7 @@
% box of the coffin;
% \item[b] a pole running along the bottom edge of the bounding
% box of the coffin;
-% \item[vc] a pole running horizontally through the centre of the
+% \item[vc] a pole running horizontally through the centre of the
% coffin half-way between the bottom and top edges of the bounding
% box (\emph{i.e.}~the \enquote{vertical centre});
% \item[t] a pole running along the top edge of the bounding
@@ -202,7 +202,7 @@
% \item[H] a pole running along the baseline of the typeset material
% contained in the coffin.
% \end{itemize}
-% In addition, coffins containing vertical-mode material also
+% In addition, coffins containing vertical-mode material also
% feature poles which reflect the richer nature of these systems:
% \begin{itemize}
% \item[B] a pole running along the baseline of the material at the
@@ -219,10 +219,10 @@
% The \meta{pole} will be located at the \meta{offset} from the
% bottom edge of the bounding box of the \meta{coffin}. The
% \meta{offset} should be given as a dimension expression; this may
-% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
-% \cs{Width}, which will evaluate to the appropriate dimensions of
-% the \meta{coffin}. For example, to create a pole running
-% horizontally through the coffin at one third of the distance from
+% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
+% \cs{Width}, which will evaluate to the appropriate dimensions of
+% the \meta{coffin}. For example, to create a pole running
+% horizontally through the coffin at one third of the distance from
% the base of the coffin to the top, the appropriate instruction would
% be
% \begin{verbatim}
@@ -230,7 +230,7 @@
% \end{verbatim}
% Note that poles which run \emph{horizontally} are described in terms
% of their \emph{vertical} location in the coffin. Also notice that
-% the total height of the coffin is described by the sum of
+% the total height of the coffin is described by the sum of
% \cs{Height} and \cs{Depth}: these are both measured from the
% horizontal baseline of the material in the coffin.
% \end{function}
@@ -243,10 +243,10 @@
% The \meta{pole} will be located at the \meta{offset} from the
% left-hand edge of the bounding box of the \meta{coffin}. The
% \meta{offset} should be given as a dimension expression; this may
-% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
-% \cs{Width}, which will evaluate to the appropriate dimensions of
+% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
+% \cs{Width}, which will evaluate to the appropriate dimensions of
% the \meta{coffin}. For example, to create a pole running vertically
-% through the coffin at one third of the distance from the left-hand
+% through the coffin at one third of the distance from the left-hand
% edge, the appropriate instruction would be
% \begin{verbatim}
% \SetVerticalPole \MyCoffin {width/3} {\Width/3}
@@ -260,7 +260,7 @@
% \cs{TotalHeight}
% \end{syntax}
% Within the \meta{offset} argument of \cs{SetHorizontalPole} and
-% \cs{SetVerticalPole}, \cs{TotalHeight} will give the distance from
+% \cs{SetVerticalPole}, \cs{TotalHeight} will give the distance from
% the base to the top of the bounding box of the relevant coffin.
% \end{function}
%
@@ -300,10 +300,10 @@
% \end{syntax}
% Rotates the \meta{coffin} by the given \meta{angle} about its reference
% point (given in degrees counter-clockwise) . This process will rotate both
-% the coffin content and poles. Multiple rotations will not result in
+% the coffin content and poles. Multiple rotations will not result in
% the bounding box of the coffin growing unnecessarily.
-%
-% The effect of rotation on a coffin is illustrated in
+%
+% The effect of rotation on a coffin is illustrated in
% Figure~\ref{fgr:rotation}. As is shown, the coffin handles will
% remain correctly positioned relative to the content of the coffin.
% The \enquote{top} of a rotated coffin may of course no longer be the
@@ -312,7 +312,7 @@
%
% \begin{figure}
% \hfil
-% \SetHorizontalCoffin\ExampleCoffin
+% \SetHorizontalCoffin\ExampleCoffin
% {^^A
% \color{black!10!white}\rule{0.5 in}{1 in}^^A
% \color{black!20!white}\rule{0.5 in}{1 in}^^A
@@ -339,8 +339,8 @@
% \end{syntax}
% Resized the \meta{coffin} to \meta{width} and \meta{total-height},
% both of which should be given as dimension expressions. These may
-% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
-% \cs{Width}, which will evaluate to the appropriate dimensions of
+% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
+% \cs{Width}, which will evaluate to the appropriate dimensions of
% the \meta{coffin}.
% \end{function}
%
@@ -348,8 +348,8 @@
% \begin{syntax}
% \cs{ScaleCoffin} \meta{coffin} \Arg{x-scale} \Arg{y-scale}
% \end{syntax}
-% Scales the \meta{coffin} by a factors \meta{x-scale} and
-% \meta{y-scale} in the horizontal and vertical directions,
+% Scales the \meta{coffin} by a factors \meta{x-scale} and
+% \meta{y-scale} in the horizontal and vertical directions,
% respectively. The two scale factors should be given as real numbers.
% \end{function}
%
@@ -359,7 +359,7 @@
%
% \begin{figure}
% \hfil
-% \SetHorizontalCoffin\ExampleCoffin
+% \SetHorizontalCoffin\ExampleCoffin
% {^^A
% \color{black!10!white}\rule{0.5 in}{1 in}^^A
% \color{black!20!white}\rule{0.5 in}{1 in}^^A
@@ -376,26 +376,26 @@
% \hfil
% \caption{Coffin resizing: left, resized to exactly $4$\,cm by
% $6$\,cm; right, scaled a factors of $2$ and $0.5$ in
-% $x$ and $y$, respectively (example coffin as in
+% $x$ and $y$, respectively (example coffin as in
% Figure~\ref{fgr:rotation}).}
-% \label{fgr:resizing}
+% \label{fgr:resizing}
% \end{figure}
%
% \section{Joining coffins}
%
% The key operation for coffins is joining coffins to each other. This
-% is always carried out such that the first coffin is the
+% is always carried out such that the first coffin is the
% \enquote{parent}, and is updated by the alignment. The second
% \enquote{child} coffin is not altered by the alignment process.
-%
+%
% \begin{function}{\JoinCoffins}
% \begin{syntax}
% \cs{JoinCoffins} *
-% ~~\meta{coffin1} [ \meta{coffin1-pole1} , \meta{coffin1-pole2} ]
+% ~~\meta{coffin1} [ \meta{coffin1-pole1} , \meta{coffin1-pole2} ]
% ~~\meta{coffin2} [ \meta{coffin2-pole1} , \meta{coffin2-pole2} ]
-% ~~( \meta{x-offset} , \meta{y-offset} )
+% ~~( \meta{x-offset} , \meta{y-offset} )
% \end{syntax}
-% Joining of two coffins is carried out by the \cs{JoinCoffins}
+% Joining of two coffins is carried out by the \cs{JoinCoffins}
% function, which takes two mandatory arguments: the \enquote{parent}
% \meta{coffin1} and the \enquote{child} \meta{coffin2}. All of the
% other arguments shown are optional.
@@ -403,15 +403,15 @@
%
% The standard \cs{JoinCoffins} functions joins \meta{coffin2} to
% \meta{coffin1} such that the bounding box of \meta{coffin1} after the
-% process will expand. The new bounding box will be the smallest
-% rectangle covering the bounding boxes of the two input coffins.
+% process will expand. The new bounding box will be the smallest
+% rectangle covering the bounding boxes of the two input coffins.
% When the starred variant of \cs{JoinCoffins} is used, the bounding
% box of \meta{coffin1} is not altered, \emph{i.e.}~\meta{coffin2} may
% protrude outside of the bounding box of the updated \meta{coffin1}.
% The difference between the two forms of alignment is best illustrated
% using a visual example. In Figure~\ref{fgr:alignment}, the two
% processes are contrasted. In both cases, the small red coffin has been
-% aligned with the large grey coffin. In the left-hand illustration,
+% aligned with the large grey coffin. In the left-hand illustration,
% the \cs{JoinCoffins} function was used, resulting in an expanded
% bounding box. In contrast, on the right \cs{AttachCoffin} was used,
% meaning that the bounding box does not include the area of the
@@ -419,9 +419,9 @@
%
% \begin{figure}
% \fboxsep 0 pt\relax
-% \SetHorizontalCoffin\ExampleCoffin
+% \SetHorizontalCoffin\ExampleCoffin
% {\color{black!20!white}\rule{1 in}{1 in}}
-% \SetHorizontalCoffin \SmallCoffin
+% \SetHorizontalCoffin \SmallCoffin
% {\color{red!20!white}\rule{0.1 in}{0.1 in}}
% \hfil
% \begin{minipage}{0.4\textwidth}
@@ -436,14 +436,14 @@
% \fbox{\TypesetCoffin\ExampleCoffin}%
% \end{minipage}
% \hfil
-% \caption{Contrast between \cs{JoinCoffins} (left) and
+% \caption{Contrast between \cs{JoinCoffins} (left) and
% \cs{JoinCoffins*} (right); the bounding box of the coffin is show
% in black.}
-% \label{fgr:alignment}
+% \label{fgr:alignment}
% \end{figure}
-%
+%
% The alignment is carried out by first calculating \meta{handle1}, the
-% point of intersection of \meta{coffin1-pole1} and
+% point of intersection of \meta{coffin1-pole1} and
% \meta{coffin1-pole2}, and \meta{handle2}, the point of intersection
% of \meta{coffin2-pole1} and \meta{coffin2-pole2}. If the two
% \meta{poles} are not specified, \cs{JoinCoffins} will use the
@@ -456,19 +456,19 @@
% |(0 pt, 0 pt)| is used.
%
% Notice that when \cs{JoinCoffins} is used the new bounding box is
-% the smallest rectangle containing the bounding boxes of the two input
+% the smallest rectangle containing the bounding boxes of the two input
% coffins. As a result, it will include additional white space unless
-% one coffin entirely overlaps the other (Figure~\ref{fgr:bounding},
+% one coffin entirely overlaps the other (Figure~\ref{fgr:bounding},
% left). Rotation of coffins will take account of the extent of the
% material after rotation when re-calculating the bounding box. This
-% means that no \emph{unnecessary} white space will be added on
+% means that no \emph{unnecessary} white space will be added on
% rotation (Figure~\ref{fgr:bounding}, right).
%
% \begin{figure}
% \fboxsep 0 pt\relax
-% \SetHorizontalCoffin\ExampleCoffin
+% \SetHorizontalCoffin\ExampleCoffin
% {\color{black!20!white}\rule{1 in}{1 in}}
-% \SetHorizontalCoffin\SmallCoffin
+% \SetHorizontalCoffin\SmallCoffin
% {\color{red!20!white}\rule{0.1 in}{0.1 in}}
% \JoinCoffins\ExampleCoffin[vc,r]\SmallCoffin[vc,l]
% \hfil
@@ -485,7 +485,7 @@
% \hfil
% \caption{The effect of rotation of a joined coffin: the black line
% shows the coffin bounding box.}
-% \label{fgr:bounding}
+% \label{fgr:bounding}
% \end{figure}
%
% As part of the joining procedure, the poles of the two input coffins
@@ -495,35 +495,35 @@
% \begin{enumerate}
% \item The \enquote{native} poles of the updated coffin, such as
% \texttt{l}, \texttt{r}, \texttt{hc}, \emph{etc}.
-% \item Poles derived from \meta{coffin1}, such as
+% \item Poles derived from \meta{coffin1}, such as
% \texttt{\meta{coffin1}-l}, \texttt{\meta{coffin1}-r},
% \texttt{\meta{coffin1}-hc}, \emph{etc.}
-% \item Poles derived from \meta{coffin2}, such as
+% \item Poles derived from \meta{coffin2}, such as
% \texttt{\meta{coffin2}-l}, \texttt{\meta{coffin2}-r},
% \texttt{\meta{coffin2}-hc}, \emph{etc.}
% \end{enumerate}
-%
+%
% Applying this ability allows a series of joining operations to
% take place, as illustrated in Figure~\ref{fgr:nested}. In this
% example, the scheme used for alignment was as follows:
% \begin{verbatim}
% \SetHorizontalCoffin\OutputCoffin{}
-% \SetHorizontalCoffin\RedCoffin
+% \SetHorizontalCoffin\RedCoffin
% {\color{red!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[vc,hc]\RedCoffin[vc,hc]
-% \SetHorizontalCoffin\BlueCoffin
+% \SetHorizontalCoffin\BlueCoffin
% {\color{blue!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\RedCoffin-vc,\RedCoffin-hc]
% \BlueCoffin[b,l]
-% \SetHorizontalCoffin\GreenCoffin
+% \SetHorizontalCoffin\GreenCoffin
% {\color{green!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\BlueCoffin-vc,\BlueCoffin-hc]
% \GreenCoffin[b,l]
-% \SetHorizontalCoffin\YellowCoffin
+% \SetHorizontalCoffin\YellowCoffin
% {\color{yellow!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\GreenCoffin-vc,\GreenCoffin-hc]
% \YellowCoffin[b,l]
-% \SetHorizontalCoffin \OrangeCoffin
+% \SetHorizontalCoffin \OrangeCoffin
% {\color{orange!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\BlueCoffin-t,\BlueCoffin-l]
% \OrangeCoffin[b,r]
@@ -533,62 +533,62 @@
% output. Each join then takes place placing the new addition relative
% to the previous one. As each coffin joined has a unique name it is
% possible to align relative to each one of the component parts of the
-% assembly. This is illustrated by the addition of the final
+% assembly. This is illustrated by the addition of the final
% \cs{OrangeCoffin} based on the earlier placement of the
% \cs{BlueCoffin}.
%
% \begin{figure}
% \centering
% \SetHorizontalCoffin\OutputCoffin{}
-% \SetHorizontalCoffin\RedCoffin
+% \SetHorizontalCoffin\RedCoffin
% {\color{red!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[vc,hc]\RedCoffin[vc,hc]
-% \SetHorizontalCoffin\BlueCoffin
+% \SetHorizontalCoffin\BlueCoffin
% {\color{blue!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\RedCoffin-vc,\RedCoffin-hc]
% \BlueCoffin[b,l]
-% \SetHorizontalCoffin\GreenCoffin
+% \SetHorizontalCoffin\GreenCoffin
% {\color{green!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\BlueCoffin-vc,\BlueCoffin-hc]
% \GreenCoffin[b,l]
-% \SetHorizontalCoffin\YellowCoffin
+% \SetHorizontalCoffin\YellowCoffin
% {\color{yellow!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\GreenCoffin-vc,\GreenCoffin-hc]
% \YellowCoffin[b,l]
-% \SetHorizontalCoffin \OrangeCoffin
+% \SetHorizontalCoffin \OrangeCoffin
% {\color{orange!20!white}\rule{0.2 in}{0.2 in}}
% \JoinCoffins\OutputCoffin[\BlueCoffin-t,\BlueCoffin-l]
% \OrangeCoffin[b,r]
% \TypesetCoffin\OutputCoffin
% \caption{Aligning coffins using poles from previous operations.}
-% \label{fgr:nested}
+% \label{fgr:nested}
% \end{figure}
%
% \section{Typesetting coffins}
%
% \begin{function}{\TypesetCoffin}
% \begin{syntax}
-% \cs{TypesetCoffin}
+% \cs{TypesetCoffin}
% ~~\meta{coffin} [ \meta{pole1} , \meta{pole2} ]
-% ~~( \meta{x-offset} , \meta{y-offset} )
+% ~~( \meta{x-offset} , \meta{y-offset} )
% \end{syntax}
% Typesetting is carried out by first calculating \meta{handle}, the
% point of intersection of \meta{pole1} and \meta{pole2}. This is an
% optional argument, and if not given then |(H,l)|, the \TeX{}
% reference point of the underlying box, is used. The coffin
-% is then typeset such that the relationship between the current
+% is then typeset such that the relationship between the current
% reference point in the document and the \meta{handle} is described
% by the \meta{x-offset} and \meta{y-offset}. This \meta{offset} is
% optional, and if not given |(0 pt, 0 pt)| is used. Typesetting a
% 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}
@@ -606,7 +606,7 @@
% 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}
@@ -627,7 +627,7 @@
%
% \section{Diagnostic functions}
%
-% Diagnostic data for following the coffin-building process is
+% Diagnostic data for following the coffin-building process is
% available both graphically and at the terminal. This reflects the
% fact that coffins are visual constructs.
%
@@ -636,22 +636,22 @@
% \cs{DisplayCoffinHandles} \meta{coffin} \Arg{colour}
% \end{syntax}
% This function first calculates the intersections between all of
-% the \meta{poles} of the \meta{coffin} to give a set of
-% \meta{handles}. It then prints the \meta{coffin} at the current
-% location in the source, with the position of the \meta{handles}
-% marked on the coffin. The \meta{handles} will be labelled as part
-% of this process: the locations of the \meta{handles} and the labels
+% the \meta{poles} of the \meta{coffin} to give a set of
+% \meta{handles}. It then prints the \meta{coffin} at the current
+% location in the source, with the position of the \meta{handles}
+% marked on the coffin. The \meta{handles} will be labelled as part
+% of this process: the locations of the \meta{handles} and the labels
% are both printed in the \meta{colour} specified.
% \end{function}
%
% \begin{function}{\MarkCoffinHandle}
% \begin{syntax}
-% \cs{MarkCoffinHandle} \meta{coffin}
+% \cs{MarkCoffinHandle} \meta{coffin}
% ~~[ \meta{pole1} , \meta{pole2} ] \Arg{colour}
% \end{syntax}
-% This function first calculates the \meta{handle} for the
+% This function first calculates the \meta{handle} for the
% \meta{coffin} as defined by the intersection of \meta{pole1} and
-% \meta{pole2}. It then marks the position of the \meta{handle}
+% \meta{pole2}. It then marks the position of the \meta{handle}
% on the \meta{coffin}. The \meta{handle} will be labelled as part of
% this process: the location of the \meta{handle} and the label are
% both printed in the \meta{colour} specified. If no \meta{poles} are
@@ -663,9 +663,9 @@
% \cs{ShowCoffinStructure} \meta{coffin}
% \end{syntax}
% This function shows the structural information about the
-% \meta{coffin} in the terminal. The width, height and depth of the
-% typeset material are given, along with the location of all of the
-% poles of the coffin. For example, for the rotated coffin in
+% \meta{coffin} in the terminal. The width, height and depth of the
+% typeset material are given, along with the location of all of the
+% poles of the coffin. For example, for the rotated coffin in
% Figure~\ref{fgr:rotation}, the output of \cs{ShowCoffinStructure}
% is:
% \begin{verbatim}
@@ -673,7 +673,6 @@
% > ht = 72.26999pt
% > dp = 0.0pt
% > wd = 72.26999pt
-%
% Poles of coffin \ExampleCoffin:
% > l => {0pt}{0pt}{0pt}{1000pt}
% > B => {0pt}{0pt}{1000pt}{0pt}
@@ -684,11 +683,11 @@
% > vc => {0pt}{36.135pt}{1000pt}{0pt}
% > t => {0pt}{72.26999pt}{1000pt}{0pt}
% > b => {0pt}{0.0pt}{1000pt}{0pt}.
-% <argument> ...ffin_show_aux:w \l_coffin_tmp_tl }
+% <recently read> }
% \end{verbatim}
% Notice that the poles of a coffin are defined by four values:
-% the $x$ and $y$ co-ordinates of a point that the pole
-% passes through and the $x$- and $y$-components of a
+% the $x$ and $y$ co-ordinates of a point that the pole
+% passes through and the $x$- and $y$-components of a
% vector denoting the direction of the pole. It is the ratio between
% the later, rather than the absolute values, which determines the
% direction of the pole.
@@ -699,17 +698,17 @@
% \begin{implementation}
%
% \section{Implementation}
-%
+%
% \begin{macrocode}
%<*package>
% \end{macrocode}
-%
+%
% \begin{macrocode}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
% \end{macrocode}
-%
-%
+%
+%
% \begin{variable}
% {
% \l_coffin_A_hpole_tl ,
@@ -722,7 +721,7 @@
% }
% Key--value definitions for the alignment system. With the exception
% of \texttt{grow-bounding-box}, all of these have to be given with a
-% value.
+% value.
% \begin{macrocode}
\keys_define:nn { coffin }
{
@@ -755,7 +754,7 @@
% \end{variable}
%
% A lot of this is more-or-less just passing data straight through.
-%
+%
% \begin{macro}{\NewCoffin}
% This is a very easy conversion.
% \begin{macrocode}
@@ -763,7 +762,7 @@
{ \coffin_new:N #1 }
% \end{macrocode}
% \end{macro}
-%
+%
% \begin{macro}{\BoxToCoffin}
% For allowing a user-level \LaTeXe{} box to be used as a coffin.
% \begin{macrocode}
@@ -855,12 +854,12 @@
% which need to be split. This is a simpler case of the code needed for
% \cs{JoinCoffins}.
% \begin{macrocode}
-\NewDocumentCommand \TypesetCoffin
+\NewDocumentCommand \TypesetCoffin
{
- m
- > { \SplitArgument { 1 } { , } } O { H , l }
+ m
+ > { \SplitArgument { 1 } { , } } O { H , l }
> { \SplitArgument { 1 } { , } } D ( ) { 0 pt , 0 pt }
- }
+ }
{ \coffin_typeset:Nnnnn #1 #2 #3 }
% \end{macrocode}
% \end{macro}
@@ -880,7 +879,7 @@
% \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
@@ -911,10 +910,10 @@
% \end{macro}
%
% \begin{macro}{\MarkCoffinHandle}
-% Marking a handle requires a bit of work with the input, so that
+% Marking a handle requires a bit of work with the input, so that
% the design-level interface is \enquote{nice}.
% \begin{macrocode}
-\NewDocumentCommand \MarkCoffinHandle
+\NewDocumentCommand \MarkCoffinHandle
{ m > { \SplitArgument { 1 } { , } } O { H , l } m }
{
\cs_if_exist:cTF { l_coffin_poles_ \int_value:w #1 _prop }
diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3dt/l3dt.sty b/Master/texmf-dist/tex/latex/l3experimental/l3dt/l3dt.sty
index 5346944521c..99bd83e7764 100644
--- a/Master/texmf-dist/tex/latex/l3experimental/l3dt/l3dt.sty
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3dt/l3dt.sty
@@ -47,7 +47,7 @@
%% -----------------------------------------------------------------------
%%
\RequirePackage{expl3}
-\GetIdInfo$Id: l3dt.dtx 3251 2012-01-28 09:42:44Z joseph $
+\GetIdInfo$Id: l3dt.dtx 3459 2012-02-26 08:58:53Z joseph $
{L3 Experimental data tables}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3sort/l3sort.sty b/Master/texmf-dist/tex/latex/l3experimental/l3sort/l3sort.sty
index 4de96fa88a3..b24bacd3794 100644
--- a/Master/texmf-dist/tex/latex/l3experimental/l3sort/l3sort.sty
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3sort/l3sort.sty
@@ -47,7 +47,7 @@
%% -----------------------------------------------------------------------
%%
\RequirePackage{expl3}
-\GetIdInfo$Id: l3sort.dtx 3295 2012-02-02 09:30:14Z joseph $
+\GetIdInfo$Id: l3sort.dtx 3467 2012-02-26 15:39:35Z bruno $
{L3 Experimental sorting functions}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty
new file mode 100644
index 00000000000..60abf42cd5d
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty
@@ -0,0 +1,2824 @@
+%%
+%% This is file `l3regex-trace.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3regex.dtx (with options: `package,trace')
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+%% File: l3regex.dtx Copyright (C) 2011-2012 The LaTeX3 Project
+%%
+%% It may be distributed and/or modified under the conditions of the
+%% LaTeX Project Public License (LPPL), either version 1.3c of this
+%% license or (at your option) any later version. The latest version
+%% of this license is in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3experimental bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX3 Project.
+%%
+%% -----------------------------------------------------------------------
+\RequirePackage{expl3}
+\GetIdInfo$Id: l3regex.dtx 3463 2012-02-26 15:05:14Z bruno $
+ {L3 Experimental Regular Expressions}
+\ProvidesExplPackage
+ {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+\RequirePackage{l3tl-build, l3tl-analysis, l3flag, l3str}
+\cs_generate_variant:Nn \tl_to_str:n { V }
+\cs_new:Npn \regex_tmp:w { }
+\tl_new:N \l_regex_internal_a_tl
+\tl_new:N \l_regex_internal_b_tl
+\int_new:N \l_regex_internal_a_int
+\int_new:N \l_regex_internal_b_int
+\int_new:N \l_regex_internal_c_int
+\bool_new:N \l_regex_internal_bool
+\seq_new:N \l_regex_internal_seq
+\tl_new:N \g_regex_internal_tl
+\tl_const:Nn \c_regex_no_match_regex
+ {
+ \regex_branch:n
+ { \regex_class:NnnnN \c_true_bool { } { 1 } { 0 } \c_true_bool }
+ }
+\int_new:N \l_regex_balance_int
+\cs_new_protected:Npn \regex_break_true:w
+ #1 \regex_break_point:TF #2 #3 {#2}
+\cs_new_protected:Npn \regex_break_point:TF #1 #2 { #2 }
+\cs_new_protected:Npn \regex_item_reverse:n #1
+ {
+ #1
+ \regex_break_point:TF { } \regex_break_true:w
+ }
+\cs_new_protected: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_protected: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
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_caseless_equal:n #1
+ {
+ \if_num:w #1 = \l_regex_current_char_int
+ \exp_after:wN \regex_break_true:w
+ \fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \fi:
+ \if_num:w #1 = \l_regex_case_changed_char_int
+ \exp_after:wN \regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_caseless_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
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
+ \fi:
+ \fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \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_compute_case_changed_char:
+ {
+ \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_eq:NN \regex_item_equal:n ?
+\cs_new_eq:NN \regex_item_range:nn ?
+\cs_new_protected:Npn \regex_item_catcode_aux:
+ {
+ "
+ \if_case:w \l_regex_current_catcode_int
+ 1 \or: 4 \or: 10 \or: 40
+ \or: 100 \or: \or: 1000 \or: 4000
+ \or: 10000 \or: \or: 100000 \or: 400000
+ \or: 1000000 \or: 4000000 \else: 1*\c_zero
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_catcode:nT #1
+ {
+ \if_int_odd:w \int_eval:w #1 / \regex_item_catcode_aux: \int_eval_end:
+ \exp_after:wN \use:n
+ \else:
+ \exp_after:wN \use_none:n
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_catcode_reverse:nT #1#2
+ { \regex_item_catcode:nT {#1} { \regex_item_reverse:n {#2} } }
+\cs_new_protected:Npn \regex_item_exact:nn #1#2
+ {
+ \if_num:w #1 = \l_regex_current_catcode_int
+ \if_num:w #2 = \l_regex_current_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_exact_cs:c #1
+ {
+ \int_compare:nNnTF \l_regex_current_catcode_int = \c_zero
+ {
+ \str_if_eq:xxTF
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
+ }
+ { #1 }
+ { \regex_break_true:w } { }
+ }
+ { }
+ }
+\cs_new_protected:Npn \regex_item_cs:n #1
+ {
+ \int_compare:nNnT \l_regex_current_catcode_int = \c_zero
+ {
+ \group_begin:
+ \regex_single_match:
+ \regex_disable_submatches:
+ \regex_build_for_cs:n {#1}
+ \bool_set_eq:NN \l_regex_saved_success_bool \g_regex_success_bool
+ \exp_args:Nx \regex_match:n
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
+ }
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \group_insert_after:N \regex_break_true:w
+ \fi:
+ \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool
+ \group_end:
+ }
+ }
+\cs_new_protected_nopar:Npn \regex_prop_d:
+ { \regex_item_caseful_range:nn \c_forty_eight { 57 } } % 0--9
+\cs_new_protected_nopar:Npn \regex_prop_h:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ }
+\cs_new_protected_nopar:Npn \regex_prop_s:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ \regex_item_caseful_equal:n \c_ten % lf
+ \regex_item_caseful_equal:n \c_twelve % ff
+ \regex_item_caseful_equal:n \c_thirteen % cr
+ }
+\cs_new_protected_nopar:Npn \regex_prop_v:
+ { \regex_item_caseful_range:nn \c_ten \c_thirteen } % lf, vtab, ff, cr
+\cs_new_protected_nopar:Npn \regex_prop_w:
+ {
+ \regex_item_caseful_range:nn \c_ninety_seven { 122 } % a--z
+ \regex_item_caseful_range:nn \c_sixty_five { 90 } % A--Z
+ \regex_item_caseful_range:nn \c_forty_eight { 57 } % 0--9
+ \regex_item_caseful_equal:n { 95 } % _
+ }
+\cs_new_protected_nopar:Npn \regex_prop_N:
+ { \regex_item_reverse:n { \regex_item_caseful_equal:n \c_ten } }
+\cs_new_protected_nopar:Npn \regex_posix_alnum:
+ { \regex_posix_alpha: \regex_posix_digit: }
+\cs_new_protected_nopar:Npn \regex_posix_alpha:
+ { \regex_posix_lower: \regex_posix_upper: }
+\cs_new_protected_nopar:Npn \regex_posix_ascii:
+ { \regex_item_caseful_range:nn \c_zero \c_one_hundred_twenty_seven }
+\cs_new_eq:NN \regex_posix_blank: \regex_prop_h:
+\cs_new_protected_nopar:Npn \regex_posix_cntrl:
+ {
+ \regex_item_caseful_range:nn \c_zero { 31 }
+ \regex_item_caseful_equal:n \c_one_hundred_twenty_seven
+ }
+\cs_new_eq:NN \regex_posix_digit: \regex_prop_d:
+\cs_new_protected_nopar:Npn \regex_posix_graph:
+ { \regex_item_caseful_range:nn { 33 } { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_lower:
+ { \regex_item_caseful_range:nn \c_ninety_seven { 122 } }
+\cs_new_protected_nopar:Npn \regex_posix_print:
+ { \regex_item_caseful_range:nn \c_thirty_two { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_punct:
+ {
+ \regex_item_caseful_range:nn { 33 } { 47 }
+ \regex_item_caseful_range:nn { 58 } { 64 }
+ \regex_item_caseful_range:nn { 91 } { 96 }
+ \regex_item_caseful_range:nn { 123 } { 126 }
+ }
+\cs_new_protected_nopar:Npn \regex_posix_space:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two
+ \regex_item_caseful_range:nn \c_nine \c_thirteen
+ }
+\cs_new_protected_nopar:Npn \regex_posix_upper:
+ { \regex_item_caseful_range:nn \c_sixty_five { 90 } }
+\cs_new_eq:NN \regex_posix_word: \regex_prop_w:
+\cs_new_protected_nopar:Npn \regex_posix_xdigit:
+ {
+ \regex_posix_digit:
+ \regex_item_caseful_range:nn \c_sixty_five { 70 }
+ \regex_item_caseful_range:nn \c_ninety_seven { 102 }
+ }
+\cs_new_protected:Npn \regex_escape_use:nnnn #1#2#3#4
+ {
+ \trace_push:nnn { regex } { 1 } { regex_escape_use:nnnn }
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \cs_set_nopar:Npn \regex_escape_unescaped:N ##1 { #1 }
+ \cs_set_nopar:Npn \regex_escape_escaped:N ##1 { #2 }
+ \cs_set_nopar:Npn \regex_escape_raw:N ##1 { #3 }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \str_gset_other:Nn \g_regex_internal_tl { #4 }
+ \tl_set:Nx \l_regex_internal_b_tl
+ {
+ \exp_after:wN \regex_escape_loop:N \g_regex_internal_tl
+ { break } \prg_break_point:n { }
+ }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \tl_build_end:
+ \trace_pop:nnn { regex } { 1 } { regex_escape_use:nnnn }
+ \l_regex_internal_a_tl
+ }
+\cs_new:Npn \regex_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { regex_escape_\token_to_str:N #1:w }
+ { \regex_escape_unescaped:N #1 }
+ \regex_escape_loop:N
+ }
+\cs_new_nopar:cpn { regex_escape_ \c_backslash_str :w }
+ \regex_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { regex_escape_/\token_to_str:N #1:w }
+ { \regex_escape_escaped:N #1 }
+ \regex_escape_loop:N
+ }
+\cs_new_eq:NN \regex_escape_unescaped:N ?
+\cs_new_eq:NN \regex_escape_escaped:N ?
+\cs_new_eq:NN \regex_escape_raw:N ?
+\cs_new_eq:NN \regex_escape_break:w \prg_map_break:
+\cs_new_nopar:cpn { regex_escape_/break:w }
+ {
+ \if_false: { \fi: }
+ \msg_kernel_error:nn { regex } { trailing-backslash }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_nopar:cpn { regex_escape_~:w } { }
+\cs_new_nopar:cpx { regex_escape_/a:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^G }
+\cs_new_nopar:cpx { regex_escape_/t:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^I }
+\cs_new_nopar:cpx { regex_escape_/n:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^J }
+\cs_new_nopar:cpx { regex_escape_/f:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^L }
+\cs_new_nopar:cpx { regex_escape_/r:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^M }
+\cs_new_nopar:cpx { regex_escape_/e:w }
+ { \exp_not:N \regex_escape_raw:N \iow_char:N \^^[ }
+\cs_new:cpn { regex_escape_/x:w } \regex_escape_loop:N
+ {
+ \exp_after:wN \regex_escape_x_end:w
+ \int_value:w "0 \regex_escape_x_test:N
+ }
+\cs_new:Npn \regex_escape_x_end:w #1 ;
+ {
+ \int_compare:nNnTF {#1} < \c_two_hundred_fifty_six
+ {
+ \exp_last_unbraced:Nf \regex_escape_raw:N
+ { \str_output_byte:n {#1} }
+ }
+ { \regex_escape_x_large:n {#1} }
+ }
+\group_begin:
+ \char_set_catcode_other:n { 0 }
+ \cs_new:Npn \regex_escape_x_large:n #1
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \int_compare:nNnTF {#1} > \c_max_char_int
+ {
+ \msg_kernel_error:nnx { regex } { x-overflow } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N
+ }
+ {
+ \char_set_lccode:nn { \c_zero } {#1}
+ \tl_to_lowercase:n
+ {
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi:
+ \regex_escape_raw:N ^^@
+ \regex_escape_loop:N
+ }
+ }
+ }
+\group_end:
+\cs_new:Npn \regex_escape_x_test:N #1
+ {
+ \str_if_eq:xxTF {#1} { break } { ; }
+ {
+ \if_charcode:w \c_space_token #1
+ \exp_after:wN \regex_escape_x_test:N
+ \else:
+ \exp_after:wN \regex_escape_x_test_ii:N
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\cs_new:Npn \regex_escape_x_test_ii:N #1
+ {
+ \if_charcode:w \c_lbrace_str #1
+ \exp_after:wN \regex_escape_x_loop:N
+ \else:
+ \str_aux_hexadecimal_use:NTF #1
+ { \exp_after:wN \regex_escape_x_ii:N }
+ { ; \exp_after:wN \regex_escape_loop:N \exp_after:wN #1 }
+ \fi:
+ }
+\cs_new:Npn \regex_escape_x_ii:N #1
+ {
+ \str_if_eq:xxTF {#1} { break } { ; }
+ {
+ \str_aux_hexadecimal_use:NTF #1
+ { ; \regex_escape_loop:N }
+ { ; \regex_escape_loop:N #1 }
+ }
+ }
+\cs_new:Npn \regex_escape_x_loop:N #1
+ {
+ \str_aux_hexadecimal_use:NTF #1
+ { \regex_escape_x_loop:N }
+ {
+ \token_if_eq_charcode:NNTF \c_space_token #1
+ { \regex_escape_x_loop:N }
+ {
+ ;
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #1
+ { \regex_escape_loop:N }
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \msg_kernel_error:nn { regex } { x-missing-rbrace } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N #1
+ }
+ }
+ }
+ }
+\prg_new_conditional:Npnn \regex_char_if_special:N #1 { TF }
+ {
+ \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 `#1 < \c_sixty_five
+ \prg_return_true: \else: \prg_return_false: \fi:
+ \fi:
+ \else:
+ \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:
+ \if_num:w `#1 < \c_one_hundred_twenty_seven
+ \prg_return_true: \else: \prg_return_false: \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \regex_char_if_alphanumeric:N #1 { TF }
+ {
+ \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:
+ \if_num:w `#1 < \c_sixty_five
+ \prg_return_false: \else: \prg_return_true: \fi:
+ \fi:
+ \else:
+ \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:
+ \fi:
+ }
+\int_new:N \l_regex_group_level_int
+\int_new:N \l_regex_mode_int
+\int_new:N \l_regex_catcodes_int
+\int_new:N \l_regex_default_catcodes_int
+\bool_new:N \l_regex_catcodes_bool
+\int_const:Nn \c_regex_catcode_C_int { "1 }
+\int_const:Nn \c_regex_catcode_B_int { "4 }
+\int_const:Nn \c_regex_catcode_E_int { "10 }
+\int_const:Nn \c_regex_catcode_M_int { "40 }
+\int_const:Nn \c_regex_catcode_T_int { "100 }
+\int_const:Nn \c_regex_catcode_P_int { "1000 }
+\int_const:Nn \c_regex_catcode_U_int { "4000 }
+\int_const:Nn \c_regex_catcode_D_int { "10000 }
+\int_const:Nn \c_regex_catcode_S_int { "100000 }
+\int_const:Nn \c_regex_catcode_L_int { "400000 }
+\int_const:Nn \c_regex_catcode_O_int { "1000000 }
+\int_const:Nn \c_regex_catcode_A_int { "4000000 }
+\int_const:Nn \c_regex_all_catcodes_int { "5515155 }
+\cs_new_eq:NN \l_regex_internal_regex \c_regex_no_match_regex
+\seq_new:N \l_regex_show_prefix_seq
+\int_new:N \l_regex_show_lines_int
+\cs_new_protected:Npn \regex_get_digits:NTFw #1#2#3#4#5
+ {
+ \regex_if_raw_digit:NNTF #4 #5
+ { #1 = #5 \regex_get_digits_loop:nw {#2} }
+ { #3 #4 #5 }
+ }
+\cs_new:Npn \regex_get_digits_loop:nw #1#2#3
+ {
+ \regex_if_raw_digit:NNTF #2 #3
+ { #3 \regex_get_digits_loop:nw {#1} }
+ { \scan_stop: #1 #2 #3 }
+ }
+\prg_new_conditional:Npnn \regex_if_raw_digit:NN #1#2 { TF }
+ {
+ \if_meaning:w \regex_compile_raw:N #1
+ \if_int_compare:w \c_one < 1 #2 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_nopar:Npn \regex_if_in_class:TF
+ {
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ }
+\cs_new_nopar:Npn \regex_if_in_cs:TF
+ {
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_ii:nn
+ \else:
+ \if_int_compare:w \l_regex_mode_int < \c_zero
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
+ }
+\cs_new_nopar:Npn \regex_if_in_class_or_catcode:TF
+ {
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \if_int_compare:w \l_regex_mode_int > \c_zero
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
+ }
+\cs_new_nopar:Npn \regex_if_within_catcode:TF
+ {
+ \if_int_compare:w \l_regex_mode_int > \c_zero
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ }
+\cs_new_protected:Npn \regex_chk_c_allowed:T
+ {
+ \if_num:w \l_regex_mode_int = \c_zero
+ \exp_after:wN \use:n
+ \else:
+ \if_num:w \l_regex_mode_int = \c_three
+ \exp_after:wN \exp_after:wN \exp_after:wN \use:n
+ \else:
+ \msg_kernel_error:nn { regex } { c-bad-mode }
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_none:n
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_mode_quit_c:
+ {
+ \if_num:w \l_regex_mode_int = \c_two
+ \l_regex_mode_int = \c_zero
+ \else:
+ \if_num:w \l_regex_mode_int = \c_six
+ \l_regex_mode_int = \c_three
+ \fi:
+ \fi:
+ }
+\cs_new_protected_nopar:Npn \regex_compile:w
+ {
+ \tl_set_build_x:Nw \l_regex_internal_regex
+ \int_zero:N \l_regex_group_level_int
+ \int_set_eq:NN \l_regex_default_catcodes_int \c_regex_all_catcodes_int
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected_nopar:Npn \regex_compile_end:
+ {
+ \regex_if_in_class:TF
+ {
+ \msg_kernel_error:nn { regex } { missing-rbrack }
+ \use:c { regex_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_num:w \l_regex_group_level_int > \c_zero
+ \msg_kernel_error:nnx { regex } { missing-rparen }
+ { \int_use:N \l_regex_group_level_int }
+ \prg_replicate:nn
+ { \l_regex_group_level_int }
+ {
+ \tl_build_one:n
+ {
+ \if_false: { \fi: }
+ \if_false: { \fi: } { 1 } { 0 } \c_true_bool
+ }
+ \tl_build_end:
+ \tl_build_one:o \l_regex_internal_regex
+ }
+ \fi:
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
+ }
+\cs_new_protected:Npn \regex_compile:n #1
+ {
+ \regex_compile:w
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_set_eq:NN \l_regex_mode_int \c_zero
+ \regex_escape_use:nnnn
+ {
+ \regex_char_if_special:NTF ##1
+ \regex_compile_special:N \regex_compile_raw:N ##1
+ }
+ {
+ \regex_char_if_alphanumeric:NTF ##1
+ \regex_compile_escaped:N \regex_compile_raw:N ##1
+ }
+ { \regex_compile_raw:N ##1 }
+ { #1 }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ \int_compare:nNnT \l_regex_mode_int < \c_zero
+ {
+ \msg_kernel_error:nn { regex } { c-missing-rbrace }
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ \regex_compile_end:
+ }
+\cs_new_protected:Npn \regex_compile_special:N #1
+ {
+ \cs_if_exist_use:cF { regex_compile_#1: }
+ { \regex_compile_raw:N #1 }
+ }
+\cs_new_protected:Npn \regex_compile_escaped:N #1
+ {
+ \cs_if_exist_use:cF { regex_compile_/#1: }
+ { \regex_compile_raw:N #1 }
+ }
+\cs_new_protected:Npn \regex_compile_one:x #1
+ {
+ \regex_mode_quit_c:
+ \regex_if_in_class:TF { }
+ {
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ }
+ \tl_build_one:x
+ {
+ \if_num:w \l_regex_catcodes_int < \c_regex_all_catcodes_int
+ \regex_item_catcode:nT { \int_use:N \l_regex_catcodes_int }
+ { \exp_not:N \exp_not:n {#1} }
+ \else:
+ \exp_not:N \exp_not:n {#1}
+ \fi:
+ }
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_if_in_class:TF { } { \regex_compile_quantifier:w }
+ }
+\cs_new_protected:Npn \regex_compile_abort_tokens:n #1
+ {
+ \use:x
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \regex_compile_raw:N
+ }
+ }
+\cs_generate_variant:Nn \regex_compile_abort_tokens:n { x }
+\cs_new_protected:Npn \regex_compile_quantifier:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
+ {
+ \cs_if_exist_use:cF { regex_compile_quantifier_#2:w }
+ { \regex_compile_quantifier_none: #1 #2 }
+ }
+ { \regex_compile_quantifier_none: #1 #2 }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_none:
+ { \tl_build_one:n { \if_false: { \fi: } { 1 } { 0 } \c_false_bool } }
+\cs_new_protected:Npn \regex_compile_quantifier_abort:xNN #1#2#3
+ {
+ \regex_compile_quantifier_none:
+ \msg_kernel_warning:nnxx { regex } { invalid-quantifier } {#1} {#3}
+ \regex_compile_abort_tokens:x {#1}
+ #2 #3
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_lazyness:nnNN #1#2#3#4
+ {
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ? }
+ { \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_true_bool } }
+ {
+ \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_false_bool }
+ #3 #4
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_?:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { 1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_*:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { -1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_+:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 1 } { -1 } }
+\cs_new_protected:cpn { regex_compile_quantifier_ \c_lbrace_str :w }
+ {
+ \regex_get_digits:NTFw \l_regex_internal_a_int
+ { \regex_compile_quantifier_braced_i:w }
+ { \regex_compile_quantifier_abort:xNN { \c_lbrace_str } }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_i:w #1#2
+ {
+ \prg_case_str:xxn { #1 #2 }
+ {
+ { \regex_compile_special:N \c_rbrace_str }
+ {
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { 0 }
+ }
+ { \regex_compile_special:N , }
+ {
+ \regex_get_digits:NTFw \l_regex_internal_b_int
+ { \regex_compile_quantifier_braced_iii:w }
+ { \regex_compile_quantifier_braced_ii:w }
+ }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_ii:w #1#2
+ {
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { -1 }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int , }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_iii:w #1#2
+ {
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \if_num:w \l_regex_internal_a_int > \l_regex_internal_b_int
+ \msg_kernel_error:nnxx { regex } { backwards-quantifier }
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ \int_zero:N \l_regex_internal_b_int
+ \else:
+ \int_sub:Nn \l_regex_internal_b_int \l_regex_internal_a_int
+ \fi:
+ \exp_args:Noo \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ {
+ \c_lbrace_str
+ \int_use:N \l_regex_internal_a_int ,
+ \int_use:N \l_regex_internal_b_int
+ }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_raw_error:N #1
+ {
+ \msg_kernel_error:nnx { regex } { bad-escape } {#1}
+ \regex_compile_raw:N #1
+ }
+\cs_new_protected:Npn \regex_compile_raw:N #1#2#3
+ {
+ \regex_if_in_class:TF
+ {
+ \str_if_eq:nnTF {#2#3} { \regex_compile_special:N - }
+ { \regex_compile_range:Nw #1 }
+ {
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ #2 #3
+ }
+ }
+ {
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ #2 #3
+ }
+ }
+\prg_new_protected_conditional:Npnn \regex_if_end_range:NN #1#2 { TF }
+ {
+ \if_meaning:w \regex_compile_raw:N #1
+ \prg_return_true:
+ \else:
+ \if_meaning:w \regex_compile_special:N #1
+ \if_charcode:w ] #2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_compile_range:Nw #1#2#3
+ {
+ \regex_if_end_range:NNTF #2 #3
+ {
+ \if_num:w `#1 > `#3 \exp_stop_f:
+ \msg_kernel_error:nnxx { regex } { range-backwards } {#1} {#3}
+ \else:
+ \tl_build_one:x
+ {
+ \if_num:w `#1 = `#3 \exp_stop_f:
+ \regex_item_equal:n
+ \else:
+ \regex_item_range:nn { \int_value:w `#1 ~ }
+ \fi:
+ { \int_value:w `#3 ~ }
+ }
+ \fi:
+ }
+ {
+ \msg_kernel_warning:nnxx { regex } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_one:x
+ {
+ \regex_item_equal:n { \int_value:w `#1 ~ }
+ \regex_item_equal:n { \int_value:w `- ~ }
+ }
+ #2#3
+ }
+ }
+\cs_new_protected_nopar:cpx { regex_compile_.: }
+ {
+ \exp_not:N \regex_if_in_class:TF
+ { \regex_compile_raw:N . }
+ { \regex_compile_one:x \exp_not:c { regex_prop_.: } }
+ }
+\cs_new_protected_nopar:cpn { regex_prop_.: }
+ {
+ \if_num:w \l_regex_current_char_int > - \c_two
+ \exp_after:wN \regex_break_true:w
+ \fi:
+ }
+\cs_set_protected:Npn \regex_tmp:w #1#2
+ {
+ \cs_new_protected_nopar:cpx { regex_compile_/#1: }
+ { \regex_compile_one:x \exp_not:c { regex_prop_#1: } }
+ \cs_new_protected_nopar:cpx { regex_compile_/#2: }
+ {
+ \regex_compile_one:x
+ { \regex_item_reverse:n \exp_not:c { regex_prop_#1: } }
+ }
+ }
+\regex_tmp:w d D
+\regex_tmp:w h H
+\regex_tmp:w s S
+\regex_tmp:w v V
+\regex_tmp:w w W
+\cs_new_protected_nopar:cpn { regex_compile_/N: }
+ { \regex_compile_one:x \regex_prop_N: }
+\cs_new_protected:Npn \regex_compile_anchor:NF #1#2
+ {
+ \regex_if_in_class_or_catcode:TF {#2}
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_anchor:N #1 } }
+ }
+ }
+\cs_set_protected:Npn \regex_tmp:w #1#2
+ {
+ \cs_new_protected_nopar:cpn { regex_compile_/#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw_error:N #1 } }
+ }
+\regex_tmp:w A \l_regex_min_pos_int
+\regex_tmp:w G \l_regex_start_pos_int
+\regex_tmp:w Z \l_regex_max_pos_int
+\regex_tmp:w z \l_regex_max_pos_int
+\cs_set_protected:Npn \regex_tmp:w #1#2
+ {
+ \cs_new_protected_nopar:cpn { regex_compile_#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw:N #1 } }
+ }
+\exp_args:Nx \regex_tmp:w { \iow_char:N \^ } \l_regex_min_pos_int
+\exp_args:Nx \regex_tmp:w { \iow_char:N \$ } \l_regex_max_pos_int
+\cs_new_protected_nopar:cpn { regex_compile_/b: }
+ {
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N b }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_b_test: } }
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_/B: }
+ {
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N B }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_false_bool { \regex_b_test: } }
+ }
+ }
+\cs_new_protected:cpn { regex_compile_]: }
+ {
+ \regex_if_in_class:TF
+ {
+ \if_num:w \l_regex_mode_int > \c_sixteen
+ \tl_build_one:n { \if_false: { \fi: } }
+ \fi:
+ \tex_advance:D \l_regex_mode_int - \c_fifteen
+ \tex_divide:D \l_regex_mode_int \c_thirteen
+ \if_int_odd:w \l_regex_mode_int \else:
+ \exp_after:wN \regex_compile_quantifier:w
+ \fi:
+ }
+ { \regex_compile_raw:N ] }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_[: }
+ {
+ \regex_if_in_class:TF
+ { \regex_compile_class_posix_test:w }
+ {
+ \regex_if_within_catcode:TF
+ {
+ \exp_after:wN \regex_compile_class_catcode:w
+ \int_use:N \l_regex_catcodes_int ;
+ }
+ { \regex_compile_class_normal:w }
+ }
+ }
+\cs_new_protected_nopar:Npn \regex_compile_class_normal:w
+ {
+ \regex_compile_class:TFNN
+ { \regex_class:NnnnN \c_true_bool }
+ { \regex_class:NnnnN \c_false_bool }
+ }
+\cs_new_protected:Npn \regex_compile_class_catcode:w #1;
+ {
+ \if_int_compare:w \l_regex_mode_int = \c_two
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ \fi:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_compile_class:TFNN
+ { \regex_item_catcode:nT {#1} }
+ { \regex_item_catcode_reverse:nT {#1} }
+ }
+\cs_new_protected:Npn \regex_compile_class:TFNN #1#2#3#4
+ {
+ \l_regex_mode_int = \int_value:w \l_regex_mode_int 3 \exp_stop_f:
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ^ }
+ {
+ \tl_build_one:n { #2 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN
+ }
+ {
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN #3 #4
+ }
+ }
+\cs_new_protected:Npn \regex_compile_class_ii:NN #1#2
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_raw:N #2 }
+ { #1 #2 }
+ }
+\cs_new_protected:Npn \regex_compile_class_posix_test:w #1#2
+ {
+ \token_if_eq_meaning:NNT \regex_compile_special:N #1
+ {
+ \prg_case_str:nnn { #2 }
+ {
+ : { \regex_compile_class_posix:NNNNw }
+ = { \msg_kernel_warning:nnx { regex } { posix-unsupported } { = } }
+ . { \msg_kernel_warning:nnx { regex } { posix-unsupported } { . } }
+ }
+ { }
+ }
+ \regex_compile_raw:N [ #1 #2
+ }
+\cs_new_protected:Npn \regex_compile_class_posix:NNNNw #1#2#3#4#5#6
+ {
+ \str_if_eq:nnTF { #5 #6 } { \regex_compile_special:N ^ }
+ {
+ \bool_set_false:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w
+ }
+ {
+ \bool_set_true:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w #5 #6
+ }
+ }
+\cs_new:Npn \regex_compile_class_posix_loop:w #1#2
+ {
+ \token_if_eq_meaning:NNTF \regex_compile_raw:N #1
+ { #2 \regex_compile_class_posix_loop:w }
+ { \if_false: { \fi: } \regex_compile_class_posix_end:w #1 #2 }
+ }
+\cs_new_protected:Npn \regex_compile_class_posix_end:w #1#2#3#4
+ {
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_special:N : \regex_compile_special:N ] }
+ {
+ \cs_if_exist:cTF { regex_posix_ \l_regex_internal_a_tl : }
+ {
+ \regex_compile_one:x
+ {
+ \bool_if:NF \l_regex_internal_bool \regex_item_reverse:n
+ \exp_not:c { regex_posix_ \l_regex_internal_a_tl : }
+ }
+ }
+ {
+ \msg_kernel_warning:nnx { regex } { posix-unknown }
+ { \l_regex_internal_a_tl }
+ \regex_compile_abort_tokens:x
+ {
+ [: \bool_if:NF \l_regex_internal_bool { ^ }
+ \l_regex_internal_a_tl :]
+ }
+ }
+ }
+ {
+ \msg_kernel_error:nnxx { regex } { posix-missing-close }
+ { [: \l_regex_internal_a_tl } { #2 #4 }
+ \regex_compile_abort_tokens:x { [: \l_regex_internal_a_tl }
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected:Npn \regex_compile_group_begin:N #1
+ {
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_mode_quit_c:
+ \tl_set_build:Nw \l_regex_internal_regex
+ \int_set_eq:NN \l_regex_default_catcodes_int \l_regex_catcodes_int
+ \int_incr:N \l_regex_group_level_int
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \regex_compile_group_end:
+ {
+ \if_num:w \l_regex_group_level_int > \c_zero
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \tl_build_one:o \l_regex_internal_regex
+ \exp_after:wN \regex_compile_quantifier:w
+ \else:
+ \msg_kernel_warning:nn { regex } { extra-rparen }
+ \exp_after:wN \regex_compile_raw:N \exp_after:wN )
+ \fi:
+ }
+\cs_new_protected_nopar:cpn { regex_compile_(: }
+ {
+ \regex_if_in_class:TF { \regex_compile_raw:N ( }
+ { \regex_compile_lparen:w }
+ }
+\cs_new_protected:Npn \regex_compile_lparen:w #1#2#3#4
+ {
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ? }
+ {
+ \cs_if_exist_use:cF
+ { regex_compile_special_group_\token_to_str:N #4 :w }
+ {
+ \msg_kernel_warning:nnx { regex } { special-group-unknown }
+ { (? \token_to_str:N #4 }
+ \regex_compile_group_begin:N \regex_group:nnnN
+ \regex_compile_raw:N ? #3 #4
+ }
+ }
+ {
+ \regex_compile_group_begin:N \regex_group:nnnN
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_|: }
+ {
+ \regex_if_in_class:TF { \regex_compile_raw:N | }
+ {
+ \tl_build_one:n
+ { \if_false: { \fi: } \regex_branch:n { \if_false: } \fi: }
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_): }
+ {
+ \regex_if_in_class:TF { \regex_compile_raw:N ) }
+ { \regex_compile_group_end: }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_::w }
+ { \regex_compile_group_begin:N \regex_group_no_capture:nnnN }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_|:w }
+ { \regex_compile_group_begin:N \regex_group_resetting:nnnN }
+\cs_new_protected:Npn \regex_compile_special_group_i:w #1#2
+ {
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ) }
+ {
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseless_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseless_range:nn }
+ }
+ {
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?i #2 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N i
+ #1 #2
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_-:w } #1#2#3#4
+ {
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_raw:N i \regex_compile_special:N ) }
+ {
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
+ }
+ {
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?-#2#4 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N -
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected:cpn { regex_compile_/c: }
+ { \regex_chk_c_allowed:T { \regex_compile_c_test:NN } }
+\cs_new_protected:Npn \regex_compile_c_test:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
+ {
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \int_set_eq:Nc \l_regex_catcodes_int { c_regex_catcode_#2_int }
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ }
+ }
+ { \cs_if_exist_use:cF { regex_compile_c_#2:w } }
+ {
+ \msg_kernel_error:nnx { regex } { c-missing-category } {#2}
+ #1 #2
+ }
+ }
+\cs_new_protected:cpn { regex_compile_c_[:w } #1#2
+ {
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ \int_zero:N \l_regex_catcodes_int
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ^ }
+ {
+ \bool_set_false:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
+ }
+ {
+ \bool_set_true:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_c_lbrack_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
+ {
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \exp_args:Nc \regex_compile_c_lbrack_add:N
+ { c_regex_catcode_#2_int }
+ \regex_compile_c_lbrack_loop:NN
+ }
+ }
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_c_lbrack_end: }
+ }
+ {
+ \msg_kernel_error:nnx { regex } { c-missing-rbrack } {#2}
+ \regex_compile_c_lbrack_end:
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_c_lbrack_add:N #1
+ {
+ \if_int_odd:w \int_eval:w \l_regex_catcodes_int / #1 \int_eval_end:
+ \else:
+ \tex_advance:D \l_regex_catcodes_int #1
+ \fi:
+ }
+\cs_new_protected_nopar:Npn \regex_compile_c_lbrack_end:
+ {
+ \if_meaning:w \c_false_bool \l_regex_catcodes_bool
+ \int_set:Nn \l_regex_catcodes_int
+ { \c_regex_all_catcodes_int - \l_regex_catcodes_int }
+ \fi:
+ }
+\cs_new_protected_nopar:cpn { regex_compile_c_ \c_lbrace_str :w }
+ {
+ \regex_compile:w
+ \regex_disable_submatches:
+ \l_regex_mode_int
+ = - \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ }
+\cs_new_protected:cpn { regex_compile_ \c_rbrace_str : }
+ {
+ \regex_if_in_cs:TF
+ {
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
+ }
+ { \exp_after:wN \regex_compile_raw:N \c_rbrace_str }
+ }
+\cs_new_protected:cpn { regex_compile_/u: } #1#2
+ {
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N u #1 #2 }
+ {
+ \str_if_eq:xxTF {#1#2} { \regex_compile_special:N \c_lbrace_str }
+ {
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_u_loop:NN
+ }
+ {
+ \msg_kernel_error:nn { regex } { u-missing-lbrace }
+ \regex_compile_raw:N u #1 #2
+ }
+ }
+ }
+\cs_new:Npn \regex_compile_u_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
+ { #2 \regex_compile_u_loop:NN }
+ {
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
+ {
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #2
+ { \if_false: { \fi: } \regex_compile_u_end: }
+ { #2 \regex_compile_u_loop:NN }
+ }
+ {
+ \if_false: { \fi: }
+ \msg_kernel_error:nnx { regex } { u-missing-rbrace } {#2}
+ \regex_compile_u_end:
+ #1 #2
+ }
+ }
+ }
+\cs_new_protected:Npn \regex_compile_u_end:
+ {
+ \tl_set:Nv \l_regex_internal_a_tl { \l_regex_internal_a_tl }
+ \if_num:w \l_regex_mode_int = \c_zero
+ \regex_compile_u_not_cs:
+ \else:
+ \regex_compile_u_in_cs:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_compile_u_in_cs:
+ {
+ \exp_args:NNo \str_gset_other:Nn \g_regex_internal_tl
+ { \l_regex_internal_a_tl }
+ \tl_build_one:x
+ {
+ \tl_map_function:NN \g_regex_internal_tl
+ \regex_compile_u_in_cs_aux:n
+ }
+ }
+\cs_new:Npn \regex_compile_u_in_cs_aux:n #1
+ {
+ \regex_class:NnnnN \c_true_bool
+ { \regex_item_caseful_equal:n { \int_value:w `#1 } }
+ { 1 } { 0 } \c_false_bool
+ }
+\cs_new_protected:Npn \regex_compile_u_not_cs:
+ {
+ \exp_args:No \tl_analysis_map_inline:nn { \l_regex_internal_a_tl }
+ {
+ \tl_build_one:n
+ {
+ \regex_class:NnnnN \c_true_bool
+ {
+ \if_num:w "##2 = \c_zero
+ \regex_item_exact_cs:c { \exp_after:wN \cs_to_str:N ##1 }
+ \else:
+ \regex_item_exact:nn { \int_value:w "##2 } { ##3 }
+ \fi:
+ }
+ { 1 } { 0 } \c_false_bool
+ }
+ }
+ }
+\cs_new_protected_nopar:cpn { regex_compile_/K: }
+ {
+ \int_compare:nNnTF \l_regex_mode_int = \c_zero
+ { \tl_build_one:n { \regex_command_K: } }
+ { \regex_compile_raw_error:N K }
+ }
+\cs_new_protected:Npn \regex_show_aux:Nx #1#2
+ {
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \cs_set_protected_nopar:Npn \regex_branch:n
+ {
+ \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \regex_show_one:n { +-branch }
+ \seq_put_right:No \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \use:n
+ }
+ \cs_set_protected_nopar:Npn \regex_group:nnnN
+ { \regex_show_group_aux:nnnnN { } }
+ \cs_set_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_show_group_aux:nnnnN { ~(no~capture) } }
+ \cs_set_protected_nopar:Npn \regex_group_resetting:nnnN
+ { \regex_show_group_aux:nnnnN { ~(resetting) } }
+ \cs_set_eq:NN \regex_class:NnnnN \regex_show_class:NnnnN
+ \cs_set_protected_nopar:Npn \regex_command_K:
+ { \regex_show_one:n { reset~match~start~(\iow_char:N\\K) } }
+ \cs_set_protected:Npn \regex_assertion:Nn ##1##2
+ { \regex_show_one:n { \bool_if:NF ##1 { negative~ } assertion:~##2 } }
+ \cs_set_nopar:Npn \regex_b_test: { word~boundary }
+ \cs_set_eq:NN \regex_anchor:N \regex_show_anchor_to_str:N
+ \cs_set_protected:Npn \regex_item_caseful_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1} } }
+ \cs_set_protected:Npn \regex_item_caseful_range:nn ##1##2
+ { \regex_show_one:n { range~[\int_eval:n{##1}, \int_eval:n{##2}] } }
+ \cs_set_protected:Npn \regex_item_caseless_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \regex_item_caseless_range:nn ##1##2
+ {
+ \regex_show_one:n
+ { Range~[\int_eval:n{##1}, \int_eval:n{##2}]~(caseless) }
+ }
+ \cs_set_protected:Npn \regex_item_catcode:nT
+ { \regex_show_item_catcode:NnT \c_true_bool }
+ \cs_set_protected:Npn \regex_item_catcode_reverse:nT
+ { \regex_show_item_catcode:NnT \c_false_bool }
+ \cs_set_protected:Npn \regex_item_reverse:n
+ { \regex_show_scope:nn { Reversed~match } }
+ \cs_set_protected:Npn \regex_item_exact:nn ##1##2
+ { \regex_show_one:n { char~##2,~catcode~##1 } }
+ \cs_set_protected:Npn \regex_item_exact_cs:c ##1
+ { \regex_show_one:n { control~sequence~\iow_char:N\\##1 } }
+ \cs_set_protected:Npn \regex_item_cs:n
+ { \regex_show_scope:nn { control~sequence } }
+ \cs_set:cpn { regex_prop_.: } { \regex_show_one:n { any~token } }
+ \seq_clear:N \l_regex_show_prefix_seq
+ \regex_show_push:n { ~ }
+ #1
+ \tl_build_end:
+ \msg_aux_show:x { > Compiled~regex~#2: \l_regex_internal_a_tl }
+ }
+\cs_new_protected:Npn \regex_show_one:n #1
+ {
+ \int_incr:N \l_regex_show_lines_int
+ \tl_build_one:x
+ { \iow_newline: \seq_use:N \l_regex_show_prefix_seq #1 }
+ }
+\cs_new_protected:Npn \regex_show_push:n #1
+ { \seq_put_right:Nx \l_regex_show_prefix_seq { #1 ~ } }
+\cs_new_protected:Npn \regex_show_pop:
+ { \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl }
+\cs_new_protected:Npn \regex_show_scope:nn #1#2
+ {
+ \regex_show_one:n {#1}
+ \regex_show_push:n { ~ }
+ #2
+ \regex_show_pop:
+ }
+\cs_new_protected:Npn \regex_show_group_aux:nnnnN #1#2#3#4#5
+ {
+ \regex_show_one:n { ,-group~begin #1 }
+ \regex_show_push:n { | }
+ \use_ii:nn #2
+ \regex_show_pop:
+ \regex_show_one:n
+ { `-group~end \regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+\cs_set:Npn \regex_show_class:NnnnN #1#2#3#4#5
+ {
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_show_lines_int
+ \regex_show_push:n {~}
+ #2
+ \exp_last_unbraced:Nf
+ \prg_case_int:nnn { \l_regex_show_lines_int }
+ {
+ {0}
+ {
+ \tl_build_end:
+ \regex_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
+ }
+ {1}
+ {
+ \tl_build_end:
+ \bool_if:NTF #1
+ {
+ #2
+ \tl_build_one:n { \regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+ {
+ \regex_show_one:n
+ { Don't~match~\regex_msg_repeated:nnN {#3} {#4} #5 }
+ \tl_build_one:o \l_regex_internal_a_tl
+ }
+ }
+ }
+ {
+ \tl_build_end:
+ \regex_show_one:n
+ {
+ \bool_if:NTF #1 { M } { Don't~m } atch
+ \regex_msg_repeated:nnN {#3} {#4} #5
+ }
+ \tl_build_one:o \l_regex_internal_a_tl
+ }
+ }
+\cs_new:Npn \regex_show_anchor_to_str:N #1
+ {
+ anchor~at~
+ \prg_case_str:nnn { #1 }
+ {
+ { \l_regex_min_pos_int } { start~(\iow_char:N\\A) }
+ { \l_regex_start_pos_int } { start~of~match~(\iow_char:N\\G) }
+ { \l_regex_max_pos_int } { end~(\iow_char:N\\Z) }
+ }
+ { <error:~'#1'~not~recognized> }
+ }
+\cs_new_protected:Npn \regex_show_item_catcode:NnT #1#2
+ {
+ \seq_set_split:Nnn \l_regex_internal_seq { } { CBEMTPUDSLOA }
+ \seq_set_filter:NNn \l_regex_internal_seq \l_regex_internal_seq
+ { \int_if_odd_p:n { #2 / \int_use:c { c_regex_catcode_##1_int } } }
+ \regex_show_scope:nn
+ {
+ categories~\seq_use:N \l_regex_internal_seq, ~
+ \bool_if:NF #1 { negative~ } class
+ }
+ }
+\int_new:N \l_regex_min_state_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
+\int_new:N \l_regex_capturing_group_int
+\cs_new_protected:Npn \regex_build:n #1
+ {
+ \regex_compile:n {#1}
+ \regex_build:N \l_regex_internal_regex
+ }
+\cs_new_protected:Npn \regex_build:N #1
+ {
+ \trace_push:nnn { regex } { 1 } { regex_build }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_zero:N \l_regex_capturing_group_int
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
+ \regex_build_new_state:
+ \regex_toks_put_right:Nn \l_regex_left_state_int
+ { \regex_action_start_wildcard: }
+ \regex_group:nnnN {#1} { 1 } { 0 } \c_false_bool
+ \regex_toks_put_right:Nn \l_regex_right_state_int
+ { \regex_action_success: }
+ \regex_trace_states:n { 2 }
+ \trace_pop:nnn { regex } { 1 } { regex_build }
+ }
+\cs_new_protected:Npn \regex_build_for_cs:n #1
+ {
+ \trace_push:nnn { regex } { 1 } { regex_build_for_cs }
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
+ \regex_build_new_state:
+ \regex_push_lr_states:
+ #1
+ \regex_pop_lr_states:
+ \regex_toks_put_right:Nn \l_regex_right_state_int
+ {
+ \if_num:w \l_regex_current_pos_int = \l_regex_max_pos_int
+ \exp_after:wN \regex_action_success:
+ \fi:
+ }
+ \regex_trace_states:n { 2 }
+ \trace_pop:nnn { regex } { 1 } { regex_build_for_cs }
+ }
+\cs_new_protected_nopar:Npn \regex_push_lr_states:
+ {
+ \seq_push:No \l_regex_left_state_seq
+ { \int_use:N \l_regex_left_state_int }
+ \seq_push:No \l_regex_right_state_seq
+ { \int_use:N \l_regex_right_state_int }
+ }
+\cs_new_protected_nopar:Npn \regex_pop_lr_states:
+ {
+ \seq_pop:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \seq_pop:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_right_state_int \l_regex_internal_a_tl
+ }
+\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2
+ {
+ \cs_set_nopar:Npx \regex_tmp:w { #2 }
+ \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 }
+ }
+\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2
+ {
+ \cs_set_nopar:Npx \regex_tmp:w {#2}
+ \tex_toks:D #1 \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w }
+ }
+\cs_new_protected:Npn \regex_toks_put_right:Nn #1#2
+ { \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D #1 #2 } }
+\cs_new_protected:Npn \regex_build_transition_left:NNN #1#2#3
+ { \regex_toks_put_left:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected:Npn \regex_build_transition_right:nNn #1#2#3
+ { \regex_toks_put_right:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected_nopar:Npn \regex_build_new_state:
+ {
+ \trace:nnx { regex } { 2 }
+ {
+ regex~new~state~
+ L=\int_use:N \l_regex_left_state_int ~ -> ~
+ R=\int_use:N \l_regex_right_state_int ~ -> ~
+ M=\int_use:N \l_regex_max_state_int ~ -> ~
+ \int_eval:n { \l_regex_max_state_int + \c_one }
+ }
+ \tex_toks:D \l_regex_max_state_int { }
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
+ \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int
+ \int_incr:N \l_regex_max_state_int
+ }
+\cs_new_protected:Npn \regex_build_transitions_lazyness:NNNNN #1#2#3#4#5
+ {
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
+ {
+ \if_meaning:w \c_true_bool #1
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ \else:
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \regex_class:NnnnN #1#2#3#4#5
+ {
+ \cs_set_nopar:Npx \regex_tests_action_cost:n ##1
+ {
+ \exp_not:n { \exp_not:n {#2} }
+ \bool_if:NTF #1
+ { \regex_break_point:TF { \regex_action_cost:n {##1} } { } }
+ { \regex_break_point:TF { } { \regex_action_cost:n {##1} } }
+ }
+ \if_case:w - #4 \exp_stop_f:
+ \regex_class_repeat:n {#3}
+ \or: \regex_class_repeat:nN {#3} #5
+ \else: \regex_class_repeat:nnN {#3} {#4} #5
+ \fi:
+ }
+\cs_new:Npn \regex_tests_action_cost:n { \regex_action_cost:n }
+\cs_new_protected:Npn \regex_class_repeat:n #1
+ {
+ \prg_replicate:nn {#1}
+ {
+ \regex_build_new_state:
+ \regex_build_transition_right:nNn \regex_tests_action_cost:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ }
+ }
+\cs_new_protected:Npn \regex_class_repeat:nN #1#2
+ {
+ \if_num:w #1 = \c_zero
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_tests_action_cost:n \l_regex_left_state_int
+ \else:
+ \regex_class_repeat:n {#1}
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_action_free:n \l_regex_internal_a_int
+ \fi:
+ }
+\cs_new_protected:Npn \regex_class_repeat:nnN #1#2#3
+ {
+ \regex_class_repeat:n {#1}
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_max_state_int + #2 - \c_one }
+ \prg_replicate:nn { #2 }
+ {
+ \regex_build_transitions_lazyness:NNNNN #3
+ \regex_action_free:n \l_regex_internal_a_int
+ \regex_tests_action_cost:n \l_regex_right_state_int
+ }
+ }
+\cs_new_protected:Npn \regex_group_aux:nnnnN #1#2#3#4#5
+ {
+ \trace_push:nnn { regex } { 1 } { regex_group }
+ \if_num:w #3 = \c_zero
+ \regex_build_new_state:
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ \fi:
+ \regex_build_new_state:
+ \regex_push_lr_states:
+ #2
+ \regex_pop_lr_states:
+ \if_case:w - #4 \exp_stop_f:
+ \regex_group_repeat:nn {#1} {#3}
+ \or: \regex_group_repeat:nnN {#1} {#3} #5
+ \else: \regex_group_repeat:nnnN {#1} {#3} {#4} #5
+ \fi:
+ \trace_pop:nnn { regex } { 1 } { regex_group }
+ }
+\cs_new_protected:Npn \regex_group:nnnN #1
+ {
+ \exp_args:No \regex_group_aux:nnnnN
+ { \int_use:N \l_regex_capturing_group_int }
+ {
+ \int_incr:N \l_regex_capturing_group_int
+ #1
+ }
+ }
+\cs_new_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_group_aux:nnnnN { -1 } }
+\cs_new_protected:Npn \regex_group_resetting:nnnN #1
+ {
+ \regex_group_aux:nnnnN { -1 }
+ {
+ \exp_args:Noo \regex_group_resetting_loop:nnNn
+ { \int_use:N \l_regex_capturing_group_int }
+ { \int_use:N \l_regex_capturing_group_int }
+ #1
+ { ?? \prg_map_break:n } { }
+ \prg_break_point:n { }
+ }
+ }
+\cs_new_protected:Npn \regex_group_resetting_loop:nnNn #1#2#3#4
+ {
+ \use_none:nn #3 { \int_set:Nn \l_regex_capturing_group_int {#1} }
+ \int_set:Nn \l_regex_capturing_group_int {#2}
+ #3 {#4}
+ \exp_args:Nf \regex_group_resetting_loop:nnNn
+ { \int_max:nn {#1} { \l_regex_capturing_group_int } }
+ {#2}
+ }
+\cs_new_protected:Npn \regex_branch:n #1
+ {
+ \trace_push:nnn { regex } { 1 } { regex_branch }
+ \regex_build_new_state:
+ \seq_get:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ #1
+ \seq_get:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_tl
+ \trace_pop:nnn { regex } { 1 } { regex_branch }
+ }
+\cs_new_protected:Npn \regex_group_repeat:nn #1#2
+ {
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_max_state_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_new_state:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \regex_build_new_state:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_group_submatches:nNN #1#2#3
+ {
+ \if_num:w #1 > \c_minus_one
+ \regex_toks_put_left:Nx #2 { \regex_action_submatch:n { #1 < } }
+ \regex_toks_put_left:Nx #3 { \regex_action_submatch:n { #1 > } }
+ \fi:
+ }
+\cs_new_protected:Npn \regex_group_repeat_aux:n #1
+ {
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \int_set_eq:NN \l_regex_internal_b_int \l_regex_max_state_int
+ \if_num:w \int_eval:w #1 > \c_one
+ \int_set:Nn \l_regex_internal_c_int
+ {
+ ( #1 - \c_one )
+ * ( \l_regex_internal_b_int - \l_regex_internal_a_int )
+ }
+ \tex_advance:D \l_regex_right_state_int \l_regex_internal_c_int
+ \tex_advance:D \l_regex_max_state_int \l_regex_internal_c_int
+ \prg_replicate:nn \l_regex_internal_c_int
+ {
+ \tex_toks:D \l_regex_internal_b_int
+ = \tex_toks:D \l_regex_internal_a_int
+ \tex_advance:D \l_regex_internal_a_int \c_one
+ \tex_advance:D \l_regex_internal_b_int \c_one
+ }
+ \fi:
+ }
+\cs_new_protected:Npn \regex_group_repeat:nnN #1#2#3
+ {
+ \if_num:w #2 = \c_zero
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \regex_build_new_state:
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \else:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \fi:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \else:
+ \regex_build_transition_left:NNN \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \fi:
+ \regex_build_new_state:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_group_repeat:nnnN #1#2#3#4
+ {
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \regex_group_repeat_aux:n { #2 + #3 }
+ \if_meaning:w \c_true_bool #4
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_max_state_int
+ \prg_replicate:nn { #3 }
+ {
+ \int_sub:Nn \l_regex_left_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_left_state_int \l_regex_max_state_int
+ }
+ \else:
+ \prg_replicate:nn { #3 - \c_one }
+ {
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ }
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_right_state_int
+ { \l_regex_left_state_int - \c_one }
+ \else:
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \fi:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ \fi:
+ \regex_build_new_state:
+ }
+\cs_new_protected:Npn \regex_assertion:Nn #1#2
+ {
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
+ {
+ \exp_not:n {#2}
+ \regex_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \regex_action_free:n
+ {
+ \int_eval:n
+ { \l_regex_right_state_int - \l_regex_left_state_int }
+ }
+ }
+ \bool_if:NT #1 { { } }
+ }
+ }
+\cs_new_protected:Npn \regex_anchor:N #1
+ {
+ \if_num:w #1 = \l_regex_current_pos_int
+ \exp_after:wN \regex_break_true:w
+ \fi:
+ }
+\cs_new_protected_nopar:Npn \regex_b_test:
+ {
+ \group_begin:
+ \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int
+ \regex_prop_w:
+ \regex_break_point:TF
+ { \group_end: \regex_item_reverse:n \regex_prop_w: }
+ { \group_end: \regex_prop_w: }
+ }
+\cs_new_protected_nopar:Npn \regex_command_K:
+ {
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
+ {
+ \regex_action_submatch:n { 0< }
+ \bool_set_true:N \l_regex_fresh_thread_bool
+ \regex_action_free:n
+ { \int_eval:n { \l_regex_right_state_int - \l_regex_left_state_int } }
+ \bool_set_false:N \l_regex_fresh_thread_bool
+ }
+ }
+\int_new:N \l_regex_min_pos_int
+\int_new:N \l_regex_max_pos_int
+\int_new:N \l_regex_current_pos_int
+\int_new:N \l_regex_start_pos_int
+\int_new:N \l_regex_success_pos_int
+\int_new:N \l_regex_current_char_int
+\int_new:N \l_regex_current_catcode_int
+\int_new:N \l_regex_last_char_int
+\int_new:N \l_regex_case_changed_char_int
+\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_step_int
+\int_new:N \l_regex_min_active_int
+\int_new:N \l_regex_max_active_int
+\tl_new:N \l_regex_every_match_tl
+\bool_new:N \l_regex_fresh_thread_bool
+\bool_new:N \l_regex_empty_success_bool
+\cs_new_eq:NN \regex_if_two_empty_matches:F \use:n
+\bool_new:N \g_regex_success_bool
+\bool_new:N \l_regex_saved_success_bool
+\bool_new:N \l_regex_match_success_bool
+\cs_new_protected:Npn \regex_match:n #1
+ {
+ \trace_push:nnx { regex } { 1 } { regex_match }
+ \trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ \int_zero:N \l_regex_balance_int
+ \int_set:Nn \l_regex_current_pos_int { \c_two * \l_regex_max_state_int }
+ \regex_query_set:nnn { } { -1 } { -2 }
+ \int_set_eq:NN \l_regex_min_pos_int \l_regex_current_pos_int
+ \tl_analysis_map_inline:nn {#1}
+ { \regex_query_set:nnn {##1} {"##2} {##3} }
+ \int_set_eq:NN \l_regex_max_pos_int \l_regex_current_pos_int
+ \regex_query_set:nnn { } { -1 } { -2 }
+ \trace:nnx { regex } { 1 } { initializing }
+ \bool_gset_false:N \g_regex_success_bool
+ \prg_stepwise_inline:nnnn
+ \l_regex_min_state_int \c_one { \l_regex_max_state_int - \c_one }
+ { \tex_dimen:D ##1 \c_one sp \scan_stop: }
+ \int_set_eq:NN \l_regex_min_active_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_step_int \c_zero
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_min_pos_int
+ \int_set:Nn \l_regex_submatch_int
+ { \c_two * \l_regex_max_state_int }
+ \bool_set_false:N \l_regex_empty_success_bool
+ \regex_match_once:
+ \trace_pop:nnx { regex } { 1 } { regex_match }
+ }
+\cs_new_protected_nopar:Npn \regex_match_once:
+ {
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \cs_set_nopar:Npn \regex_if_two_empty_matches:F
+ { \int_compare:nNnF \l_regex_start_pos_int = \l_regex_current_pos_int }
+ \else:
+ \cs_set_eq:NN \regex_if_two_empty_matches:F \use:n
+ \fi:
+ \int_set_eq:NN \l_regex_start_pos_int \l_regex_success_pos_int
+ \bool_set_false:N \l_regex_match_success_bool
+ \prop_clear:N \l_regex_current_submatches_prop
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \regex_store_state:n { \l_regex_min_state_int }
+ \int_set:Nn \l_regex_current_pos_int
+ { \l_regex_start_pos_int - \c_one }
+ \regex_query_get:
+ \regex_match_loop:
+ \l_regex_every_match_tl
+ }
+\cs_new_protected_nopar:Npn \regex_single_match:
+ {
+ \tl_set:Nn \l_regex_every_match_tl
+ { \bool_gset_eq:NN \g_regex_success_bool \l_regex_match_success_bool }
+ }
+\cs_new_protected:Npn \regex_multi_match:n #1
+ {
+ \tl_set:Nn \l_regex_every_match_tl
+ {
+ \if_meaning:w \c_true_bool \l_regex_match_success_bool
+ \bool_gset_true:N \g_regex_success_bool
+ #1
+ \exp_after:wN \regex_match_once:
+ \fi:
+ }
+ }
+\cs_new_protected_nopar:Npn \regex_match_loop:
+ {
+ \tex_advance:D \l_regex_step_int \c_two
+ \int_incr:N \l_regex_current_pos_int
+ \int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int
+ \int_set_eq:NN \l_regex_case_changed_char_int \c_max_int
+ \regex_query_get:
+ \use:x
+ {
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \l_regex_min_active_int ;
+ }
+ \prg_break_point:n { \bool_set_false:N \l_regex_fresh_thread_bool }
+ \if_num:w \l_regex_max_active_int > \l_regex_min_active_int
+ \if_num:w \l_regex_current_pos_int < \l_regex_max_pos_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_match_loop:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \regex_match_one_active:w #1;
+ {
+ \if_num:w #1 < \l_regex_max_active_int
+ \regex_use_state_and_submatches:nn
+ { \int_value:w \tex_skip:D #1 }
+ { \tex_the:D \tex_toks:D #1 }
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \int_eval:w #1 + \c_one \exp_after:wN ;
+ \fi:
+ }
+\cs_new_protected:Npn \regex_query_set:nnn #1#2#3
+ {
+ \tex_muskip:D \l_regex_current_pos_int
+ = \etex_gluetomu:D
+ #3 sp
+ plus #2 sp
+ minus \l_regex_balance_int sp
+ \scan_stop:
+ \tex_toks:D \l_regex_current_pos_int {#1}
+ \int_incr:N \l_regex_current_pos_int
+ \if_case:w #2 \exp_stop_f:
+ \or: \int_incr:N \l_regex_balance_int
+ \or: \int_decr:N \l_regex_balance_int
+ \fi:
+ }
+\cs_new_protected_nopar:Npn \regex_query_get:
+ {
+ \l_regex_current_char_int
+ = \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ \l_regex_current_catcode_int = \etex_gluestretch:D
+ \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ }
+\cs_new_protected_nopar:Npn \regex_use_state:
+ {
+ \trace:nnx { regex } { 2 } { state~\int_use:N \l_regex_current_state_int }
+ \tex_dimen:D \l_regex_current_state_int
+ = \l_regex_step_int sp \scan_stop:
+ \tex_the:D \tex_toks:D \l_regex_current_state_int
+ \tex_dimen:D \l_regex_current_state_int
+ = \int_eval:w \l_regex_step_int + \c_one \int_eval_end: sp \scan_stop:
+ }
+\cs_new_protected:Npn \regex_use_state_and_submatches:nn #1 #2
+ {
+ \int_set:Nn \l_regex_current_state_int {#1}
+ \if_num:w \tex_dimen:D \l_regex_current_state_int < \l_regex_step_int
+ \tl_set:Nn \l_regex_current_submatches_prop {#2}
+ \exp_after:wN \regex_use_state:
+ \fi:
+ \scan_stop:
+ }
+\cs_new_protected_nopar:Npn \regex_action_start_wildcard:
+ {
+ \bool_set_true:N \l_regex_fresh_thread_bool
+ \regex_action_free:n {1}
+ \bool_set_false:N \l_regex_fresh_thread_bool
+ \regex_action_cost:n {0}
+ }
+\cs_new_protected_nopar:Npn \regex_action_free:n
+ { \regex_action_free_aux:nn { > \l_regex_step_int \else: } }
+\cs_new_protected_nopar:Npn \regex_action_free_group:n
+ { \regex_action_free_aux:nn { < \l_regex_step_int } }
+\cs_new_protected:Npn \regex_action_free_aux:nn #1#2
+ {
+ \use:x
+ {
+ \int_add:Nn \l_regex_current_state_int {#2}
+ \exp_not:n
+ {
+ \if_num:w \tex_dimen:D \l_regex_current_state_int #1
+ \exp_after:wN \regex_use_state:
+ \fi:
+ }
+ \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 }
+ }
+ }
+\cs_new_protected:Npn \regex_action_cost:n #1
+ {
+ \exp_args:No \regex_store_state:n
+ { \int_use:N \int_eval:w \l_regex_current_state_int + #1 }
+ }
+\cs_new_protected:Npn \regex_store_state:n #1
+ {
+ \regex_store_submatches:
+ \tex_skip:D \l_regex_max_active_int = #1 sp \scan_stop:
+ \int_incr:N \l_regex_max_active_int
+ }
+\cs_new_protected_nopar:Npn \regex_store_submatches:
+ {
+ \tex_toks:D \l_regex_max_active_int \exp_after:wN
+ { \l_regex_current_submatches_prop }
+ }
+\cs_new_protected_nopar:Npn \regex_disable_submatches:
+ {
+ \cs_set_protected_nopar:Npn \regex_store_submatches: { }
+ \cs_set_protected:Npn \regex_action_submatch:n ##1 { }
+ }
+\cs_new_protected:Npn \regex_action_submatch:n #1
+ {
+ \prop_put:Nno \l_regex_current_submatches_prop {#1}
+ { \int_use:N \l_regex_current_pos_int }
+ }
+\cs_new_protected_nopar:Npn \regex_action_success:
+ {
+ \regex_if_two_empty_matches:F
+ {
+ \bool_set_true:N \l_regex_match_success_bool
+ \bool_set_eq:NN \l_regex_empty_success_bool
+ \l_regex_fresh_thread_bool
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_current_pos_int
+ \prop_set_eq:NN \l_regex_success_submatches_prop
+ \l_regex_current_submatches_prop
+ \prg_map_break:
+ }
+ }
+\int_new:N \l_regex_replacement_csnames_int
+\tl_new:N \l_regex_balance_tl
+\cs_new:Npn \regex_replacement_balance_one_match:n #1
+ { - \regex_submatch_balance:n {#1} }
+\cs_new:Npn \regex_replacement_do_one_match:n #1
+ {
+ \regex_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D #1 }
+ { \tex_skip:D #1 }
+ }
+\cs_new:Npn \regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+\cs_new:Npn \regex_query_range:nn #1#2
+ {
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 \exp_after:wN ;
+ \int_use:N \int_eval:w #2 ;
+ \prg_break_point:n { }
+ }
+\cs_new:Npn \regex_query_range_loop:ww #1 ; #2 ;
+ {
+ \if_num:w #1 < #2 \exp_stop_f:
+ \else:
+ \exp_after:wN \prg_map_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \exp_stop_f:
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 + \c_one ; #2 ;
+ }
+\cs_new:Npn \regex_query_submatch:n #1
+ {
+ \regex_query_range:nn
+ { \tex_skip:D \int_eval:w #1 }
+ { \etex_gluestretch:D \tex_skip:D \int_eval:w #1 }
+ }
+\cs_new_protected:Npn \regex_submatch_balance:n #1
+ {
+ \etex_glueshrink:D \etex_mutoglue:D \etex_muexpr:D
+ \tex_muskip:D \etex_gluestretch:D \tex_skip:D #1
+ - \tex_muskip:D \tex_skip:D #1
+ \scan_stop:
+ }
+\cs_new_protected:Npn \regex_replacement:n #1
+ {
+ \trace_push:nnn { regex } { 1 } { regex_replacement:n }
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_balance_int
+ \tl_clear:N \l_regex_balance_tl
+ \regex_escape_use:nnnn
+ {
+ \if_charcode:w \c_rbrace_str ##1
+ \regex_replacement_rbrace:N \else: \tl_build_one:n \fi: ##1
+ }
+ { \regex_replacement_escaped:N ##1 }
+ { \tl_build_one:n ##1 }
+ {#1}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \msg_kernel_error:nnx { regex } { replacement-missing-rbrace }
+ { \int_use:N \l_regex_replacement_csnames_int }
+ \tl_build_one:x
+ { \prg_replicate:nn \l_regex_replacement_csnames_int \cs_end: }
+ \fi:
+ \cs_gset:Npx \regex_replacement_balance_one_match:n ##1
+ {
+ + \int_use:N \l_regex_balance_int
+ \l_regex_balance_tl
+ - \regex_submatch_balance:n {##1}
+ }
+ \tl_build_end:
+ \exp_args:No \regex_replacement_aux:n \l_regex_internal_a_tl
+ \trace_pop:nnn { regex } { 1 } { regex_replacement:n }
+ }
+\cs_new_protected:Npn \regex_replacement_aux:n #1
+ {
+ \cs_set:Npn \regex_replacement_do_one_match:n ##1
+ {
+ \regex_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D ##1 }
+ { \tex_skip:D ##1 }
+ #1
+ }
+ }
+\cs_new_protected:Npn \regex_replacement_escaped:N #1
+ {
+ \cs_if_exist_use:cF { regex_replacement_#1:w }
+ {
+ \if_num:w \c_one < 1#1 \exp_stop_f:
+ \regex_replacement_put_submatch:n {#1}
+ \else:
+ \tl_build_one:n #1
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \regex_replacement_put_submatch:n #1
+ {
+ \if_num:w #1 < \l_regex_capturing_group_int
+ \tl_build_one:n { \regex_query_submatch:n { #1 + ##1 } }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \tl_put_right:Nn \l_regex_balance_tl
+ { + \regex_submatch_balance:n { \int_eval:w #1+##1 \int_eval_end: } }
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_replacement_g:w #1#2
+ {
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
+ {
+ \int_zero:N \l_regex_internal_a_int
+ \regex_replacement_g_digits:NN
+ }
+ { \regex_replacement_error:NNN g #1 #2 }
+ }
+\cs_new_protected:Npn \regex_replacement_g_digits:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
+ {
+ \if_num:w \c_one < 1#2 \exp_stop_f:
+ \int_set:Nn \l_regex_internal_a_int
+ { \c_ten * \l_regex_internal_a_int + #2 }
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \regex_replacement_g_digits:NN
+ \else:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ {
+ \if_meaning:w \regex_replacement_rbrace:N #1
+ \exp_args:No \regex_replacement_put_submatch:n
+ { \int_use:N \l_regex_internal_a_int }
+ \exp_after:wN \use_none:nn
+ \else:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ #1 #2
+ }
+\cs_new_protected:Npn \regex_replacement_c:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
+ {
+ \cs_if_exist_use:cF { regex_replacement_c_#2:w }
+ { \regex_replacement_error:NNN c #1#2 }
+ }
+ { \regex_replacement_error:NNN c #1#2 }
+ }
+\cs_new_protected_nopar:cpn { regex_replacement_c_ \c_lbrace_str :w }
+ {
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n
+ { \exp_not:n { \exp_after:wN \regex_replacement_exp_not:N \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:N \cs:w } }
+ \fi:
+ \int_incr:N \l_regex_replacement_csnames_int
+ }
+\cs_new_protected:Npn \regex_replacement_u:w #1#2
+ {
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
+ {
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \exp_not:V \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:V \cs:w } }
+ \fi:
+ \int_incr:N \l_regex_replacement_csnames_int
+ }
+ { \regex_replacement_error:NNN u #1#2 }
+ }
+\cs_new_protected:Npn \regex_replacement_rbrace:N #1
+ {
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \tl_build_one:n \cs_end:
+ \int_decr:N \l_regex_replacement_csnames_int
+ \else:
+ \tl_build_one:n #1
+ \fi:
+ }
+\group_begin:
+ \cs_new_protected:Npn \regex_replacement_char:nNN #1#2#3
+ {
+ \if_meaning:w \prg_do_nothing: #3
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \tex_lccode:D \c_zero = `#3 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {#1} }
+ \fi:
+ }
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_A:w
+ { \regex_replacement_char:nNN { \exp_not:n { \exp_not:N ^^@ } } }
+ \char_set_catcode_group_begin:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_B:w
+ {
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_incr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_after:wN ^^@ \if_false: } \fi: } }
+ }
+ \cs_new_protected:Npn \regex_replacement_c_C:w #1#2
+ { \tl_build_one:n { \exp_not:N \exp_not:N \exp_not:c {#2} } }
+ \char_set_catcode_math_subscript:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_D:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_E:w
+ {
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_decr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \if_false: { \fi: ^^@ } }
+ }
+ \char_set_catcode_letter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_L:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_math_toggle:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_M:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_other:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_O:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_parameter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_P:w
+ {
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_not:n { ^^@^^@^^@^^@ } } }
+ }
+ \cs_new_protected:Npn \regex_replacement_c_S:w #1#2
+ {
+ \if_meaning:w \prg_do_nothing: #2
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \if_num:w `#2 = \c_zero
+ \msg_kernel_error:nn { regex } { replacement-null-space }
+ \fi:
+ \tex_lccode:D 32 = `#2 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {~} }
+ \fi:
+ }
+ \char_set_catcode_alignment:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_T:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_math_superscript:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_U:w
+ { \regex_replacement_char:nNN { ^^@ } }
+\group_end:
+\cs_new_protected:Npn \regex_replacement_error:NNN #1#2#3
+ {
+ \msg_kernel_error:nnx { regex } { replacement-#1 } {#3}
+ #2 #3
+ }
+\cs_new_protected:Npn \regex_new:N #1
+ { \cs_new_eq:NN #1 \c_regex_no_match_regex }
+\cs_new_protected_nopar:Npn \regex_set:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_set_eq:NN #1 \l_regex_internal_regex
+ }
+\cs_new_protected_nopar:Npn \regex_gset:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_gset_eq:NN #1 \l_regex_internal_regex
+ }
+\cs_new_protected_nopar:Npn \regex_const:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_const:Nx #1 { \exp_not:o \l_regex_internal_regex }
+ }
+\cs_new_protected:Npn \regex_show:n #1
+ {
+ \regex_compile:n {#1}
+ \regex_show_aux:Nx \l_regex_internal_regex
+ { { \tl_to_str:n {#1} } }
+ }
+\cs_new_protected:Npn \regex_show:N #1
+ { \regex_show_aux:Nx #1 { variable~\token_to_str:N #1 } }
+\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
+ {
+ \regex_match_aux:nn { \regex_build:n {#1} } {#2}
+ \regex_aux_return:
+ }
+\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \regex_match_aux:nn { \regex_build:N #1 } {#2}
+ \regex_aux_return:
+ }
+\cs_new_protected:Npn \regex_count:nnN #1
+ { \regex_count_aux:nnN { \regex_build:n {#1} } }
+\cs_new_protected:Npn \regex_count:NnN #1
+ { \regex_count_aux:nnN { \regex_build:N #1 } }
+\cs_set_protected:Npn \regex_tmp:w #1#2#3
+ {
+ \cs_new_protected:Npn #2 ##1 { #1 { \regex_build:n {##1} } }
+ \cs_new_protected:Npn #3 ##1 { #1 { \regex_build:N ##1 } }
+ \prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
+ { #1 { \regex_build:n {##1} } {##2} ##3 \regex_aux_return: }
+ \prg_new_protected_conditional:Npnn #3 ##1##2##3 { T , F , TF }
+ { #1 { \regex_build:N ##1 } {##2} ##3 \regex_aux_return: }
+ }
+\regex_tmp:w \regex_extract_once_aux:nnN
+ \regex_extract_once:nnN \regex_extract_once:NnN
+\regex_tmp:w \regex_extract_all_aux:nnN
+ \regex_extract_all:nnN \regex_extract_all:NnN
+\regex_tmp:w \regex_replace_once_aux:nnN
+ \regex_replace_once:nnN \regex_replace_once:NnN
+\regex_tmp:w \regex_replace_all_aux:nnN
+ \regex_replace_all:nnN \regex_replace_all:NnN
+\regex_tmp:w \regex_split_aux:nnN \regex_split:nnN \regex_split:NnN
+\int_new:N \l_regex_match_count_int
+\flag_new:n { regex_begin }
+\flag_new:n { regex_end }
+\int_new:N \l_regex_submatch_int
+\int_new:N \l_regex_zeroth_submatch_int
+\cs_new_protected_nopar:Npn \regex_aux_return:
+ {
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_match_aux:nn #1#2
+ {
+ \group_begin:
+ \regex_disable_submatches:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \group_end:
+ }
+\cs_new_protected:Npn \regex_count_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_disable_submatches:
+ \int_zero:N \l_regex_match_count_int
+ \regex_multi_match:n { \int_incr:N \l_regex_match_count_int }
+ #1
+ \regex_match:n {#2}
+ \exp_args:NNNo
+ \group_end:
+ \int_set:Nn #3 { \int_use:N \l_regex_match_count_int }
+ }
+\cs_new_protected:Npn \regex_extract_once_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \regex_extract:
+ \regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \regex_extract_all_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_match:n {#2}
+ \regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \regex_split_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_multi_match:n
+ {
+ \if_num:w \l_regex_start_pos_int < \l_regex_success_pos_int
+ \regex_extract:
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \l_regex_start_pos_int sp
+ plus \tex_skip:D \l_regex_zeroth_submatch_int \scan_stop:
+ \fi:
+ }
+ #1
+ \regex_match:n {#2}
+ \tex_skip:D \l_regex_submatch_int
+ = \l_regex_start_pos_int sp plus \l_regex_max_pos_int sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \if_num:w \l_regex_start_pos_int = \l_regex_max_pos_int
+ \int_decr:N \l_regex_submatch_int
+ \fi:
+ \fi:
+ \regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \regex_group_end_extract_seq:N #1
+ {
+ \cs_set_eq:NN \seq_item:n \scan_stop:
+ \flag_clear:n { regex_begin }
+ \flag_clear:n { regex_end }
+ \tl_set:Nx \l_regex_internal_a_tl
+ {
+ \prg_stepwise_function:nnnN
+ { \c_two * \l_regex_max_state_int }
+ \c_one
+ { \l_regex_submatch_int - \c_one }
+ \regex_extract_seq_aux:n
+ }
+ \int_compare:nNnF
+ { \flag_height:n { regex_begin } + \flag_height:n { regex_end } }
+ = \c_zero
+ {
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \flag_height:n { regex_end } }
+ { \flag_height:n { regex_begin } }
+ }
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1 { \l_regex_internal_a_tl }
+ }
+ }
+\cs_new:Npn \regex_extract_seq_aux:n #1
+ {
+ \seq_item:n
+ {
+ \exp_after:wN \regex_extract_seq_aux:ww
+ \int_value:w \regex_submatch_balance:n {#1} ; #1;
+ }
+ }
+\cs_new:Npn \regex_extract_seq_aux:ww #1; #2;
+ {
+ \if_num:w #1 < \c_zero
+ \flag_raise:n { regex_end }
+ \prg_replicate:nn {-#1} { \exp_not:n { { \if_false: } \fi: } }
+ \fi:
+ \regex_query_submatch:n {#2}
+ \if_num:w #1 > \c_zero
+ \flag_raise:n { regex_begin }
+ \prg_replicate:nn {#1} { \exp_not:n { \if_false: { \fi: } } }
+ \fi:
+ }
+\cs_new_protected_nopar:Npn \regex_extract:
+ {
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \int_set_eq:NN \l_regex_zeroth_submatch_int \l_regex_submatch_int
+ \prg_replicate:nn \l_regex_capturing_group_int
+ {
+ \tex_skip:D \l_regex_submatch_int \c_zero sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
+ }
+ \prop_map_inline:Nn \l_regex_success_submatches_prop
+ {
+ \if_num:w ##1 \c_minus_one
+ \exp_after:wN \regex_extract_aux_e:wn \int_value:w
+ \else:
+ \exp_after:wN \regex_extract_aux_b:wn \int_value:w
+ \fi:
+ \int_eval:w \l_regex_zeroth_submatch_int + ##1 {##2}
+ }
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \tex_the:D \tex_skip:D \l_regex_zeroth_submatch_int
+ minus \l_regex_start_pos_int sp \scan_stop:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_extract_aux_b:wn #1 < #2
+ {
+ \tex_skip:D #1 = #2 sp
+ plus \etex_gluestretch:D \tex_skip:D #1 \scan_stop:
+ }
+\cs_new_protected:Npn \regex_extract_aux_e:wn #1 > #2
+ {
+ \tex_skip:D #1
+ = 1 \tex_skip:D #1 plus #2 sp \scan_stop:
+ }
+\cs_new_protected:Npn \regex_replace_once_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_single_match:
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n { #3 }
+ \if_meaning:w \c_false_bool \g_regex_success_bool
+ \group_end:
+ \else:
+ \regex_extract:
+ \int_set:Nn \l_regex_balance_int
+ {
+ \regex_replacement_balance_one_match:n
+ { \l_regex_zeroth_submatch_int }
+ }
+ \tl_set:Nx \l_regex_internal_a_tl
+ {
+ \regex_replacement_do_one_match:n { \l_regex_zeroth_submatch_int }
+ \regex_query_range:nn
+ { \etex_gluestretch:D \tex_skip:D \l_regex_zeroth_submatch_int }
+ { \l_regex_max_pos_int }
+ }
+ \regex_group_end_replace:N #3
+ \fi:
+ }
+\cs_new_protected:Npn \regex_replace_all_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n {#3}
+ \int_set:Nn \l_regex_balance_int
+ {
+ 0
+ \prg_stepwise_function:nnnN
+ { \c_two * \l_regex_max_state_int }
+ \l_regex_capturing_group_int
+ { \l_regex_submatch_int - \c_one }
+ \regex_replacement_balance_one_match:n
+ }
+ \tl_set:Nx \l_regex_internal_a_tl
+ {
+ \prg_stepwise_function:nnnN
+ { \c_two * \l_regex_max_state_int }
+ \l_regex_capturing_group_int
+ { \l_regex_submatch_int - \c_one }
+ \regex_replacement_do_one_match:n
+ \regex_query_range:nn
+ \l_regex_start_pos_int \l_regex_max_pos_int
+ }
+ \regex_group_end_replace:N #3
+ }
+\cs_new_protected_nopar:Npn \regex_group_end_replace:N #1
+ {
+ \if_num:w \l_regex_balance_int = \c_zero
+ \else:
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { replacing }
+ { \int_max:nn { - \l_regex_balance_int } { \c_zero } }
+ { \int_max:nn { \l_regex_balance_int } { \c_zero } }
+ \fi:
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1
+ {
+ \if_int_compare:w \l_regex_balance_int < \c_zero
+ \prg_replicate:nn { - \l_regex_balance_int }
+ { { \if_false: } \fi: }
+ \fi:
+ \l_regex_internal_a_tl
+ \if_int_compare:w \l_regex_balance_int > \c_zero
+ \prg_replicate:nn { \l_regex_balance_int }
+ { \if_false: { \fi: } }
+ \fi:
+ }
+ }
+ }
+\msg_kernel_new:nnnn { regex } { trailing-backslash }
+ { Trailing~escape~character~\iow_char:N\\. }
+ {
+ A~regular~expression~or~its~replacement~text~ends~with~
+ the~escape~character~\iow_char:N\\.~It~will~be~ignored.
+ }
+\msg_kernel_new:nnnn { regex } { x-missing-rbrace }
+ { Missing~closing~brace~in~\iow_char:N\\x~hexadecimal~sequence. }
+ {
+ You~wrote~something~like~
+ `\iow_char:N\\x\{\int_to_hexadecimal:n{#1}'.~
+ The~closing~brace~is~missing.
+ }
+\msg_kernel_new:nnnn { regex } { x-overflow }
+ { Character~code~'#1'~too~large~in~\iow_char:N\\x~hexadecimal~sequence. }
+ {
+ You~wrote~something~like~
+ `\iow_char:N\\x\{\int_to_hexadecimal:n{#1}\}'.~
+ The~character~code~'#1'~is~larger~than~\int_use:N \c_max_char_int.
+ }
+\msg_kernel_new:nnnn { regex } { invalid-quantifier }
+ { Braced~quantifier~'#1'~may~not~be~followed~by~'#2'. }
+ {
+ The~character~'#2'~is~invalid~in~the~braced~quantifier~'#1'.~
+ The~only~valid~quantifiers~are~'*',~'?',~'+',~'{<int>}',~
+ '{<min>,}'~and~'{<min>,<max>}',~followed~or~not~by~'?'.
+ }
+\msg_kernel_new:nnnn { regex } { missing-rbrack }
+ { Missing~right~bracket~inserted~in~regular~expression. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~character~class~
+ was~started~with~'[',~but~the~matching~']'~is~missing.
+ }
+\msg_kernel_new:nnnn { regex } { missing-rparen }
+ {
+ Missing~right~parenthes\int_compare:nTF{#1=1}{i}{e}s~
+ inserted~in~regular~expression.
+ }
+ {
+ LaTeX~was~given~a~regular~expression~with~\int_eval:n{#1}~
+ more~left~parenthes\int_compare:nTF{#1=1}{i}{e}s~than~right~
+ parenthes\int_compare:nTF{#1=1}{i}{e}s.
+ }
+\msg_kernel_new:nnnn { regex } { extra-rparen }
+ { Extra~right~parenthesis~ignored~in~regular~expression. }
+ {
+ LaTeX~came~across~a~closing~parenthesis~when~no~submatch~group~
+ was~open.~The~parenthesis~will~be~ignored.
+ }
+\msg_kernel_new:nnnn { regex } { bad-escape }
+ {
+ Invalid~escape~\c_backslash_str #1~
+ \regex_if_in_cs:TF { within~a~control~sequence. }
+ {
+ \regex_if_in_class:TF
+ { in~a~character~class. }
+ { following~a~category~test. }
+ }
+ }
+ {
+ The~escape~sequence~\iow_char:N\\#1~may~not~appear~
+ \regex_if_in_cs:TF
+ {
+ within~a~control~sequence~test~introduced~by~
+ \iow_char:N\\c\iow_char:N\{.
+ }
+ {
+ \regex_if_in_class:TF
+ { within~a~character~class~ }
+ { following~a~category~test~such~as~\iow_char:N\\cL~ }
+ because~it~does~not~match~exactly~one~character.
+ }
+ }
+\msg_kernel_new:nnnn { regex } { range-missing-end }
+ { Invalid~end-point~for~range~'#1-#2'~in~character~class. }
+ {
+ The~end-point~'#2'~of~the~range~'#1-#2'~may~not~serve~as~an~
+ end-point~for~a~range:~alphanumeric~characters~should~not~be~
+ escaped,~and~non-alphanumeric~characters~should~be~escaped.
+ }
+\msg_kernel_new:nnnn { regex } { range-backwards }
+ { Range~[#1-#2]~out~of~order~in~character~class. }
+ {
+ In~ranges~of~characters~[x-y]~appearing~in~character~classes,~
+ the~first~character~code~must~not~be~larger~than~the~second.~
+ Here,~#1~has~character~code~\int_eval:n {`#1},~while~#2~has~
+ character~code~\int_eval:n {`#2}.
+ }
+\msg_kernel_new:nnnn { regex } { c-bad-mode }
+ { Invalid~nested~\iow_char:N\\c~escape~in~regular~expression. }
+ {
+ The~\iow_char:N\\c~escape~cannot~be~used~within~
+ a~control~sequence~test~'\iow_char:N\\c{...}'.~
+ To~combine~several~category~tests,~use~'\iow_char:N\\c[...]'.
+ }
+\msg_kernel_new:nnnn { regex } { c-missing-rbrace }
+ { Missing~right~brace~inserted~for~\iow_char:N\\c~escape. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~
+ '\iow_char:N\\c\iow_char:N\{...'~construction~was~not~ended~
+ with~a~closing~brace~'\iow_char:N\}'.
+ }
+\msg_kernel_new:nnnn { regex } { c-missing-rbrack }
+ { Missing~right~bracket~inserted~for~\iow_char:N\\c~escape. }
+ {
+ A~construction~'\iow_char:N\\c[...'~appears~in~a~
+ regular~expression,~but~the~closing~']'~is~not~present.
+ }
+\msg_kernel_new:nnnn { regex } { c-missing-category }
+ { Invalid~character~'#1'~following~\iow_char:N\\c~escape. }
+ {
+ In~regular~expressions,~the~\iow_char:N\\c~escape~sequence~
+ may~only~be~followed~by~a~left~brace,~a~left~bracket,~or~a~
+ capital~letter~representing~a~character~category,~namely~
+ one~of~ABCDELMOPSTU.
+ }
+\msg_kernel_new:nnnn { regex } { u-missing-lbrace }
+ { Missing~left~brace~following~\iow_char:N\\u~escape. }
+ {
+ The~\iow_char:N\\u~escape~sequence~must~be~followed~by~
+ a~brace~group~with~the~name~of~the~variable~to~use.
+ }
+\msg_kernel_new:nnnn { regex } { u-missing-rbrace }
+ { Missing~right~brace~inserted~for~\iow_char:N\\u~escape. }
+ {
+ LaTeX~
+ \tl_if_empty:xTF {#2}
+ { reached~the~end~of~the~string~ }
+ { encountered~an~escaped~alphanumeric~character '\iow_char:N\\#2'~ }
+ when~parsing~the~argument~of~an~'\iow_char:N\\u\iow_char:N\{...\}'~escape.
+ }
+\msg_kernel_new:nnnn { regex } { posix-unsupported }
+ { POSIX~collating~element~'[#1 ~ #1]'~not~supported. }
+ {
+ The~[.foo.]~and~[=bar=]~syntaxes~have~a~special~meaning~in~POSIX~
+ regular~expressions.~This~is~not~supported~by~LaTeX.~Maybe~you~
+ forgot~to~escape~a~left~bracket~in~a~character~class?
+ }
+\msg_kernel_new:nnnn { regex } { posix-unknown }
+ { POSIX~class~[:#1:]~unknown. }
+ {
+ [:#1:]~is~not~among~the~known~POSIX~classes~
+ [:alnum:],~[:alpha:],~[:ascii:],~[:blank:],~
+ [:cntrl:],~[:digit:],~[:graph:],~[:lower:],~
+ [:print:],~[:punct:],~[:space:],~[:upper:],~
+ [:word:],~and~[:xdigit:].
+ }
+\msg_kernel_new:nnnn { regex } { posix-missing-close }
+ { Missing~closing~':]'~for~POSIX~class. }
+ { The~POSIX~syntax~'#1'~must~be~followed~by~':]',~not~'#2'. }
+\msg_kernel_new:nnnn { regex } { result-unbalanced }
+ { Missing~brace~inserted~when~#1. }
+ {
+ LaTeX~was~asked~to~do~some~regular~expression~operation,~
+ and~the~resulting~token~list~would~not~have~the~same~number~
+ of~begin-group~and~end-group~tokens.~Braces~were~inserted:~
+ #2~left,~#3~right.
+ }
+\msg_kernel_new:nnnn { regex } { unknown-option }
+ { Unknown~option~`#1'~for~regular~expressions. }
+ {
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-c }
+ { Misused~\iow_char:N\\c~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
+ can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~
+ or~a~brace~group,~not~by~'#1'.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-u }
+ { Misused~\iow_char:N\\u~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~\iow_char:N\\u~escape~sequence~
+ must~be~~followed~by~a~brace~group~holding~the~name~of~the~
+ variable~to~use.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-g }
+ { Missing~brace~for~the~\iow_char:N\\g~construction~in~a~replacement~text. }
+ {
+ In~the~replacement~text~for~a~regular~expression~search,~
+ submatches~are~represented~either~as~\iow_char:N \\g{dd..d},~
+ or~\\d,~where~`d'~are~single~digits.~Here,~a~brace~is~missing.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-catcode-end }
+ {
+ Missing~character~for~the~\iow_char:N\\c<category><character>~
+ construction~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
+ can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~representing~
+ the~character~category.~Then,~a~character~must~follow.~LaTeX~
+ reached~the~end~of~the~replacement~when~looking~for~that.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-null-space }
+ { TeX~cannot~build~a~space~token~with~character~code~0. }
+ {
+ You~asked~for~a~character~token~with~category~'space',~
+ and~character~code~0,~for~instance~through~
+ '\iow_char:N\\cS\iow_char:N\\x00'.~
+ This~specific~case~is~impossible~and~will~be~replaced~
+ by~a~normal~space.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-missing-rbrace }
+ { Missing~right~brace~inserted~in~replacement~text. }
+ {
+ There~were~\int_use:N \l_regex_replacement_csnames_int \
+ missing~right~braces.
+ }
+\cs_new:Npn \regex_msg_repeated:nnN #1#2#3
+ {
+ \str_if_eq:xxF { #1 #2 } { 1 0 }
+ {
+ , ~ repeated ~
+ \prg_case_int:nnn {#2}
+ {
+ { -1 } { #1~or~more~times,~\bool_if:NTF #3 { lazy } { greedy } }
+ { 0 } { #1~times }
+ }
+ {
+ between~#1~and~\int_eval:n {#1+#2}~times,~
+ \bool_if:NTF #3 { lazy } { greedy }
+ }
+ }
+ }
+\cs_new_protected:Npn \regex_trace_states:n #1
+ {
+ \prg_stepwise_inline:nnnn
+ \l_regex_min_state_int
+ \c_one
+ { \l_regex_max_state_int - 1 }
+ {
+ \trace:nnx { regex } { #1 }
+ { \iow_char:N \\toks ##1 = { \tex_the:D \tex_toks:D ##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 2b99a8eb661..17480334049 100644
--- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty
@@ -46,236 +46,35 @@
%%
%% -----------------------------------------------------------------------
\RequirePackage{expl3}
-\GetIdInfo$Id: l3regex.dtx 3326 2012-02-07 00:39:44Z bruno $
+\GetIdInfo$Id: l3regex.dtx 3463 2012-02-26 15:05:14Z bruno $
{L3 Experimental Regular Expressions}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
-\RequirePackage{l3str, l3tl-analysis, l3flag}
+\RequirePackage{l3tl-build, l3tl-analysis, l3flag, l3str}
+\cs_generate_variant:Nn \tl_to_str:n { V }
\cs_new:Npn \regex_tmp:w { }
\tl_new:N \l_regex_internal_a_tl
\tl_new:N \l_regex_internal_b_tl
\int_new:N \l_regex_internal_a_int
\int_new:N \l_regex_internal_b_int
\int_new:N \l_regex_internal_c_int
+\bool_new:N \l_regex_internal_bool
+\seq_new:N \l_regex_internal_seq
\tl_new:N \g_regex_internal_tl
-\tl_const:Nn \c_regex_d_tl
- { \regex_item_range:nn \c_forty_eight { 57 } } % 0--9
-\tl_const:Nn \c_regex_h_tl
- {
- \regex_item_equal:n \c_thirty_two % space
- \regex_item_equal:n \c_nine % tab
- }
-\tl_const:Nn \c_regex_s_tl
- {
- \regex_item_equal:n \c_thirty_two % space
- \regex_item_equal:n \c_nine % tab
- \regex_item_equal:n \c_ten % lf
- \regex_item_equal:n \c_twelve % ff
- \regex_item_equal:n \c_thirteen % cr
- }
-\tl_const:Nn \c_regex_v_tl
- { \regex_item_range:nn \c_ten \c_thirteen } % lf, vtab, ff, cr
-\tl_const:Nn \c_regex_w_tl
- {
- \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_D_tl
- { \c_regex_d_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_H_tl
- { \c_regex_h_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_S_tl
- { \c_regex_s_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_V_tl
- { \c_regex_v_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_W_tl
- { \c_regex_w_tl \regex_break_point:TF { } \regex_break_true:w }
-\tl_const:Nn \c_regex_N_tl
- {
- \regex_item_equal:n \c_ten
- \regex_break_point:TF { } { \regex_break_true:w }
- }
-\tl_const:cn { c_regex_._tl }
- {
- \if_num:w \l_regex_current_char_int > - \c_two
- \exp_after:wN \regex_break_true:w
- \fi:
- }
-\int_new:N \l_regex_build_mode_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_end_group_seq
-\seq_new:N \l_regex_end_alternation_seq
-\int_new:N \l_regex_capturing_group_int
-\seq_new:N \l_regex_capturing_group_seq
-\int_new:N \l_regex_capturing_group_max_int
-\tl_new:N \l_regex_one_or_group_tl
-\int_new:N \l_regex_catcodes_int
-\int_new:N \l_regex_catcodes_default_int
-\int_const:Nn \c_regex_catcode_C_int { "1 }
-\int_const:Nn \c_regex_catcode_B_int { "4 }
-\int_const:Nn \c_regex_catcode_E_int { "10 }
-\int_const:Nn \c_regex_catcode_M_int { "40 }
-\int_const:Nn \c_regex_catcode_T_int { "100 }
-\int_const:Nn \c_regex_catcode_P_int { "1000 }
-\int_const:Nn \c_regex_catcode_U_int { "4000 }
-\int_const:Nn \c_regex_catcode_D_int { "10000 }
-\int_const:Nn \c_regex_catcode_S_int { "100000 }
-\int_const:Nn \c_regex_catcode_L_int { "400000 }
-\int_const:Nn \c_regex_catcode_O_int { "1000000 }
-\int_const:Nn \c_regex_catcode_A_int { "4000000 }
-\int_const:Nn \c_regex_catcodes_all_int { "5515155 }
-\bool_new:N \l_regex_catcodes_bool
-\bool_new:N \l_regex_tests_bool
-\tl_new:N \l_regex_tests_tl
-\bool_new:N \l_regex_tests_saved_bool
-\tl_new:N \l_regex_tests_saved_tl
-\int_new:N \l_regex_catcodes_saved_int
-\int_new:N \l_regex_min_index_int
-\int_new:N \l_regex_max_index_int
-\int_new:N \l_regex_nesting_int
-\int_new:N \l_regex_current_index_int
-\int_new:N \l_regex_start_index_int
-\int_new:N \l_regex_success_index_int
-\int_new:N \l_regex_current_char_int
-\int_new:N \l_regex_current_catcode_int
-\tl_new:N \l_regex_current_token_tl
-\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_step_int
-\int_new:N \l_regex_max_active_int
-\tl_new:N \l_regex_every_match_tl
-\bool_new:N \l_regex_fresh_thread_bool
-\bool_new:N \l_regex_success_empty_bool
-\cs_new_eq:NN \regex_if_two_empty_matches:F \use:n
-\bool_new:N \g_regex_success_bool
-\bool_new:N \l_regex_saved_success_bool
-\bool_new:N \l_regex_success_match_bool
\tl_const:Nn \c_regex_no_match_regex
{
- \regex_nfa:Nw \c_regex_no_match_regex
- \l_regex_max_state_int = \c_one
- \l_regex_capturing_group_int = \c_zero
- \tex_toks:D \c_zero { \s_stop }
- \s_stop
- }
-\cs_new_protected:Npn \regex_new:N #1
- {
- \chk_if_free_cs:N #1
- \cs_gset_eq:NN #1 \c_regex_no_match_regex
- }
-\regex_new:N \l_regex_internal_regex
-\int_new:N \l_regex_replacement_csnames_int
-\int_new:N \l_regex_replacement_int
-\int_new:N \l_regex_submatch_int
-\int_new:N \l_regex_submatch_start_int
-\int_new:N \l_regex_match_count_int
-\flag_new:n { regex_begin }
-\flag_new:n { regex_end }
-\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2
- {
- \cs_set_nopar:Npx \regex_tmp:w \s_stop { \s_stop #2 }
- \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
- { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 }
- }
-\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2
- {
- \cs_set_nopar:Npx \regex_tmp:w {#2}
- \tex_toks:D #1 \exp_after:wN
- { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w }
- }
-\cs_new:Npn \regex_toks_range:nn #1#2
- {
- \exp_after:wN \regex_toks_range:ww
- \int_use:N \int_eval:w #1 \exp_after:wN ;
- \int_use:N \int_eval:w #2 ;
- \prg_break_point:n { }
- }
-\cs_new:Npn \regex_toks_range:ww #1 ; #2 ;
- {
- \if_num:w #1 < #2 \exp_stop_f:
- \else:
- \exp_after:wN \prg_map_break:
- \fi:
- \tex_the:D \tex_toks:D #1 \exp_stop_f:
- \exp_after:wN \regex_toks_range:ww
- \int_use:N \int_eval:w #1 + \c_one ; #2 ;
- }
-\cs_new:Npn \regex_query_submatch:nn #1#2
- {
- \if_num:w #1 < \l_regex_capturing_group_int
- \exp_after:wN \regex_query_submatch:w
- \int_use:N \int_eval:w #1 + #2 ;
- \fi:
- }
-\cs_new:Npn \regex_query_submatch:w #1 ;
- {
- \regex_toks_range:nn
- { \tex_skip:D #1 }
- { \etex_gluestretch:D \tex_skip:D #1 }
- }
-\cs_new_protected:Npn \regex_seq_pop_int:NN #1#2
- {
- \seq_pop:NN #1 \l_regex_internal_a_tl
- \int_set:Nn #2 \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_get_int:NN #1#2
- {
- \seq_get:NN #1 \l_regex_internal_a_tl
- \int_set:Nn #2 \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_push_int:NN #1#2
- { \seq_push:No #1 { \int_use:N #2 } }
-\cs_new_protected:Npn \regex_seq_pop_use:N #1
- {
- \seq_pop:NN #1 \l_regex_internal_a_tl
- \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_seq_get_use:N #1
- {
- \seq_get:NN #1 \l_regex_internal_a_tl
- \l_regex_internal_a_tl
- }
-\cs_new_protected:Npn \regex_get_digits:nw #1
- {
- \tex_afterassignment:D \regex_tmp:w
- \cs_set_nopar:Npx \regex_tmp:w
- {
- \exp_not:n {#1}
- { \if_false: } } \fi:
- \regex_get_digits_aux:NN
- }
-\cs_new:Npn \regex_get_digits_aux:NN #1#2
- {
- \if_meaning:w \regex_build_raw:N #1
- \if_num:w 9 < 1 \exp_not:N #2 \exp_stop_f:
- #2
- \else:
- \regex_get_digits_end:w #1 #2
- \fi:
- \else:
- \regex_get_digits_end:w #1 #2
- \fi:
- \regex_get_digits_aux:NN
- }
-\cs_new:Npn \regex_get_digits_end:w #1 \fi: #2 \regex_get_digits_aux:NN
- {
- \fi: #2
- \if_false: { { \fi: } }
- #1
+ \regex_branch:n
+ { \regex_class:NnnnN \c_true_bool { } { 1 } { 0 } \c_true_bool }
}
+\int_new:N \l_regex_balance_int
\cs_new_protected:Npn \regex_break_true:w
#1 \regex_break_point:TF #2 #3 {#2}
\cs_new_protected:Npn \regex_break_point:TF #1 #2 { #2 }
+\cs_new_protected:Npn \regex_item_reverse:n #1
+ {
+ #1
+ \regex_break_point:TF { } \regex_break_true:w
+ }
\cs_new_protected:Npn \regex_item_caseful_equal:n #1
{
\if_num:w #1 = \l_regex_current_char_int
@@ -290,17 +89,14 @@
\fi:
\fi:
}
-\cs_new_protected:Npn \regex_item_caseful_geq:n #1
- {
- \reverse_if:N \if_num:w #1 > \l_regex_current_char_int
- \exp_after:wN \regex_break_true:w
- \fi:
- }
\cs_new_protected:Npn \regex_item_caseless_equal:n #1
{
\if_num:w #1 = \l_regex_current_char_int
\exp_after:wN \regex_break_true:w
\fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \fi:
\if_num:w #1 = \l_regex_case_changed_char_int
\exp_after:wN \regex_break_true:w
\fi:
@@ -312,38 +108,34 @@
\exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
\fi:
\fi:
+ \if_num:w \l_regex_case_changed_char_int = \c_max_int
+ \regex_compute_case_changed_char:
+ \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:Npn \regex_item_caseless_geq:n #1
+\cs_new_protected_nopar:Npn \regex_compute_case_changed_char:
{
- \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
+ \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: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_eq:NN \regex_item_equal:n ?
+\cs_new_eq:NN \regex_item_range:nn ?
\cs_new_protected:Npn \regex_item_catcode_aux:
{
"
@@ -362,44 +154,134 @@
\exp_after:wN \use_none:n
\fi:
}
-\cs_new_protected:Npn \regex_item_cs:n #1
+\cs_new_protected:Npn \regex_item_catcode_reverse:nT #1#2
+ { \regex_item_catcode:nT {#1} { \regex_item_reverse:n {#2} } }
+\cs_new_protected:Npn \regex_item_exact:nn #1#2
{
- \int_compare:nNnT \l_regex_current_catcode_int = \c_zero
+ \if_num:w #1 = \l_regex_current_catcode_int
+ \if_num:w #2 = \l_regex_current_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_item_exact_cs:c #1
+ {
+ \int_compare:nNnTF \l_regex_current_catcode_int = \c_zero
{
- \tl_set:Nn \l_regex_internal_regex {#1}
- \bool_set_eq:NN \l_regex_saved_success_bool \g_regex_success_bool
- \exp_args:NNx \regex_match:NnTF \l_regex_internal_regex
+ \str_if_eq:xxTF
{
- \exp_after:wN \exp_after:wN
- \exp_after:wN \cs_to_str:N \l_regex_current_token_tl
+ \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
}
- {
- \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool
- \regex_break_true:w
- }
- { \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool }
+ { #1 }
+ { \regex_break_true:w } { }
}
+ { }
+ }
+\cs_new_protected:Npn \regex_item_cs:n #1
+ {
+ \int_compare:nNnT \l_regex_current_catcode_int = \c_zero
+ {
+ \group_begin:
+ \regex_single_match:
+ \regex_disable_submatches:
+ \regex_build_for_cs:n {#1}
+ \bool_set_eq:NN \l_regex_saved_success_bool \g_regex_success_bool
+ \exp_args:Nx \regex_match:n
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \cs_to_str:N
+ \tex_the:D \tex_toks:D \l_regex_current_pos_int
+ }
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \group_insert_after:N \regex_break_true:w
+ \fi:
+ \bool_gset_eq:NN \g_regex_success_bool \l_regex_saved_success_bool
+ \group_end:
+ }
+ }
+\cs_new_protected_nopar:Npn \regex_prop_d:
+ { \regex_item_caseful_range:nn \c_forty_eight { 57 } } % 0--9
+\cs_new_protected_nopar:Npn \regex_prop_h:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ }
+\cs_new_protected_nopar:Npn \regex_prop_s:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two % space
+ \regex_item_caseful_equal:n \c_nine % tab
+ \regex_item_caseful_equal:n \c_ten % lf
+ \regex_item_caseful_equal:n \c_twelve % ff
+ \regex_item_caseful_equal:n \c_thirteen % cr
+ }
+\cs_new_protected_nopar:Npn \regex_prop_v:
+ { \regex_item_caseful_range:nn \c_ten \c_thirteen } % lf, vtab, ff, cr
+\cs_new_protected_nopar:Npn \regex_prop_w:
+ {
+ \regex_item_caseful_range:nn \c_ninety_seven { 122 } % a--z
+ \regex_item_caseful_range:nn \c_sixty_five { 90 } % A--Z
+ \regex_item_caseful_range:nn \c_forty_eight { 57 } % 0--9
+ \regex_item_caseful_equal:n { 95 } % _
+ }
+\cs_new_protected_nopar:Npn \regex_prop_N:
+ { \regex_item_reverse:n { \regex_item_caseful_equal:n \c_ten } }
+\cs_new_protected_nopar:Npn \regex_posix_alnum:
+ { \regex_posix_alpha: \regex_posix_digit: }
+\cs_new_protected_nopar:Npn \regex_posix_alpha:
+ { \regex_posix_lower: \regex_posix_upper: }
+\cs_new_protected_nopar:Npn \regex_posix_ascii:
+ { \regex_item_caseful_range:nn \c_zero \c_one_hundred_twenty_seven }
+\cs_new_eq:NN \regex_posix_blank: \regex_prop_h:
+\cs_new_protected_nopar:Npn \regex_posix_cntrl:
+ {
+ \regex_item_caseful_range:nn \c_zero { 31 }
+ \regex_item_caseful_equal:n \c_one_hundred_twenty_seven
+ }
+\cs_new_eq:NN \regex_posix_digit: \regex_prop_d:
+\cs_new_protected_nopar:Npn \regex_posix_graph:
+ { \regex_item_caseful_range:nn { 33 } { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_lower:
+ { \regex_item_caseful_range:nn \c_ninety_seven { 122 } }
+\cs_new_protected_nopar:Npn \regex_posix_print:
+ { \regex_item_caseful_range:nn \c_thirty_two { 126 } }
+\cs_new_protected_nopar:Npn \regex_posix_punct:
+ {
+ \regex_item_caseful_range:nn { 33 } { 47 }
+ \regex_item_caseful_range:nn { 58 } { 64 }
+ \regex_item_caseful_range:nn { 91 } { 96 }
+ \regex_item_caseful_range:nn { 123 } { 126 }
+ }
+\cs_new_protected_nopar:Npn \regex_posix_space:
+ {
+ \regex_item_caseful_equal:n \c_thirty_two
+ \regex_item_caseful_range:nn \c_nine \c_thirteen
+ }
+\cs_new_protected_nopar:Npn \regex_posix_upper:
+ { \regex_item_caseful_range:nn \c_sixty_five { 90 } }
+\cs_new_eq:NN \regex_posix_word: \regex_prop_w:
+\cs_new_protected_nopar:Npn \regex_posix_xdigit:
+ {
+ \regex_posix_digit:
+ \regex_item_caseful_range:nn \c_sixty_five { 70 }
+ \regex_item_caseful_range:nn \c_ninety_seven { 102 }
}
\cs_new_protected:Npn \regex_escape_use:nnnn #1#2#3#4
{
- \group_begin:
+ \tl_set_build:Nw \l_regex_internal_a_tl
\cs_set_nopar:Npn \regex_escape_unescaped:N ##1 { #1 }
\cs_set_nopar:Npn \regex_escape_escaped:N ##1 { #2 }
\cs_set_nopar:Npn \regex_escape_raw:N ##1 { #3 }
\int_set:Nn \tex_escapechar:D { 92 }
- \str_gset_other:Nn \g_regex_internal_tl {#4}
- \int_zero:N \l_regex_internal_a_int
- \tl_set:Nx \l_regex_internal_a_tl
+ \str_gset_other:Nn \g_regex_internal_tl { #4 }
+ \tl_set:Nx \l_regex_internal_b_tl
{
\exp_after:wN \regex_escape_loop:N \g_regex_internal_tl
{ break } \prg_break_point:n { }
}
- \use:x
- {
- \group_end:
- \regex_toks_range:nn \c_zero \l_regex_internal_a_int
- \exp_not:o \l_regex_internal_a_tl
- }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \tl_build_end:
+ \l_regex_internal_a_tl
}
\cs_new:Npn \regex_escape_loop:N #1
{
@@ -437,82 +319,96 @@
{ \exp_not:N \regex_escape_raw:N \iow_char:N \^^M }
\cs_new_nopar:cpx { regex_escape_/e:w }
{ \exp_not:N \regex_escape_raw:N \iow_char:N \^^[ }
-\cs_new_nopar:cpn { regex_escape_/x:w } \regex_escape_loop:N
+\cs_new:cpn { regex_escape_/x:w } \regex_escape_loop:N
{
- \if_false: { \fi: }
- \tex_toks:D \l_regex_internal_a_int
- \exp_after:wN { \l_regex_internal_a_tl }
- \int_incr:N \l_regex_internal_a_int
- \int_zero:N \l_regex_current_char_int
- \regex_escape_x_test:N
+ \exp_after:wN \regex_escape_x_end:w
+ \int_value:w "0 \regex_escape_x_test:N
}
-\cs_new_protected:Npx \regex_escape_x_test:N #1
+\cs_new:Npn \regex_escape_x_end:w #1 ;
{
- \exp_not:N \token_if_eq_charcode:NNTF \c_space_token #1
- { \exp_not:N \regex_escape_x_test:N }
+ \int_compare:nNnTF {#1} < \c_two_hundred_fifty_six
{
- \l_regex_current_char_int = "0
- \exp_not:N \token_if_eq_charcode:NNTF \c_lbrace_str #1
- { \exp_not:N \regex_escape_x_braced_loop:N }
- { \exp_not:N \regex_escape_x_unbraced_i:N #1 }
+ \exp_last_unbraced:Nf \regex_escape_raw:N
+ { \str_output_byte:n {#1} }
}
+ { \regex_escape_x_large:n {#1} }
}
-\cs_new:Npn \regex_escape_x_unbraced_i:N #1
- {
- \str_aux_hexadecimal_use:NTF #1
- { \regex_escape_x_unbraced_ii:N }
- { \exp_stop_f: \regex_escape_x_end: #1 }
- }
-\cs_new:Npn \regex_escape_x_unbraced_ii:N #1
+\group_begin:
+ \char_set_catcode_other:n { 0 }
+ \cs_new:Npn \regex_escape_x_large:n #1
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \int_compare:nNnTF {#1} > \c_max_char_int
+ {
+ \msg_kernel_error:nnx { regex } { x-overflow } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N
+ }
+ {
+ \char_set_lccode:nn { \c_zero } {#1}
+ \tl_to_lowercase:n
+ {
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi:
+ \regex_escape_raw:N ^^@
+ \regex_escape_loop:N
+ }
+ }
+ }
+\group_end:
+\cs_new:Npn \regex_escape_x_test:N #1
{
- \token_if_eq_charcode:NNTF \c_space_token #1
- { \regex_escape_x_unbraced_ii:N }
+ \str_if_eq:xxTF {#1} { break } { ; }
{
- \str_aux_hexadecimal_use:NTF #1
- { \exp_stop_f: \regex_escape_x_end: }
- { \exp_stop_f: \regex_escape_x_end: #1 }
+ \if_charcode:w \c_space_token #1
+ \exp_after:wN \regex_escape_x_test:N
+ \else:
+ \exp_after:wN \regex_escape_x_test_ii:N
+ \exp_after:wN #1
+ \fi:
}
}
-\cs_new:Npn \regex_escape_x_braced_loop:N #1
+\cs_new:Npn \regex_escape_x_test_ii:N #1
{
- \token_if_eq_charcode:NNTF \c_space_token #1
- { \regex_escape_x_braced_loop:N }
+ \if_charcode:w \c_lbrace_str #1
+ \exp_after:wN \regex_escape_x_loop:N
+ \else:
+ \str_aux_hexadecimal_use:NTF #1
+ { \exp_after:wN \regex_escape_x_ii:N }
+ { ; \exp_after:wN \regex_escape_loop:N \exp_after:wN #1 }
+ \fi:
+ }
+\cs_new:Npn \regex_escape_x_ii:N #1
+ {
+ \str_if_eq:xxTF {#1} { break } { ; }
{
\str_aux_hexadecimal_use:NTF #1
- { \regex_escape_x_braced_loop:N }
- { \exp_stop_f: \regex_escape_x_braced_end:N #1 }
+ { ; \regex_escape_loop:N }
+ { ; \regex_escape_loop:N #1 }
}
}
-\cs_new_protected:Npx \regex_escape_x_braced_end:N #1
+\cs_new:Npn \regex_escape_x_loop:N #1
{
- \exp_not:N \token_if_eq_charcode:NNTF \c_rbrace_str #1
- { \exp_not:N \regex_escape_x_end: }
+ \str_aux_hexadecimal_use:NTF #1
+ { \regex_escape_x_loop:N }
{
- \msg_kernel_error:nnx { regex } { x-missing-rbrace }
- { \int_use:N \l_regex_current_char_int }
- \exp_not:N \regex_escape_x_end: #1
+ \token_if_eq_charcode:NNTF \c_space_token #1
+ { \regex_escape_x_loop:N }
+ {
+ ;
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #1
+ { \regex_escape_loop:N }
+ {
+ \if_false: { \fi: }
+ \tl_build_one:o \l_regex_internal_b_tl
+ \msg_kernel_error:nn { regex } { x-missing-rbrace } {#1}
+ \tl_set:Nx \l_regex_internal_b_tl
+ { \if_false: } \fi: \regex_escape_loop:N #1
+ }
+ }
}
}
-\group_begin:
- \char_set_catcode_other:N \^^@
- \cs_new_protected_nopar:Npn \regex_escape_x_end:
- {
- \if_num:w \l_regex_current_char_int > \c_max_char_int
- \msg_kernel_error:nnx { regex } { x-overflow }
- { \int_use:N \l_regex_current_char_int }
- \exp_after:wN \use:n
- \else:
- \tex_lccode:D \c_zero \l_regex_current_char_int
- \exp_after:wN \tl_to_lowercase:n
- \fi:
- {
- \tl_set:Nx \l_regex_internal_a_tl
- { \if_false: } \fi:
- \regex_escape_raw:N ^^@
- \regex_escape_loop:N
- }
- }
-\group_end:
\prg_new_conditional:Npnn \regex_char_if_special:N #1 { TF }
{
\if_num:w `#1 < \c_ninety_one
@@ -554,198 +450,368 @@
\fi:
\fi:
}
-\cs_new_nopar:Npn \regex_build_if_in_class:TF
+\int_new:N \l_regex_group_level_int
+\int_new:N \l_regex_mode_int
+\int_new:N \l_regex_catcodes_int
+\int_new:N \l_regex_default_catcodes_int
+\bool_new:N \l_regex_catcodes_bool
+\int_const:Nn \c_regex_catcode_C_int { "1 }
+\int_const:Nn \c_regex_catcode_B_int { "4 }
+\int_const:Nn \c_regex_catcode_E_int { "10 }
+\int_const:Nn \c_regex_catcode_M_int { "40 }
+\int_const:Nn \c_regex_catcode_T_int { "100 }
+\int_const:Nn \c_regex_catcode_P_int { "1000 }
+\int_const:Nn \c_regex_catcode_U_int { "4000 }
+\int_const:Nn \c_regex_catcode_D_int { "10000 }
+\int_const:Nn \c_regex_catcode_S_int { "100000 }
+\int_const:Nn \c_regex_catcode_L_int { "400000 }
+\int_const:Nn \c_regex_catcode_O_int { "1000000 }
+\int_const:Nn \c_regex_catcode_A_int { "4000000 }
+\int_const:Nn \c_regex_all_catcodes_int { "5515155 }
+\cs_new_eq:NN \l_regex_internal_regex \c_regex_no_match_regex
+\seq_new:N \l_regex_show_prefix_seq
+\int_new:N \l_regex_show_lines_int
+\cs_new_protected:Npn \regex_get_digits:NTFw #1#2#3#4#5
+ {
+ \regex_if_raw_digit:NNTF #4 #5
+ { #1 = #5 \regex_get_digits_loop:nw {#2} }
+ { #3 #4 #5 }
+ }
+\cs_new:Npn \regex_get_digits_loop:nw #1#2#3
+ {
+ \regex_if_raw_digit:NNTF #2 #3
+ { #3 \regex_get_digits_loop:nw {#1} }
+ { \scan_stop: #1 #2 #3 }
+ }
+\prg_new_conditional:Npnn \regex_if_raw_digit:NN #1#2 { TF }
+ {
+ \if_meaning:w \regex_compile_raw:N #1
+ \if_int_compare:w \c_one < 1 #2 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_nopar:Npn \regex_if_in_class:TF
{
- \if_int_odd:w \l_regex_build_mode_int
+ \if_int_odd:w \l_regex_mode_int
\exp_after:wN \use_i:nn
\else:
\exp_after:wN \use_ii:nn
\fi:
}
-\cs_new_nopar:Npn \regex_build_if_assertions_forbidden:TF
+\cs_new_nopar:Npn \regex_if_in_cs:TF
{
- \if_int_odd:w \l_regex_build_mode_int
- \exp_after:wN \use_i:nn
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_ii:nn
\else:
- \if_int_compare:w \l_regex_build_mode_int > \c_zero
+ \if_int_compare:w \l_regex_mode_int < \c_zero
\exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
\else:
\exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
\fi:
\fi:
}
-\cs_new_protected_nopar:Npn \regex_build_new_state:
+\cs_new_nopar:Npn \regex_if_in_class_or_catcode:TF
{
- \tex_toks:D \l_regex_max_state_int { \s_stop }
- \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
- \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int
- \int_incr:N \l_regex_max_state_int
+ \if_int_odd:w \l_regex_mode_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \if_int_compare:w \l_regex_mode_int > \c_zero
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
}
-\cs_new_protected:Npn \regex_build_transition:NN #1#2
+\cs_new_nopar:Npn \regex_if_within_catcode:TF
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- { #1 { \int_eval:n { #2 - \l_regex_left_state_int } } }
+ \if_int_compare:w \l_regex_mode_int > \c_zero
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
}
-\cs_new_protected:Npn \regex_build_transitions:NNNN #1#2#3#4
+\cs_new_protected:Npn \regex_chk_c_allowed:T
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- {
- #1 { \int_eval:n { #2 - \l_regex_left_state_int } }
- #3 { \int_eval:n { #4 - \l_regex_left_state_int } }
- }
+ \if_num:w \l_regex_mode_int = \c_zero
+ \exp_after:wN \use:n
+ \else:
+ \if_num:w \l_regex_mode_int = \c_three
+ \exp_after:wN \exp_after:wN \exp_after:wN \use:n
+ \else:
+ \msg_kernel_error:nn { regex } { c-bad-mode }
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_none:n
+ \fi:
+ \fi:
}
-\cs_new_protected_nopar:Npn \regex_build:w
+\cs_new_protected:Npn \regex_mode_quit_c:
{
- \int_set_eq:NN \l_regex_catcodes_default_int \c_regex_catcodes_all_int
- \int_set_eq:NN \l_regex_catcodes_int \l_regex_catcodes_default_int
- \int_set_eq:NN \l_regex_capturing_group_int \c_zero
- \seq_clear:N \l_regex_capturing_group_seq
- \tl_clear:N \l_regex_tests_tl
- \bool_set_true:N \l_regex_tests_bool
- \int_zero:N \l_regex_max_state_int
- \regex_build_new_state:
+ \if_num:w \l_regex_mode_int = \c_two
+ \l_regex_mode_int = \c_zero
+ \else:
+ \if_num:w \l_regex_mode_int = \c_six
+ \l_regex_mode_int = \c_three
+ \fi:
+ \fi:
}
-\cs_new_protected_nopar:Npn \regex_build_end:
+\cs_new_protected_nopar:Npn \regex_compile:w
{
- \regex_build_exit_class:
- \regex_build_exit_groups:
- \regex_toks_put_right:Nx \l_regex_right_state_int
- { \regex_action_success: }
- \int_incr:N \l_regex_capturing_group_int
+ \tl_set_build_x:Nw \l_regex_internal_regex
+ \int_zero:N \l_regex_group_level_int
+ \int_set_eq:NN \l_regex_default_catcodes_int \c_regex_all_catcodes_int
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
}
-\cs_new_protected_nopar:Npn \regex_build_exit_class:
+\cs_new_protected_nopar:Npn \regex_compile_end:
{
- \regex_build_if_in_class:TF
- {
- \msg_kernel_error:nn { regex } { missing-rbrack }
- \use:c { regex_build_]: }
- }
- { }
- }
-\cs_new_protected_nopar:Npn \regex_build_exit_groups:
- {
- \seq_if_empty:NF \l_regex_capturing_group_seq
- {
+ \regex_if_in_class:TF
+ {
+ \msg_kernel_error:nn { regex } { missing-rbrack }
+ \use:c { regex_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_num:w \l_regex_group_level_int > \c_zero
\msg_kernel_error:nnx { regex } { missing-rparen }
- { \seq_length:N \l_regex_capturing_group_seq }
+ { \int_use:N \l_regex_group_level_int }
\prg_replicate:nn
- { \seq_length:N \l_regex_capturing_group_seq }
- { \regex_build_close_aux: \regex_build_group_: }
- }
- }
-\cs_new_protected:Npn \regex_build:n #1
- {
- \regex_build:w
- \int_set_eq:NN \l_regex_build_mode_int \c_zero
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
- { \regex_action_start_wildcard: }
- \regex_build_open_aux:
- \regex_escape_use:nnnn
+ { \l_regex_group_level_int }
{
- \regex_char_if_special:NTF ##1
- \regex_build_special:N \regex_build_raw:N ##1
- }
- {
- \regex_char_if_alphanumeric:NTF ##1
- \regex_build_escaped:N \regex_build_raw:N ##1
+ \tl_build_one:n
+ {
+ \if_false: { \fi: }
+ \if_false: { \fi: } { 1 } { 0 } \c_true_bool
+ }
+ \tl_build_end:
+ \tl_build_one:o \l_regex_internal_regex
}
- { \regex_build_raw:N ##1 }
- { #1 }
- \prg_do_nothing: \prg_do_nothing:
- \prg_do_nothing: \prg_do_nothing:
- \int_compare:nNnT \l_regex_build_mode_int < \c_zero
+ \fi:
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
+ }
+\cs_new_protected:Npn \regex_compile:n #1
+ {
+ \regex_compile:w
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_set_eq:NN \l_regex_mode_int \c_zero
+ \regex_escape_use:nnnn
+ {
+ \regex_char_if_special:NTF ##1
+ \regex_compile_special:N \regex_compile_raw:N ##1
+ }
+ {
+ \regex_char_if_alphanumeric:NTF ##1
+ \regex_compile_escaped:N \regex_compile_raw:N ##1
+ }
+ { \regex_compile_raw:N ##1 }
+ { #1 }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ \int_compare:nNnT \l_regex_mode_int < \c_zero
{
\msg_kernel_error:nn { regex } { c-missing-rbrace }
- \exp_after:wN \regex_build_special:N \c_rbrace_str
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
}
- \seq_put_right:Nn \l_regex_capturing_group_seq {0}
- \regex_build_close_aux: \regex_build_group_:
- \regex_build_end:
+ \regex_compile_end:
}
-\cs_new_protected:Npn \regex_build_special:N #1
+\cs_new_protected:Npn \regex_compile_special:N #1
{
- \cs_if_exist_use:cF { regex_build_#1: }
- { \regex_build_raw:N #1 }
+ \cs_if_exist_use:cF { regex_compile_#1: }
+ { \regex_compile_raw:N #1 }
}
-\cs_new_protected:Npn \regex_build_escaped:N #1
+\cs_new_protected:Npn \regex_compile_escaped:N #1
{
- \cs_if_exist_use:cF { regex_build_/#1: }
- { \regex_build_raw:N #1 }
+ \cs_if_exist_use:cF { regex_compile_/#1: }
+ { \regex_compile_raw:N #1 }
}
-\cs_new_protected:Npn \regex_build_one:n #1
- { \regex_build_one:x { \exp_not:n {#1} } }
-\cs_new_protected:Npn \regex_build_one:x #1
+\cs_new_protected:Npn \regex_compile_one:x #1
{
- \tl_put_right:Nx \l_regex_tests_tl
+ \regex_mode_quit_c:
+ \regex_if_in_class:TF { }
{
- \if_num:w \l_regex_catcodes_int < \c_regex_catcodes_all_int
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ }
+ \tl_build_one:x
+ {
+ \if_num:w \l_regex_catcodes_int < \c_regex_all_catcodes_int
\regex_item_catcode:nT { \int_use:N \l_regex_catcodes_int }
+ { \exp_not:N \exp_not:n {#1} }
\else:
- \exp_after:wN \use:n
+ \exp_not:N \exp_not:n {#1}
\fi:
- {#1}
}
- \int_set_eq:NN \l_regex_catcodes_int \l_regex_catcodes_default_int
- \if_num:w \l_regex_build_mode_int = \c_two
- \l_regex_build_mode_int = \c_zero
- \else:
- \if_num:w \l_regex_build_mode_int = \c_six
- \l_regex_build_mode_int = \c_three
- \fi:
- \fi:
- \if_int_odd:w \l_regex_build_mode_int \else:
- \exp_after:wN \regex_build_one_quantifier:
- \fi:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_if_in_class:TF { } { \regex_compile_quantifier:w }
}
-\cs_new:Npn \regex_tests_action_cost:n #1
+\cs_new_protected:Npn \regex_compile_abort_tokens:n #1
{
- \exp_not:o \l_regex_tests_tl
- \bool_if:NTF \l_regex_tests_bool
- { \regex_break_point:TF { \regex_action_cost:n {#1} } { } }
- { \regex_break_point:TF { } { \regex_action_cost:n {#1} } }
+ \use:x
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \regex_compile_raw:N
+ }
}
-\cs_new_protected:Npn \regex_build_raw_error:N #1
+\cs_generate_variant:Nn \regex_compile_abort_tokens:n { x }
+\cs_new_protected:Npn \regex_compile_quantifier:w #1#2
{
- \if_int_odd:w \l_regex_build_mode_int
- \msg_kernel_error:nnx { regex } { class-bad-escape } {#1}
- \else:
- \msg_kernel_error:nnx { regex } { catcode-bad-escape } {#1}
- \fi:
- \regex_build_raw:N #1
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
+ {
+ \cs_if_exist_use:cF { regex_compile_quantifier_#2:w }
+ { \regex_compile_quantifier_none: #1 #2 }
+ }
+ { \regex_compile_quantifier_none: #1 #2 }
}
-\cs_new_protected:Npn \regex_build_raw:N #1#2#3
+\cs_new_protected:Npn \regex_compile_quantifier_none:
+ { \tl_build_one:n { \if_false: { \fi: } { 1 } { 0 } \c_false_bool } }
+\cs_new_protected:Npn \regex_compile_quantifier_abort:xNN #1#2#3
{
- \regex_build_if_in_class:TF
- { \str_if_eq:nnTF {#2#3} { \regex_build_special:N - } }
- { \use_ii:nn }
- { \regex_class_range:Nw #1 }
+ \regex_compile_quantifier_none:
+ \msg_kernel_warning:nnxx { regex } { invalid-quantifier } {#1} {#3}
+ \regex_compile_abort_tokens:x {#1}
+ #2 #3
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_lazyness:nnNN #1#2#3#4
+ {
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ? }
+ { \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_true_bool } }
{
- \regex_build_one:x { \regex_item_equal:n { \int_value:w `#1 ~ } }
- #2 #3
+ \tl_build_one:n { \if_false: { \fi: } { #1 } { #2 } \c_false_bool }
+ #3 #4
}
}
-\cs_new_protected:Npn \regex_class_range:Nw #1#2#3
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_?:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { 1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_*:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 0 } { -1 } }
+\cs_new_protected_nopar:cpn { regex_compile_quantifier_+:w }
+ { \regex_compile_quantifier_lazyness:nnNN { 1 } { -1 } }
+\cs_new_protected:cpn { regex_compile_quantifier_ \c_lbrace_str :w }
{
- \token_if_eq_meaning:NNTF #2 \regex_build_escaped:N % [
- { \use_i:nn } { \str_if_eq:nnTF { #2#3 } { \regex_build_special:N ] } }
+ \regex_get_digits:NTFw \l_regex_internal_a_int
+ { \regex_compile_quantifier_braced_i:w }
+ { \regex_compile_quantifier_abort:xNN { \c_lbrace_str } }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_i:w #1#2
+ {
+ \prg_case_str:xxn { #1 #2 }
{
- \msg_kernel_warning:nnxx { regex } { range-missing-end } % [
- {#1} { \token_if_eq_charcode:NNF #3 ] { \c_backslash_str } #3 }
- \tl_put_right:Nx \l_regex_tests_tl
+ { \regex_compile_special:N \c_rbrace_str }
{
- \regex_item_equal:n { \int_value:w `#1 ~ }
- \regex_item_equal:n { \int_value:w `- ~ }
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { 0 }
+ }
+ { \regex_compile_special:N , }
+ {
+ \regex_get_digits:NTFw \l_regex_internal_b_int
+ { \regex_compile_quantifier_braced_iii:w }
+ { \regex_compile_quantifier_braced_ii:w }
+ }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_ii:w #1#2
+ {
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \exp_args:No \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int } { -1 }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ { \c_lbrace_str \int_use:N \l_regex_internal_a_int , }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_quantifier_braced_iii:w #1#2
+ {
+ \str_if_eq:xxTF
+ { #1 #2 } { \regex_compile_special:N \c_rbrace_str }
+ {
+ \if_num:w \l_regex_internal_a_int > \l_regex_internal_b_int
+ \msg_kernel_error:nnxx { regex } { backwards-quantifier }
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ \int_zero:N \l_regex_internal_b_int
+ \else:
+ \int_sub:Nn \l_regex_internal_b_int \l_regex_internal_a_int
+ \fi:
+ \exp_args:Noo \regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_regex_internal_a_int }
+ { \int_use:N \l_regex_internal_b_int }
+ }
+ {
+ \regex_compile_quantifier_abort:xNN
+ {
+ \c_lbrace_str
+ \int_use:N \l_regex_internal_a_int ,
+ \int_use:N \l_regex_internal_b_int
+ }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \regex_compile_raw_error:N #1
+ {
+ \msg_kernel_error:nnx { regex } { bad-escape } {#1}
+ \regex_compile_raw:N #1
+ }
+\cs_new_protected:Npn \regex_compile_raw:N #1#2#3
+ {
+ \regex_if_in_class:TF
+ {
+ \str_if_eq:nnTF {#2#3} { \regex_compile_special:N - }
+ { \regex_compile_range:Nw #1 }
+ {
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ #2 #3
}
- #2#3
}
{
+ \regex_compile_one:x
+ { \regex_item_equal:n { \int_value:w `#1 ~ } }
+ #2 #3
+ }
+ }
+\prg_new_protected_conditional:Npnn \regex_if_end_range:NN #1#2 { TF }
+ {
+ \if_meaning:w \regex_compile_raw:N #1
+ \prg_return_true:
+ \else:
+ \if_meaning:w \regex_compile_special:N #1
+ \if_charcode:w ] #2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_compile_range:Nw #1#2#3
+ {
+ \regex_if_end_range:NNTF #2 #3
+ {
\if_num:w `#1 > `#3 \exp_stop_f:
\msg_kernel_error:nnxx { regex } { range-backwards } {#1} {#3}
\else:
- \tl_put_right:Nx \l_regex_tests_tl
+ \tl_build_one:x
{
\if_num:w `#1 = `#3 \exp_stop_f:
\regex_item_equal:n
@@ -756,1088 +822,1125 @@
}
\fi:
}
- }
-\cs_new_protected_nopar:cpx { regex_build_.: }
- {
- \exp_not:N \regex_build_if_in_class:TF
- { \regex_build_raw:N . }
- { \regex_build_one:n \exp_not:c { c_regex_._tl } }
- }
-\tl_map_inline:nn { dDhHsSvVwWN }
- {
- \cs_new_protected_nopar:cpx { regex_build_/#1: }
- { \regex_build_one:n \exp_not:c { c_regex_#1_tl } }
- }
-\cs_new_protected:Npn \regex_build_simple_assertion:nn #1#2
- {
- \regex_build_if_assertions_forbidden:TF {#1}
{
- \regex_build_new_state:
- \regex_toks_put_right:Nx \l_regex_left_state_int
+ \msg_kernel_warning:nnxx { regex } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_one:x
{
- \exp_not:n {#2}
- {
- \regex_action_free:n
- {
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
- }
- }
+ \regex_item_equal:n { \int_value:w `#1 ~ }
+ \regex_item_equal:n { \int_value:w `- ~ }
}
- %^^A todo: add \regex_assertion_quantifier:
+ #2#3
}
}
-\cs_new_protected_nopar:cpn { regex_build_^: }
- {
- \regex_build_simple_assertion:nn { \regex_build_raw:N ^ }
- { \int_compare:nNnT \l_regex_min_index_int = \l_regex_current_index_int }
- }
-\cs_new_protected_nopar:cpn { regex_build_/A: }
+\cs_new_protected_nopar:cpx { regex_compile_.: }
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N A }
- { \int_compare:nNnT \l_regex_min_index_int = \l_regex_current_index_int }
+ \exp_not:N \regex_if_in_class:TF
+ { \regex_compile_raw:N . }
+ { \regex_compile_one:x \exp_not:c { regex_prop_.: } }
}
-\cs_new_protected_nopar:cpn { regex_build_/G: }
+\cs_new_protected_nopar:cpn { regex_prop_.: }
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N G }
- { \int_compare:nNnT \l_regex_start_index_int = \l_regex_current_index_int }
+ \if_num:w \l_regex_current_char_int > - \c_two
+ \exp_after:wN \regex_break_true:w
+ \fi:
}
-\group_begin:
- \char_set_catcode_other:N \$
- \cs_new_protected_nopar:cpn { regex_build_$: } % $
- {
- \regex_build_simple_assertion:nn { \regex_build_raw:N $ } % $
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
- }
-\group_end:
-\cs_new_protected_nopar:cpn { regex_build_/Z: }
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N Z }
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
+ \cs_new_protected_nopar:cpx { regex_compile_/#1: }
+ { \regex_compile_one:x \exp_not:c { regex_prop_#1: } }
+ \cs_new_protected_nopar:cpx { regex_compile_/#2: }
+ {
+ \regex_compile_one:x
+ { \regex_item_reverse:n \exp_not:c { regex_prop_#1: } }
+ }
}
-\cs_new_protected_nopar:cpn { regex_build_/z: }
+\regex_tmp:w d D
+\regex_tmp:w h H
+\regex_tmp:w s S
+\regex_tmp:w v V
+\regex_tmp:w w W
+\cs_new_protected_nopar:cpn { regex_compile_/N: }
+ { \regex_compile_one:x \regex_prop_N: }
+\cs_new_protected:Npn \regex_compile_anchor:NF #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N z }
- { \int_compare:nNnT \l_regex_current_char_int < \c_minus_one }
+ \regex_if_in_class_or_catcode:TF {#2}
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_anchor:N #1 } }
+ }
}
-\cs_new_protected_nopar:cpn { regex_build_/b: }
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N b }
- { \regex_if_word_boundary:T }
+ \cs_new_protected_nopar:cpn { regex_compile_/#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw_error:N #1 } }
}
-\cs_new_protected_nopar:cpn { regex_build_/B: }
+\regex_tmp:w A \l_regex_min_pos_int
+\regex_tmp:w G \l_regex_start_pos_int
+\regex_tmp:w Z \l_regex_max_pos_int
+\regex_tmp:w z \l_regex_max_pos_int
+\cs_set_protected:Npn \regex_tmp:w #1#2
{
- \regex_build_simple_assertion:nn { \regex_build_raw_error:N B }
- { \regex_if_word_boundary:TF { } }
+ \cs_new_protected_nopar:cpn { regex_compile_#1: }
+ { \regex_compile_anchor:NF #2 { \regex_compile_raw:N #1 } }
}
-\cs_new_protected_nopar:Npn \regex_if_word_boundary:TF
+\exp_args:Nx \regex_tmp:w { \iow_char:N \^ } \l_regex_min_pos_int
+\exp_args:Nx \regex_tmp:w { \iow_char:N \$ } \l_regex_max_pos_int
+\cs_new_protected_nopar:cpn { regex_compile_/b: }
{
- \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 { -2 } }
- { \group_end: \c_regex_w_tl }
- \regex_break_point:TF
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N b }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_true_bool { \regex_b_test: } }
+ }
}
-\cs_new_protected_nopar:Npn \regex_if_word_boundary:T
- { \regex_if_word_boundary:TF \use:n \use_none:n }
-\cs_new_protected_nopar:cpn { regex_build_[: }
+\cs_new_protected_nopar:cpn { regex_compile_/B: }
{
- \regex_build_if_in_class:TF
- { \regex_build_raw:N [ }
- { \regex_class_first:NNNN }
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N B }
+ {
+ \tl_build_one:n
+ { \regex_assertion:Nn \c_false_bool { \regex_b_test: } }
+ }
}
-\cs_new_protected:cpn { regex_build_]: }
+\cs_new_protected:cpn { regex_compile_]: }
{
- \regex_build_if_in_class:TF
+ \regex_if_in_class:TF
{
- \if_num:w \l_regex_build_mode_int > \c_sixteen
- \tl_set:Nx \l_regex_tests_tl
- {
- \exp_not:o \l_regex_tests_saved_tl
- \if_num:w \l_regex_catcodes_saved_int < \c_regex_catcodes_all_int
- \regex_item_catcode:nT
- { \int_use:N \l_regex_catcodes_saved_int }
- \else:
- \exp_after:wN \use:n
- \fi:
- {
- \exp_not:o \l_regex_tests_tl
- \bool_if:NF \l_regex_tests_bool
- { \regex_break_point:TF { } \regex_break_true:w }
- }
- }
- \bool_set_eq:NN \l_regex_tests_bool \l_regex_tests_saved_bool
+ \if_num:w \l_regex_mode_int > \c_sixteen
+ \tl_build_one:n { \if_false: { \fi: } }
\fi:
- \tex_advance:D \l_regex_build_mode_int - \c_fifteen
- \tex_divide:D \l_regex_build_mode_int \c_thirteen
- \if_int_odd:w \l_regex_build_mode_int \else:
- \exp_after:wN \regex_build_one_quantifier:
+ \tex_advance:D \l_regex_mode_int - \c_fifteen
+ \tex_divide:D \l_regex_mode_int \c_thirteen
+ \if_int_odd:w \l_regex_mode_int \else:
+ \exp_after:wN \regex_compile_quantifier:w
\fi:
}
- { \regex_build_raw:N ] }
+ { \regex_compile_raw:N ] }
}
-\cs_new_protected:Npn \regex_class_first:NNNN #1#2#3#4
+\cs_new_protected_nopar:cpn { regex_compile_[: }
{
- \l_regex_build_mode_int = \int_value:w \l_regex_build_mode_int 3 ~ %
- \if_num:w \l_regex_build_mode_int > \c_sixteen
- \tl_set_eq:NN \l_regex_tests_saved_tl \l_regex_tests_tl
- \bool_set_eq:NN \l_regex_tests_saved_bool \l_regex_tests_bool
- \int_set_eq:NN \l_regex_catcodes_saved_int \l_regex_catcodes_int
- \int_set_eq:NN \l_regex_catcodes_int \c_regex_catcodes_all_int
- \fi:
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
+ \regex_if_in_class:TF
+ { \regex_compile_class_posix_test:w }
{
- \token_if_eq_charcode:NNTF #2 ^
+ \regex_if_within_catcode:TF
{
- \bool_set_false:N \l_regex_tests_bool % [
- \str_if_eq:nnTF {#3#4} { \regex_build_special:N ] }
- { \regex_build_raw:N }
- }
- { % [
- \token_if_eq_charcode:NNTF #2 ]
- { \regex_build_raw:N #2 }
- { #1 #2 }
+ \exp_after:wN \regex_compile_class_catcode:w
+ \int_use:N \l_regex_catcodes_int ;
}
+ { \regex_compile_class_normal:w }
}
- { #1 #2 }
- #3 #4
- }
-\cs_new_protected:cpn { regex_build_/c: }
- {
- \if_case:w \l_regex_build_mode_int
- \exp_after:wN \regex_build_c_aux:wNN
- \or: \or: \or: \exp_after:wN \regex_build_c_aux:wNN
- \fi:
- \msg_kernel_error:nn { regex } { c-bad-mode }
- \s_stop
}
-\cs_new_protected:Npn \regex_build_c_aux:wNN #1 \s_stop #2#3
+\cs_new_protected_nopar:Npn \regex_compile_class_normal:w
{
- \token_if_eq_meaning:NNTF #2 \regex_build_raw:N
- {
- \cs_if_exist:cTF { c_regex_catcode_#3_int }
- {
- \int_set_eq:Nc \l_regex_catcodes_int { c_regex_catcode_#3_int }
- \l_regex_build_mode_int
- = \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- }
- }
- { \cs_if_exist_use:cF { regex_build_c_#3: } }
- {
- \msg_kernel_error:nnx { regex } { c-missing-category } {#3}
- #2 #3
- }
+ \regex_compile_class:TFNN
+ { \regex_class:NnnnN \c_true_bool }
+ { \regex_class:NnnnN \c_false_bool }
}
-\cs_new_protected:cpn { regex_build_c_ \c_lbrace_str : }
+\cs_new_protected:Npn \regex_compile_class_catcode:w #1;
{
- \group_begin:
- \l_regex_build_mode_int
- = - \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- \regex_build:w
+ \if_int_compare:w \l_regex_mode_int = \c_two
+ \tl_build_one:n
+ { \regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ \fi:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \regex_compile_class:TFNN
+ { \regex_item_catcode:nT {#1} }
+ { \regex_item_catcode_reverse:nT {#1} }
}
-\cs_new_protected:cpn { regex_build_c_[: } #1#2
+\cs_new_protected:Npn \regex_compile_class:TFNN #1#2#3#4
{
- \int_zero:N \l_regex_catcodes_int
- \str_if_eq:nnTF { #1 #2 } { \regex_build_special:N ^ }
+ \l_regex_mode_int = \int_value:w \l_regex_mode_int 3 \exp_stop_f:
+ \str_if_eq:nnTF { #3 #4 } { \regex_compile_special:N ^ }
{
- \bool_set_false:N \l_regex_catcodes_bool
- \regex_build_c_lbrack_loop:NN
+ \tl_build_one:n { #2 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN
}
{
- \bool_set_true:N \l_regex_catcodes_bool
- \regex_build_c_lbrack_loop:NN
- #1#2
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_compile_class_ii:NN #3 #4
}
}
-\cs_new_protected:Npn \regex_build_c_lbrack_loop:NN #1#2
+\cs_new_protected:Npn \regex_compile_class_ii:NN #1#2
{
- \token_if_eq_meaning:NNTF #1 \regex_build_raw:N
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_raw:N #2 }
+ { #1 #2 }
+ }
+\cs_new_protected:Npn \regex_compile_class_posix_test:w #1#2
+ {
+ \token_if_eq_meaning:NNT \regex_compile_special:N #1
{
- \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ \prg_case_str:nnn { #2 }
{
- \exp_args:Nc \regex_build_c_lbrack_add:N
- { c_regex_catcode_#2_int }
- \regex_build_c_lbrack_loop:NN
+ : { \regex_compile_class_posix:NNNNw }
+ = { \msg_kernel_warning:nnx { regex } { posix-unsupported } { = } }
+ . { \msg_kernel_warning:nnx { regex } { posix-unsupported } { . } }
}
+ { }
}
- { % [
- \token_if_eq_charcode:NNTF #2 ]
- { \regex_build_c_lbrack_end: }
- }
- {
- \msg_kernel_error:nnx { regex } { c-missing-rbrack } {#2}
- \regex_build_c_lbrack_end:
- }
+ \regex_compile_raw:N [ #1 #2
}
-\cs_new_protected_nopar:Npn \regex_build_c_lbrack_end:
+\cs_new_protected:Npn \regex_compile_class_posix:NNNNw #1#2#3#4#5#6
{
- \l_regex_build_mode_int
- = \if_case:w \l_regex_build_mode_int \c_two \else: \c_six \fi:
- \if_meaning:w \c_false_bool \l_regex_catcodes_bool
- \int_set:Nn \l_regex_catcodes_int
- { \c_regex_catcodes_all_int - \l_regex_catcodes_int }
- \fi:
+ \str_if_eq:nnTF { #5 #6 } { \regex_compile_special:N ^ }
+ {
+ \bool_set_false:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w
+ }
+ {
+ \bool_set_true:N \l_regex_internal_bool
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_class_posix_loop:w #5 #6
+ }
}
-\cs_new_protected:Npn \regex_build_c_lbrack_add:N #1
+\cs_new:Npn \regex_compile_class_posix_loop:w #1#2
{
- \if_int_odd:w \int_eval:w \l_regex_catcodes_int / #1 \int_eval_end:
- \else:
- \tex_advance:D \l_regex_catcodes_int #1
- \fi:
+ \token_if_eq_meaning:NNTF \regex_compile_raw:N #1
+ { #2 \regex_compile_class_posix_loop:w }
+ { \if_false: { \fi: } \regex_compile_class_posix_end:w #1 #2 }
}
-\cs_new_protected:cpn { regex_build_ \c_rbrace_str : }
+\cs_new_protected:Npn \regex_compile_class_posix_end:w #1#2#3#4
{
- \int_compare:nNnTF \l_regex_build_mode_int < \c_zero
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_special:N : \regex_compile_special:N ] }
{
- \regex_build_exit_class:
- \regex_build_exit_groups:
- \regex_build_escaped:N Z
- \regex_build_end:
- \use:x
+ \cs_if_exist:cTF { regex_posix_ \l_regex_internal_a_tl : }
{
- \group_end:
- \regex_build_one:n
- {
- \regex_item_cs:n
- { \regex_set_aux:N \l_regex_internal_regex }
- }
+ \regex_compile_one:x
+ {
+ \bool_if:NF \l_regex_internal_bool \regex_item_reverse:n
+ \exp_not:c { regex_posix_ \l_regex_internal_a_tl : }
+ }
}
- }
- { \exp_after:wN \regex_build_raw:N \c_rbrace_str }
- }
-\cs_new_protected:Npn \regex_build_quantifier:w #1#2
- {
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
- {
- \cs_if_exist_use:cF { regex_build_quantifier_#2:w }
{
- \regex_build_quantifier_end:nn { } { }
- #1 #2
+ \msg_kernel_warning:nnx { regex } { posix-unknown }
+ { \l_regex_internal_a_tl }
+ \regex_compile_abort_tokens:x
+ {
+ [: \bool_if:NF \l_regex_internal_bool { ^ }
+ \l_regex_internal_a_tl :]
+ }
}
}
{
- \regex_build_quantifier_end:nn { } { }
- #1 #2
+ \msg_kernel_error:nnxx { regex } { posix-missing-close }
+ { [: \l_regex_internal_a_tl } { #2 #4 }
+ \regex_compile_abort_tokens:x { [: \l_regex_internal_a_tl }
+ #1 #2 #3 #4
}
}
-\cs_new_protected_nopar:cpn { regex_build_quantifier_?:w }
- { \regex_build_quantifier_aux:nnNN { } { ? } }
-\cs_new_protected_nopar:cpn { regex_build_quantifier_*:w }
- { \regex_build_quantifier_aux:nnNN { } { * } }
-\cs_new_protected_nopar:cpn { regex_build_quantifier_+:w }
- { \regex_build_quantifier_aux:nnNN { } { + } }
-\cs_new_protected:Npn \regex_build_quantifier_aux:nnNN #1#2#3#4
+\cs_new_protected:Npn \regex_compile_group_begin:N #1
{
- \str_if_eq:nnTF { #3 #4 } { \regex_build_special:N ? }
- { \regex_build_quantifier_end:nn { #2 #4 } {#1} }
- {
- \regex_build_quantifier_end:nn { #2 } {#1}
- #3 #4
- }
+ \tl_build_one:n { #1 { \if_false: } \fi: }
+ \regex_mode_quit_c:
+ \tl_set_build:Nw \l_regex_internal_regex
+ \int_set_eq:NN \l_regex_default_catcodes_int \l_regex_catcodes_int
+ \int_incr:N \l_regex_group_level_int
+ \tl_build_one:n { \regex_branch:n { \if_false: } \fi: }
}
-\cs_new_protected_nopar:cpn { regex_build_quantifier_ \c_lbrace_str :w }
- { \regex_get_digits:nw { \regex_build_quantifier_lbrace:n } }
-\cs_new_protected:Npn \regex_build_quantifier_lbrace:n #1
+\cs_new_protected:Npn \regex_compile_group_end:
{
- \tl_if_empty:nTF {#1}
- {
- \regex_build_quantifier_end:nn { } { }
- \exp_after:wN \regex_build_raw:N \c_lbrace_str
- }
- { \regex_build_quantifier_lbrace:nw {#1} }
+ \if_num:w \l_regex_group_level_int > \c_zero
+ \tl_build_one:n { \if_false: { \fi: } }
+ \tl_build_end:
+ \int_set_eq:NN \l_regex_catcodes_int \l_regex_default_catcodes_int
+ \tl_build_one:o \l_regex_internal_regex
+ \exp_after:wN \regex_compile_quantifier:w
+ \else:
+ \msg_kernel_warning:nn { regex } { extra-rparen }
+ \exp_after:wN \regex_compile_raw:N \exp_after:wN )
+ \fi:
+ }
+\cs_new_protected_nopar:cpn { regex_compile_(: }
+ {
+ \regex_if_in_class:TF { \regex_compile_raw:N ( }
+ { \regex_compile_lparen:w }
}
-\cs_new_protected:Npx \regex_build_quantifier_lbrace:nw #1#2#3
+\cs_new_protected:Npn \regex_compile_lparen:w #1#2#3#4
{
- \exp_not:N \prg_case_str:nnn { #2 #3 }
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ? }
{
- { \exp_not:N \regex_build_special:N , }
+ \cs_if_exist_use:cF
+ { regex_compile_special_group_\token_to_str:N #4 :w }
{
- \exp_not:N \regex_get_digits:nw
- { \exp_not:N \regex_build_quantifier_lbrace:nnw {#1} }
+ \msg_kernel_warning:nnx { regex } { special-group-unknown }
+ { (? \token_to_str:N #4 }
+ \regex_compile_group_begin:N \regex_group:nnnN
+ \regex_compile_raw:N ? #3 #4
}
- { \exp_not:N \regex_build_special:N \c_rbrace_str }
- { \exp_not:N \regex_build_quantifier_end:nn {n} { {#1} } }
}
{
- \exp_not:N \regex_build_quantifier_end:nn { } { }
- \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2
+ \regex_compile_group_begin:N \regex_group:nnnN
+ #1 #2 #3 #4
}
}
-\cs_new_protected:Npn \regex_build_quantifier_lbrace:nnw #1#2#3#4
+\cs_new_protected_nopar:cpn { regex_compile_|: }
{
- \str_if_eq:xxTF
- { \exp_not:n {#3#4} }
- { \exp_not:N \regex_build_special:N \c_rbrace_str }
+ \regex_if_in_class:TF { \regex_compile_raw:N | }
{
- \tl_if_empty:nTF {#2}
- { \regex_build_quantifier_aux:nnNN { {#1} } { n* } }
- { \regex_build_quantifier_aux:nnNN { {#1} {#2} } { nn } }
+ \tl_build_one:n
+ { \if_false: { \fi: } \regex_branch:n { \if_false: } \fi: }
}
- {
- \regex_build_quantifier_end:nn { } { }
- \use:x
- {
- \exp_args:No \tl_map_function:nN
- { \c_lbrace_str #1 , #2 }
- \regex_build_raw:N
- }
- #3 #4
- }
- }
-\cs_new_protected:Npn \regex_build_quantifier_end:nn #1#2
- {
- \use:c { regex_build_ \l_regex_one_or_group_tl _ #1 : } #2
- \tl_clear:N \l_regex_tests_tl
- \bool_set_true:N \l_regex_tests_bool
}
-\cs_new_protected_nopar:Npn \regex_build_one_quantifier:
+\cs_new_protected_nopar:cpn { regex_compile_): }
{
- \tl_set:Nx \l_regex_one_or_group_tl { one }
- \regex_build_quantifier:w
+ \regex_if_in_class:TF { \regex_compile_raw:N ) }
+ { \regex_compile_group_end: }
}
-\cs_new_protected:Npn \regex_build_one_n: #1
+\cs_new_protected_nopar:cpn { regex_compile_special_group_::w }
+ { \regex_compile_group_begin:N \regex_group_no_capture:nnnN }
+\cs_new_protected_nopar:cpn { regex_compile_special_group_|:w }
+ { \regex_compile_group_begin:N \regex_group_resetting:nnnN }
+\cs_new_protected:Npn \regex_compile_special_group_i:w #1#2
{
- \prg_replicate:nn {#1}
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ) }
{
- \regex_build_transition:NN
- \regex_tests_action_cost:n \l_regex_right_state_int
- }
- }
-\cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n:
-\cs_new_protected_nopar:Npn \regex_build_one_:
- { \regex_build_one_n: \c_one }
-\cs_new_protected:cpn { regex_build_one_n*: } #1
- {
- \int_compare:nNnTF {#1} = \c_zero
- {
- \regex_build_transitions:NNNN
- \regex_tests_action_cost:n \l_regex_left_state_int
- \regex_action_free:n \l_regex_right_state_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseless_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseless_range:nn }
}
{
- \regex_build_one_n: {#1}
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_internal_a_int
- \regex_action_free:n \l_regex_right_state_int
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?i #2 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N i
+ #1 #2
}
}
-\cs_new_protected:cpn { regex_build_one_n*?: } #1
+\cs_new_protected_nopar:cpn { regex_compile_special_group_-:w } #1#2#3#4
{
- \int_compare:nNnTF {#1} = \c_zero
- {
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_right_state_int
- \regex_tests_action_cost:n \l_regex_left_state_int
- }
+ \str_if_eq:nnTF { #1 #2 #3 #4 }
+ { \regex_compile_raw:N i \regex_compile_special:N ) }
{
- \regex_build_one_n: {#1}
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_right_state_int
- \regex_action_free:n \l_regex_internal_a_int
+ \cs_set_nopar:Npn \regex_item_equal:n { \regex_item_caseful_equal:n }
+ \cs_set_nopar:Npn \regex_item_range:nn { \regex_item_caseful_range:nn }
}
- }
-\cs_new_protected_nopar:cpx { regex_build_one_*: }
- { \exp_not:c { regex_build_one_n*: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_one_*?: }
- { \exp_not:c { regex_build_one_n*?: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_one_+: }
- { \exp_not:c { regex_build_one_n*: } \c_one }
-\cs_new_protected_nopar:cpx { regex_build_one_+?: }
- { \exp_not:c { regex_build_one_n*?: } \c_one }
-\cs_new_protected_nopar:Npn \regex_build_one_nn:
- {
- \regex_build_one_nn_aux:nnnn { }
{
- \regex_build_transitions:NNNN
- \regex_tests_action_cost:n \l_regex_right_state_int
- \regex_action_free:n \l_regex_internal_a_int
+ \msg_kernel_warning:nnx { regex } { unknown-option } { (?-#2#4 }
+ \regex_compile_raw:N (
+ \regex_compile_raw:N ?
+ \regex_compile_raw:N -
+ #1 #2 #3 #4
}
}
-\cs_new_protected_nopar:cpn { regex_build_one_nn?: }
+\cs_new_protected:cpn { regex_compile_/c: }
+ { \regex_chk_c_allowed:T { \regex_compile_c_test:NN } }
+\cs_new_protected:Npn \regex_compile_c_test:NN #1#2
{
- \regex_build_one_nn_aux:nnnn {?}
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
{
- \regex_build_transitions:NNNN
- \regex_action_free:n \l_regex_internal_a_int
- \regex_tests_action_cost:n \l_regex_right_state_int
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \int_set_eq:Nc \l_regex_catcodes_int { c_regex_catcode_#2_int }
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ }
}
+ { \cs_if_exist_use:cF { regex_compile_c_#2:w } }
+ {
+ \msg_kernel_error:nnx { regex } { c-missing-category } {#2}
+ #1 #2
+ }
}
-\cs_new_protected:Npn \regex_build_one_nn_aux:nnnn #1#2#3#4
+\cs_new_protected:cpn { regex_compile_c_[:w } #1#2
{
- \regex_build_one_n: {#3}
- \int_compare:nNnTF {#3} > {#4}
+ \l_regex_mode_int
+ = \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
+ \int_zero:N \l_regex_catcodes_int
+ \str_if_eq:nnTF { #1 #2 } { \regex_compile_special:N ^ }
{
- \msg_kernel_error:nnxxx
- { regex } { quantifier-backwards } {#3} {#4} {#1}
+ \bool_set_false:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
}
{
- \int_set:Nn \l_regex_internal_a_int
- { \l_regex_right_state_int + #4 - #3 }
- \prg_replicate:nn { #4 - #3 } { #2 }
+ \bool_set_true:N \l_regex_catcodes_bool
+ \regex_compile_c_lbrack_loop:NN
+ #1 #2
}
}
-\cs_new_protected_nopar:cpn { regex_build_one_?: }
- { \regex_build_one_nn: \c_zero \c_one }
-\cs_new_protected_nopar:cpx { regex_build_one_??: }
- { \exp_not:c { regex_build_one_nn?: } \c_zero \c_one }
-\cs_new_protected:cpn { regex_build_(: } #1#2
+\cs_new_protected:Npn \regex_compile_c_lbrack_loop:NN #1#2
{
- \regex_build_if_in_class:TF
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
{
- \regex_build_raw:N (
- #1 #2
+ \cs_if_exist:cTF { c_regex_catcode_#2_int }
+ {
+ \exp_args:Nc \regex_compile_c_lbrack_add:N
+ { c_regex_catcode_#2_int }
+ \regex_compile_c_lbrack_loop:NN
+ }
}
{
- \str_if_eq:nnTF { #1 #2 } { \regex_build_special:N ? }
- { \regex_build_special_group:NN }
+ \token_if_eq_charcode:NNTF #2 ]
+ { \regex_compile_c_lbrack_end: }
+ }
{
- \int_incr:N \l_regex_capturing_group_int
- \regex_seq_push_int:NN
- \l_regex_capturing_group_seq \l_regex_capturing_group_int
- \regex_build_open_aux:
+ \msg_kernel_error:nnx { regex } { c-missing-rbrack } {#2}
+ \regex_compile_c_lbrack_end:
#1 #2
}
- }
}
-\cs_new_protected_nopar:Npn \regex_build_open_aux:
+\cs_new_protected:Npn \regex_compile_c_lbrack_add:N #1
{
- \regex_build_new_state:
- \regex_seq_push_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_push_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \bool_if:NTF \l_regex_caseless_bool
- { \seq_push:Nn \l_regex_end_group_seq \regex_build_caseless: }
- { \seq_push:Nn \l_regex_end_group_seq \regex_build_caseful: }
- \seq_push:Nn \l_regex_end_alternation_seq { }
- \regex_build_begin_alternation:
+ \if_int_odd:w \int_eval:w \l_regex_catcodes_int / #1 \int_eval_end:
+ \else:
+ \tex_advance:D \l_regex_catcodes_int #1
+ \fi:
}
-\cs_new_protected_nopar:cpn { regex_build_|: }
+\cs_new_protected_nopar:Npn \regex_compile_c_lbrack_end:
{
- \regex_build_if_in_class:TF { \regex_build_raw:N | }
- {
- \regex_build_end_alternation:
- \regex_build_begin_alternation:
- }
+ \if_meaning:w \c_false_bool \l_regex_catcodes_bool
+ \int_set:Nn \l_regex_catcodes_int
+ { \c_regex_all_catcodes_int - \l_regex_catcodes_int }
+ \fi:
}
-\cs_new_protected_nopar:cpn { regex_build_): }
+\cs_new_protected_nopar:cpn { regex_compile_c_ \c_lbrace_str :w }
{
- \regex_build_if_in_class:TF { \regex_build_raw:N ) }
- {
- \seq_if_empty:NTF \l_regex_capturing_group_seq
- { \msg_kernel_error:nn { regex } { extra-rparen } }
- {
- \regex_build_close_aux:
- \regex_build_group_quantifier:
- }
- }
+ \regex_compile:w
+ \regex_disable_submatches:
+ \l_regex_mode_int
+ = - \if_case:w \l_regex_mode_int \c_two \else: \c_six \fi:
}
-\cs_new_protected_nopar:Npn \regex_build_close_aux:
+\cs_new_protected:cpn { regex_compile_ \c_rbrace_str : }
{
- \regex_build_end_alternation:
- \regex_seq_pop_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_pop_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \regex_seq_pop_use:N \l_regex_end_group_seq
- \seq_pop:NN \l_regex_end_alternation_seq \l_regex_internal_a_tl
+ \regex_if_in_cs:TF
+ {
+ \regex_compile_end:
+ \regex_compile_one:x
+ { \regex_item_cs:n { \exp_not:o \l_regex_internal_regex } }
+ }
+ { \exp_after:wN \regex_compile_raw:N \c_rbrace_str }
}
-\cs_new_protected_nopar:Npn \regex_build_begin_alternation:
+\cs_new_protected:cpn { regex_compile_/u: } #1#2
{
- \regex_build_new_state:
- \regex_seq_get_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_toks_put_right:Nx \l_regex_left_state_int
+ \regex_if_in_class_or_catcode:TF
+ { \regex_compile_raw_error:N u #1 #2 }
{
- \regex_action_free:n
+ \str_if_eq:xxTF {#1#2} { \regex_compile_special:N \c_lbrace_str }
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
+ \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
+ \regex_compile_u_loop:NN
+ }
+ {
+ \msg_kernel_error:nn { regex } { u-missing-lbrace }
+ \regex_compile_raw:N u #1 #2
}
}
}
-\cs_new_protected_nopar:Npn \regex_build_end_alternation:
+\cs_new:Npn \regex_compile_u_loop:NN #1#2
{
- \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
- \regex_seq_get_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \regex_toks_put_right:Nx \l_regex_left_state_int
+ \token_if_eq_meaning:NNTF #1 \regex_compile_raw:N
+ { #2 \regex_compile_u_loop:NN }
{
- \regex_action_free:n
+ \token_if_eq_meaning:NNTF #1 \regex_compile_special:N
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_left_state_int }
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #2
+ { \if_false: { \fi: } \regex_compile_u_end: }
+ { #2 \regex_compile_u_loop:NN }
+ }
+ {
+ \if_false: { \fi: }
+ \msg_kernel_error:nnx { regex } { u-missing-rbrace } {#2}
+ \regex_compile_u_end:
+ #1 #2
}
}
- \regex_seq_get_use:N \l_regex_end_alternation_seq
}
-\cs_new_protected:Npn \regex_build_special_group:NN #1#2
+\cs_new_protected:Npn \regex_compile_u_end:
+ {
+ \tl_set:Nv \l_regex_internal_a_tl { \l_regex_internal_a_tl }
+ \if_num:w \l_regex_mode_int = \c_zero
+ \regex_compile_u_not_cs:
+ \else:
+ \regex_compile_u_in_cs:
+ \fi:
+ }
+\cs_new_protected:Npn \regex_compile_u_in_cs:
{
- \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : }
+ \exp_args:NNo \str_gset_other:Nn \g_regex_internal_tl
+ { \l_regex_internal_a_tl }
+ \tl_build_one:x
{
- \msg_kernel_error:nnx { regex } { special-group-unknown }
- { (? \token_to_str:N #2 } %)
- \regex_build_special:N ( % )
- \regex_build_raw:N ?
- #1 #2
+ \tl_map_function:NN \g_regex_internal_tl
+ \regex_compile_u_in_cs_aux:n
}
}
-\cs_new_protected_nopar:cpn { regex_build_special_group_:: }
+\cs_new:Npn \regex_compile_u_in_cs_aux:n #1
{
- \regex_seq_push_int:NN \l_regex_capturing_group_seq \c_minus_one
- \regex_build_open_aux:
+ \regex_class:NnnnN \c_true_bool
+ { \regex_item_caseful_equal:n { \int_value:w `#1 } }
+ { 1 } { 0 } \c_false_bool
}
-\cs_new_protected_nopar:cpn { regex_build_special_group_|: }
+\cs_new_protected:Npn \regex_compile_u_not_cs:
{
- \regex_seq_push_int:NN \l_regex_capturing_group_seq \c_minus_one
- \regex_build_new_state:
- \regex_seq_push_int:NN \l_regex_left_state_seq \l_regex_left_state_int
- \regex_seq_push_int:NN \l_regex_right_state_seq \l_regex_right_state_int
- \seq_push:Nx \l_regex_end_alternation_seq
+ \exp_args:No \tl_analysis_map_inline:nn { \l_regex_internal_a_tl }
{
- \exp_not:N \int_compare:nNnT
- \l_regex_capturing_group_int
- > \l_regex_capturing_group_max_int
+ \tl_build_one:n
{
- \int_set_eq:NN
- \l_regex_capturing_group_max_int
- \l_regex_capturing_group_int
+ \regex_class:NnnnN \c_true_bool
+ {
+ \if_num:w "##2 = \c_zero
+ \regex_item_exact_cs:c { \exp_after:wN \cs_to_str:N ##1 }
+ \else:
+ \regex_item_exact:nn { \int_value:w "##2 } { ##3 }
+ \fi:
+ }
+ { 1 } { 0 } \c_false_bool
}
- \int_set:Nn \l_regex_capturing_group_int
- { \int_use:N \l_regex_capturing_group_int }
}
- \seq_push:Nx \l_regex_end_group_seq
- {
- \bool_if:NTF \l_regex_caseless_bool
- \regex_build_caseless:
- \regex_build_caseful:
- \int_set_eq:NN
- \l_regex_capturing_group_int
- \l_regex_capturing_group_max_int
- }
- \regex_build_begin_alternation:
}
-\cs_new_protected_nopar:Npn \regex_build_special_group_i:
+\cs_new_protected_nopar:cpn { regex_compile_/K: }
{
- \regex_build_options:NNN +
- \regex_build_raw:N i
+ \int_compare:nNnTF \l_regex_mode_int = \c_zero
+ { \tl_build_one:n { \regex_command_K: } }
+ { \regex_compile_raw_error:N K }
}
-\cs_new_protected_nopar:cpn { regex_build_special_group_-: }
+\cs_new_protected:Npn \regex_show_aux:Nx #1#2
{
- \regex_build_options:NNN -
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \cs_set_protected_nopar:Npn \regex_branch:n
+ {
+ \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \regex_show_one:n { +-branch }
+ \seq_put_right:No \l_regex_show_prefix_seq \l_regex_internal_a_tl
+ \use:n
+ }
+ \cs_set_protected_nopar:Npn \regex_group:nnnN
+ { \regex_show_group_aux:nnnnN { } }
+ \cs_set_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_show_group_aux:nnnnN { ~(no~capture) } }
+ \cs_set_protected_nopar:Npn \regex_group_resetting:nnnN
+ { \regex_show_group_aux:nnnnN { ~(resetting) } }
+ \cs_set_eq:NN \regex_class:NnnnN \regex_show_class:NnnnN
+ \cs_set_protected_nopar:Npn \regex_command_K:
+ { \regex_show_one:n { reset~match~start~(\iow_char:N\\K) } }
+ \cs_set_protected:Npn \regex_assertion:Nn ##1##2
+ { \regex_show_one:n { \bool_if:NF ##1 { negative~ } assertion:~##2 } }
+ \cs_set_nopar:Npn \regex_b_test: { word~boundary }
+ \cs_set_eq:NN \regex_anchor:N \regex_show_anchor_to_str:N
+ \cs_set_protected:Npn \regex_item_caseful_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1} } }
+ \cs_set_protected:Npn \regex_item_caseful_range:nn ##1##2
+ { \regex_show_one:n { range~[\int_eval:n{##1}, \int_eval:n{##2}] } }
+ \cs_set_protected:Npn \regex_item_caseless_equal:n ##1
+ { \regex_show_one:n { char~code~\int_eval:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \regex_item_caseless_range:nn ##1##2
+ {
+ \regex_show_one:n
+ { Range~[\int_eval:n{##1}, \int_eval:n{##2}]~(caseless) }
+ }
+ \cs_set_protected:Npn \regex_item_catcode:nT
+ { \regex_show_item_catcode:NnT \c_true_bool }
+ \cs_set_protected:Npn \regex_item_catcode_reverse:nT
+ { \regex_show_item_catcode:NnT \c_false_bool }
+ \cs_set_protected:Npn \regex_item_reverse:n
+ { \regex_show_scope:nn { Reversed~match } }
+ \cs_set_protected:Npn \regex_item_exact:nn ##1##2
+ { \regex_show_one:n { char~##2,~catcode~##1 } }
+ \cs_set_protected:Npn \regex_item_exact_cs:c ##1
+ { \regex_show_one:n { control~sequence~\iow_char:N\\##1 } }
+ \cs_set_protected:Npn \regex_item_cs:n
+ { \regex_show_scope:nn { control~sequence } }
+ \cs_set:cpn { regex_prop_.: } { \regex_show_one:n { any~token } }
+ \seq_clear:N \l_regex_show_prefix_seq
+ \regex_show_push:n { ~ }
+ #1
+ \tl_build_end:
+ \msg_aux_show:x { > Compiled~regex~#2: \l_regex_internal_a_tl }
}
-\cs_new_protected:Npn \regex_build_options:NNN #1#2#3
+\cs_new_protected:Npn \regex_show_one:n #1
{
- \token_if_eq_meaning:NNTF \regex_build_raw:N #2
- {
- \cs_if_exist_use:cF { regex_build_option_#1#3: }
- { \msg_kernel_error:nnx { regex } { unknown-option } { #3 } }
- \regex_build_options:NNN #1
- }
- {
- \prg_case_str:nnn { #3 }
- { % (
- { ) } { }
- { - } { \regex_build_options:NNN - }
- }
- { \msg_kernel_error:nnx { regex } { invalid-in-option } { #3 } }
- }
+ \int_incr:N \l_regex_show_lines_int
+ \tl_build_one:x
+ { \iow_newline: \seq_use:N \l_regex_show_prefix_seq #1 }
}
-\cs_new_protected_nopar:cpn { regex_build_option_+i: }
+\cs_new_protected:Npn \regex_show_push:n #1
+ { \seq_put_right:Nx \l_regex_show_prefix_seq { #1 ~ } }
+\cs_new_protected:Npn \regex_show_pop:
+ { \seq_pop_right:NN \l_regex_show_prefix_seq \l_regex_internal_a_tl }
+\cs_new_protected:Npn \regex_show_scope:nn #1#2
{
- \regex_build_caseless:
- \cs_set_eq:NN \regex_match_loop_case_hook:
- \regex_match_loop_caseless_hook:
+ \regex_show_one:n {#1}
+ \regex_show_push:n { ~ }
+ #2
+ \regex_show_pop:
}
-\cs_new_protected_nopar:cpn { regex_build_option_-i: }
- { \regex_build_caseful: }
-\cs_new_protected_nopar:Npn \regex_build_group_quantifier:
+\cs_new_protected:Npn \regex_show_group_aux:nnnnN #1#2#3#4#5
{
- \tl_set:Nn \l_regex_one_or_group_tl { group }
- \regex_build_quantifier:w
+ \regex_show_one:n { ,-group~begin #1 }
+ \regex_show_push:n { | }
+ \use_ii:nn #2
+ \regex_show_pop:
+ \regex_show_one:n
+ { `-group~end \regex_msg_repeated:nnN {#3} {#4} #5 }
}
-\cs_new_protected:Npn \regex_build_group_submatches:NN #1#2
+\cs_set:Npn \regex_show_class:NnnnN #1#2#3#4#5
{
- \seq_pop:NN \l_regex_capturing_group_seq \l_regex_internal_a_tl
- \int_compare:nNnF { \l_regex_internal_a_tl } < \c_zero
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_show_lines_int
+ \regex_show_push:n {~}
+ #2
+ \exp_last_unbraced:Nf
+ \prg_case_int:nnn { \l_regex_show_lines_int }
{
- \regex_toks_put_left:Nx #1
- { \regex_action_submatch:n { \l_regex_internal_a_tl < } }
- \regex_toks_put_left:Nx #2
- { \regex_action_submatch:n { \l_regex_internal_a_tl > } }
- }
- }
-\cs_new_protected_nopar:Npn \regex_build_group_qs_aux:
- {
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \regex_build_new_state:
- \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_internal_a_int
- \regex_toks_put_left:Nx \l_regex_right_state_int
- {
- \int_sub:Nn \l_regex_current_state_int
+ {0}
{
- \int_eval:n % ^^A here we lie!
- { \l_regex_right_state_int - \l_regex_internal_a_int }
+ \tl_build_end:
+ \regex_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
}
- }
- \use:x
- {
- \tex_toks:D \l_regex_internal_a_int
+ {1}
{
- \s_stop
- \regex_action_free:n
+ \tl_build_end:
+ \bool_if:NTF #1
{
- \int_eval:n
- { \l_regex_right_state_int - \l_regex_internal_a_int }
+ #2
+ \tl_build_one:n { \regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+ {
+ \regex_show_one:n
+ { Don't~match~\regex_msg_repeated:nnN {#3} {#4} #5 }
+ \tl_build_one:o \l_regex_internal_a_tl
}
}
}
- \regex_build_group_submatches:NN
- \l_regex_right_state_int \l_regex_left_state_int
+ {
+ \tl_build_end:
+ \regex_show_one:n
+ {
+ \bool_if:NTF #1 { M } { Don't~m } atch
+ \regex_msg_repeated:nnN {#3} {#4} #5
+ }
+ \tl_build_one:o \l_regex_internal_a_tl
+ }
}
-\cs_new_protected:Npn \regex_build_group_qs_aux:NN #1#2
+\cs_new:Npn \regex_show_anchor_to_str:N #1
{
- \regex_build_group_qs_aux:
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \regex_build_transition:NN \regex_action_free:n #1
- #2 \l_regex_internal_a_int
+ anchor~at~
+ \prg_case_str:nnn { #1 }
{
- \regex_action_free:n
- { \int_eval:n { \l_regex_right_state_int - \l_regex_internal_a_int } }
+ { \l_regex_min_pos_int } { start~(\iow_char:N\\A) }
+ { \l_regex_start_pos_int } { start~of~match~(\iow_char:N\\G) }
+ { \l_regex_max_pos_int } { end~(\iow_char:N\\Z) }
}
+ { <error:~'#1'~not~recognized> }
}
-\cs_new_protected_nopar:cpn { regex_build_group_?: }
+\cs_new_protected:Npn \regex_show_item_catcode:NnT #1#2
{
- \regex_build_group_qs_aux:NN
- \l_regex_right_state_int \regex_toks_put_right:Nx
+ \seq_set_split:Nnn \l_regex_internal_seq { } { CBEMTPUDSLOA }
+ \seq_set_filter:NNn \l_regex_internal_seq \l_regex_internal_seq
+ { \int_if_odd_p:n { #2 / \int_use:c { c_regex_catcode_##1_int } } }
+ \regex_show_scope:nn
+ {
+ categories~\seq_use:N \l_regex_internal_seq, ~
+ \bool_if:NF #1 { negative~ } class
+ }
}
-\cs_new_protected_nopar:cpn { regex_build_group_??: }
+\int_new:N \l_regex_min_state_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
+\int_new:N \l_regex_capturing_group_int
+\cs_new_protected:Npn \regex_build:n #1
{
- \regex_build_group_qs_aux:NN
- \l_regex_right_state_int \regex_toks_put_left:Nx
+ \regex_compile:n {#1}
+ \regex_build:N \l_regex_internal_regex
}
-\cs_new_protected:Npn \regex_build_group_transition_right_max:
+\cs_new_protected:Npn \regex_build: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_right_state_int } }
- }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_zero:N \l_regex_capturing_group_int
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
+ \regex_build_new_state:
+ \regex_toks_put_right:Nn \l_regex_left_state_int
+ { \regex_action_start_wildcard: }
+ \regex_group:nnnN {#1} { 1 } { 0 } \c_false_bool
+ \regex_toks_put_right:Nn \l_regex_right_state_int
+ { \regex_action_success: }
}
-\cs_new_protected:Npn \regex_build_group_n_aux:n #1
+\cs_new_protected:Npn \regex_build_for_cs:n #1
{
- \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
- \int_set_eq:NN \l_regex_internal_b_int \l_regex_max_state_int
- \int_add:Nn \l_regex_right_state_int
- { ( #1 - 1 ) * ( \l_regex_max_state_int - \l_regex_left_state_int ) }
- \int_set:Nn \l_regex_max_state_int
- {
- \l_regex_left_state_int
- + (#1) * ( \l_regex_max_state_int - \l_regex_left_state_int )
- }
- \int_while_do:nNnn \l_regex_internal_b_int < \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_max_state_int \l_regex_min_state_int
+ \regex_build_new_state:
+ \regex_build_new_state:
+ \regex_push_lr_states:
+ #1
+ \regex_pop_lr_states:
+ \regex_toks_put_right:Nn \l_regex_right_state_int
{
- \tex_toks:D \l_regex_internal_b_int
- = \tex_toks:D \l_regex_internal_a_int
- \int_incr:N \l_regex_internal_a_int
- \int_incr:N \l_regex_internal_b_int
+ \if_num:w \l_regex_current_pos_int = \l_regex_max_pos_int
+ \exp_after:wN \regex_action_success:
+ \fi:
}
}
-\cs_new_protected:Npn \regex_build_group_n: #1
+\cs_new_protected_nopar:Npn \regex_push_lr_states:
{
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#1}
- \regex_build_new_state:
- \int_compare:nNnF {#1} = \c_zero
- {
- \regex_build_group_submatches:NN
- \l_regex_internal_a_int \l_regex_left_state_int
- }
+ \seq_push:No \l_regex_left_state_seq
+ { \int_use:N \l_regex_left_state_int }
+ \seq_push:No \l_regex_right_state_seq
+ { \int_use:N \l_regex_right_state_int }
}
-\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n:
-\cs_new_protected_nopar:Npn \regex_build_group_:
- { \regex_build_group_n: \c_one }
-\cs_new_protected_nopar:cpn { regex_build_group_n*: }
+\cs_new_protected_nopar:Npn \regex_pop_lr_states:
{
- \regex_build_group_n_star_aux:NNn
- \regex_toks_put_right:Nx \regex_toks_put_left:Nx
+ \seq_pop:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \seq_pop:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_right_state_int \l_regex_internal_a_tl
}
-\cs_new_protected_nopar:cpn { regex_build_group_n*?: }
+\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2
{
- \regex_build_group_n_star_aux:NNn
- \regex_toks_put_left:Nx \regex_toks_put_right:Nx
+ \cs_set_nopar:Npx \regex_tmp:w { #2 }
+ \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 }
}
-\cs_new_protected:Npn \regex_build_group_n_star_aux:NNn #1#2#3
+\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2
{
- \int_compare:nNnTF {#3} = \c_zero
- { \regex_build_group_qs_aux:NN \l_regex_internal_a_int #1 }
- {
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#3}
- \regex_build_new_state:
- #2 \l_regex_left_state_int
- {
- \regex_action_free:n
- {
- \int_eval:n
- { \l_regex_internal_a_int - \l_regex_left_state_int }
- }
- }
- \regex_build_group_submatches:NN
- \l_regex_internal_a_int \l_regex_left_state_int
- }
+ \cs_set_nopar:Npx \regex_tmp:w {#2}
+ \tex_toks:D #1 \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w }
}
-\cs_new_protected_nopar:cpx { regex_build_group_*: }
- { \exp_not:c { regex_build_group_n*: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_group_*?: }
- { \exp_not:c { regex_build_group_n*?: } \c_zero }
-\cs_new_protected_nopar:cpx { regex_build_group_+: }
- { \exp_not:c { regex_build_group_n*: } \c_one }
-\cs_new_protected_nopar:cpx { regex_build_group_+?: }
- { \exp_not:c { regex_build_group_n*?: } \c_one }
-\cs_new_protected_nopar:Npn \regex_build_group_nn:
+\cs_new_protected:Npn \regex_toks_put_right:Nn #1#2
+ { \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D #1 #2 } }
+\cs_new_protected:Npn \regex_build_transition_left:NNN #1#2#3
+ { \regex_toks_put_left:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected:Npn \regex_build_transition_right:nNn #1#2#3
+ { \regex_toks_put_right:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected_nopar:Npn \regex_build_new_state:
{
- \regex_build_group_nn_aux:nnnn { }
- \regex_build_group_transition_right_max:
+ \tex_toks:D \l_regex_max_state_int { }
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int
+ \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int
+ \int_incr:N \l_regex_max_state_int
}
-\cs_new_protected_nopar:cpn { regex_build_group_nn?: }
+\cs_new_protected:Npn \regex_build_transitions_lazyness:NNNNN #1#2#3#4#5
{
- \regex_build_group_nn_aux:nnnn { }
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
{
- \use:x
- {
- \tex_toks:D \l_regex_right_state_int
- {
- \exp_after:wN \regex_build_group_nn_aux_ii:w
- \tex_the:D \tex_toks:D \l_regex_right_state_int
- }
- }
+ \if_meaning:w \c_true_bool #1
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ \else:
+ #4 { \int_eval:n { #5 - \l_regex_left_state_int } }
+ #2 { \int_eval:n { #3 - \l_regex_left_state_int } }
+ \fi:
}
}
-\cs_new_protected:Npn \regex_build_group_nn_aux:nnnn #1#2#3#4
+\cs_new_protected:Npn \regex_class:NnnnN #1#2#3#4#5
{
- \int_compare:nNnTF {#3} > {#4}
- {
- \msg_kernel_error:nnxxx
- { regex } { quantifier-backwards } {#3} {#4} {#1}
- }
+ \cs_set_nopar:Npx \regex_tests_action_cost:n ##1
{
- \int_compare:nNnTF {#3} = \c_zero
- {
- \regex_build_group_qs_aux:
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n { #4 - #3 }
- \int_set_eq:NN \l_regex_internal_c_int \l_regex_right_state_int
- \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int
- \prg_replicate:nn { #4 - #3 }
- {
- #2
- \int_add:Nn \l_regex_right_state_int
- { \l_regex_internal_b_int - \l_regex_internal_a_int }
- }
- \int_set_eq:NN \l_regex_right_state_int \l_regex_internal_c_int
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_new_state:
- }
- {
- \regex_build_group_transition_right_max:
- \regex_build_group_n_aux:n {#3}
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_group_submatches:NN
- \l_regex_left_state_int \l_regex_right_state_int
- \regex_build_group_n_aux:n { #4 - #3 + \c_one }
- \int_sub:Nn \l_regex_right_state_int
- { \l_regex_internal_a_int - \l_regex_left_state_int }
- \prg_replicate:nn { #4 - #3 }
- {
- #2
- \int_add:Nn \l_regex_right_state_int
- { \l_regex_internal_b_int - \l_regex_internal_a_int }
- }
- \int_set_eq:NN \l_regex_left_state_int \l_regex_internal_a_int
- \regex_build_new_state:
- }
+ \exp_not:n { \exp_not:n {#2} }
+ \bool_if:NTF #1
+ { \regex_break_point:TF { \regex_action_cost:n {##1} } { } }
+ { \regex_break_point:TF { } { \regex_action_cost:n {##1} } }
}
+ \if_case:w - #4 \exp_stop_f:
+ \regex_class_repeat:n {#3}
+ \or: \regex_class_repeat:nN {#3} #5
+ \else: \regex_class_repeat:nnN {#3} {#4} #5
+ \fi:
}
-\cs_new_protected:Npn \regex_build_group_nn_aux_ii:w #1 \regex_action_free:n
+\cs_new:Npn \regex_tests_action_cost:n { \regex_action_cost:n }
+\cs_new_protected:Npn \regex_class_repeat:n #1
{
- #1
- \regex_action_free:n
+ \prg_replicate:nn {#1}
{
- \int_eval:n
- { \l_regex_max_state_int - \l_regex_right_state_int }
+ \regex_build_new_state:
+ \regex_build_transition_right:nNn \regex_tests_action_cost:n
+ \l_regex_left_state_int \l_regex_right_state_int
}
- \regex_action_free:n
}
-\cs_new_protected:cpn { regex_build_/u: } #1#2
+\cs_new_protected:Npn \regex_class_repeat:nN #1#2
{
- \regex_build_if_assertions_forbidden:TF
- { \regex_build_raw_error:N u #1#2 }
- {
- \str_if_eq:xxTF {#1#2} { \regex_build_special:N \c_lbrace_str }
- {
- \tl_set:Nx \l_regex_internal_a_tl { \if_false: } \fi:
- \regex_build_u_loop:NN
- }
- {
- \msg_kernel_error:nn { regex } { u-missing-lbrace }
- #1#2
- }
- }
+ \if_num:w #1 = \c_zero
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_tests_action_cost:n \l_regex_left_state_int
+ \else:
+ \regex_class_repeat:n {#1}
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \regex_build_transitions_lazyness:NNNNN #2
+ \regex_action_free:n \l_regex_right_state_int
+ \regex_action_free:n \l_regex_internal_a_int
+ \fi:
}
-\cs_new:Npn \regex_build_u_loop:NN #1#2
+\cs_new_protected:Npn \regex_class_repeat:nnN #1#2#3
{
- \token_if_eq_meaning:NNTF #1 \regex_build_raw:N
- { #2 \regex_build_u_loop:NN }
+ \regex_class_repeat:n {#1}
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_max_state_int + #2 - \c_one }
+ \prg_replicate:nn { #2 }
{
- \token_if_eq_meaning:NNTF #1 \regex_build_special:N
- {
- \exp_after:wN \token_if_eq_charcode:NNTF \c_rbrace_str #2
- { \if_false: { \fi: } \regex_build_u_end: }
- { #2 \regex_build_u_loop:NN }
- }
- {
- \if_false: { \fi: }
- \msg_kernel_error:nnx { regex } { u-missing-rbrace } {#2}
- \regex_build_u_end:
- #1#2
- }
+ \regex_build_transitions_lazyness:NNNNN #3
+ \regex_action_free:n \l_regex_internal_a_int
+ \regex_tests_action_cost:n \l_regex_right_state_int
}
}
-\cs_new_protected:Npn \regex_build_u_end:
+\cs_new_protected:Npn \regex_group_aux:nnnnN #1#2#3#4#5
{
- \tl_set:Nv \l_regex_internal_a_tl \l_regex_internal_a_tl
- \if_num:w \l_regex_build_mode_int = \c_zero
- \regex_build_u_not_cs:
- \else:
- \regex_build_u_in_cs:
- \fi:
- \tl_clear:N \l_regex_tests_tl
+ \if_num:w #3 = \c_zero
+ \regex_build_new_state:
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ \fi:
+ \regex_build_new_state:
+ \regex_push_lr_states:
+ #2
+ \regex_pop_lr_states:
+ \if_case:w - #4 \exp_stop_f:
+ \regex_group_repeat:nn {#1} {#3}
+ \or: \regex_group_repeat:nnN {#1} {#3} #5
+ \else: \regex_group_repeat:nnnN {#1} {#3} {#4} #5
+ \fi:
}
-\cs_new_protected:Npn \regex_build_u_in_cs:
+\cs_new_protected:Npn \regex_group:nnnN #1
{
- \exp_args:NNo \str_gset_other:Nn \g_regex_internal_tl
- { \l_regex_internal_a_tl }
- \tl_map_inline:Nn \g_regex_internal_tl
+ \exp_args:No \regex_group_aux:nnnnN
+ { \int_use:N \l_regex_capturing_group_int }
{
- \tl_set:Nx \l_regex_tests_tl { \regex_item_equal:n { `##1 } }
- \regex_build_one_:
+ \int_incr:N \l_regex_capturing_group_int
+ #1
}
}
-\cs_new_protected:Npn \regex_build_u_not_cs:
+\cs_new_protected_nopar:Npn \regex_group_no_capture:nnnN
+ { \regex_group_aux:nnnnN { -1 } }
+\cs_new_protected:Npn \regex_group_resetting:nnnN #1
{
- \exp_args:No \tl_analysis_map_inline:nn { \l_regex_internal_a_tl }
+ \regex_group_aux:nnnnN { -1 }
{
- \tl_set:Nx \l_regex_tests_tl
- {
- \exp_not:N \int_compare:nNnT
- \l_regex_current_catcode_int = { \int_value:w "##2 }
- {
- \int_compare:nNnTF { "##2 } = \c_zero
- {
- \exp_not:n
- { \str_if_eq:xxT { \l_regex_current_token_tl } {##1} }
- { \regex_break_true:w }
- }
- { \regex_item_equal:n {##3} }
- }
- }
- \regex_build_one_:
+ \exp_args:Noo \regex_group_resetting_loop:nnNn
+ { \int_use:N \l_regex_capturing_group_int }
+ { \int_use:N \l_regex_capturing_group_int }
+ #1
+ { ?? \prg_map_break:n } { }
+ \prg_break_point:n { }
}
}
-\cs_new_protected:Npn \regex_store_state:n #1
+\cs_new_protected:Npn \regex_group_resetting_loop:nnNn #1#2#3#4
{
- \int_incr:N \l_regex_max_active_int
- \tex_skip:D \l_regex_max_active_int #1 sp \scan_stop:
- \regex_store_submatches:n {#1}
+ \use_none:nn #3 { \int_set:Nn \l_regex_capturing_group_int {#1} }
+ \int_set:Nn \l_regex_capturing_group_int {#2}
+ #3 {#4}
+ \exp_args:Nf \regex_group_resetting_loop:nnNn
+ { \int_max:nn {#1} { \l_regex_capturing_group_int } }
+ {#2}
}
-\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 \use_none_delimit_by_s_stop:w } }
-\cs_new_protected:Npn \regex_state_use_aux:n #1
+\cs_new_protected:Npn \regex_branch:n #1
{
- \if_num:w \tex_dimen:D \l_regex_current_state_int < \l_regex_step_int
- \tex_dimen:D \l_regex_current_state_int
- = \l_regex_step_int sp \scan_stop:
- #1 \tex_the:D \tex_toks:D \exp_after:wN \l_regex_current_state_int
- \fi:
- \scan_stop:
+ \regex_build_new_state:
+ \seq_get:NN \l_regex_left_state_seq \l_regex_internal_a_tl
+ \int_set:Nn \l_regex_left_state_int \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_left_state_int \l_regex_right_state_int
+ #1
+ \seq_get:NN \l_regex_right_state_seq \l_regex_internal_a_tl
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_tl
}
-\cs_new_protected_nopar:Npn \regex_disable_submatches:
+\cs_new_protected:Npn \regex_group_repeat:nn #1#2
{
- \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
- \regex_protected_use_none:n
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_max_state_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_new_state:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \regex_build_new_state:
+ \fi:
}
-\cs_new_protected:Npn \regex_protected_use_none:n #1 { }
-\cs_new_protected:Npn \regex_store_submatches:n #1
+\cs_new_protected:Npn \regex_group_submatches:nNN #1#2#3
{
- \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
- }
+ \if_num:w #1 > \c_minus_one
+ \regex_toks_put_left:Nx #2 { \regex_action_submatch:n { #1 < } }
+ \regex_toks_put_left:Nx #3 { \regex_action_submatch:n { #1 > } }
+ \fi:
}
-\cs_new_protected:Npn \regex_store_submatches_aux:w #1 \s_stop
+\cs_new_protected:Npn \regex_group_repeat_aux:n #1
{
- \regex_store_submatches_aux_ii:Nnnw
- #1
- \regex_state_submatches:nn \c_minus_one \q_prop
- \s_stop
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_internal_a_int \l_regex_left_state_int
+ \int_set_eq:NN \l_regex_internal_b_int \l_regex_max_state_int
+ \if_num:w \int_eval:w #1 > \c_one
+ \int_set:Nn \l_regex_internal_c_int
+ {
+ ( #1 - \c_one )
+ * ( \l_regex_internal_b_int - \l_regex_internal_a_int )
+ }
+ \tex_advance:D \l_regex_right_state_int \l_regex_internal_c_int
+ \tex_advance:D \l_regex_max_state_int \l_regex_internal_c_int
+ \prg_replicate:nn \l_regex_internal_c_int
+ {
+ \tex_toks:D \l_regex_internal_b_int
+ = \tex_toks:D \l_regex_internal_a_int
+ \tex_advance:D \l_regex_internal_a_int \c_one
+ \tex_advance:D \l_regex_internal_b_int \c_one
+ }
+ \fi:
}
-\cs_new_protected:Npn \regex_store_submatches_aux_ii:Nnnw
- \regex_state_submatches:nn #1 #2 #3 \s_stop
+\cs_new_protected:Npn \regex_group_repeat:nnN #1#2#3
{
- \exp_after:wN \c_zero
- \exp_after:wN \regex_state_submatches:nn \exp_after:wN
- { \int_value:w \int_eval:w \l_regex_step_int + \c_one \exp_after:wN }
- \exp_after:wN { \l_regex_current_submatches_prop }
- \regex_state_submatches:nn {#1} {#2}
- \s_stop
+ \if_num:w #2 = \c_zero
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \int_set:Nn \l_regex_internal_a_int
+ { \l_regex_left_state_int - \c_one }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \regex_build_new_state:
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \else:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \fi:
+ \else:
+ \regex_group_repeat_aux:n {#2}
+ \regex_group_submatches:nNN {#1}
+ \l_regex_internal_a_int \l_regex_right_state_int
+ \if_meaning:w \c_true_bool #3
+ \regex_build_transition_right:nNn \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \else:
+ \regex_build_transition_left:NNN \regex_action_free_group:n
+ \l_regex_right_state_int \l_regex_internal_a_int
+ \fi:
+ \regex_build_new_state:
+ \fi:
}
-\cs_new_protected:Npn \regex_state_submatches:nn #1#2
+\cs_new_protected:Npn \regex_group_repeat:nnnN #1#2#3#4
{
- \if_num:w #1 = \l_regex_step_int
- \tl_set:Nn \l_regex_current_submatches_prop { #2 }
+ \regex_group_submatches:nNN {#1}
+ \l_regex_left_state_int \l_regex_right_state_int
+ \regex_group_repeat_aux:n { #2 + #3 }
+ \if_meaning:w \c_true_bool #4
+ \int_set_eq:NN \l_regex_left_state_int \l_regex_max_state_int
+ \prg_replicate:nn { #3 }
+ {
+ \int_sub:Nn \l_regex_left_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_left:NNN \regex_action_free:n
+ \l_regex_left_state_int \l_regex_max_state_int
+ }
+ \else:
+ \prg_replicate:nn { #3 - \c_one }
+ {
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
+ }
+ \if_num:w #2 = \c_zero
+ \int_set:Nn \l_regex_right_state_int
+ { \l_regex_left_state_int - \c_one }
+ \else:
+ \int_sub:Nn \l_regex_right_state_int
+ { \l_regex_internal_b_int - \l_regex_internal_a_int }
+ \fi:
+ \regex_build_transition_right:nNn \regex_action_free:n
+ \l_regex_right_state_int \l_regex_max_state_int
\fi:
+ \regex_build_new_state:
}
-\cs_new_protected:Npn \regex_match:n #1
+\cs_new_protected:Npn \regex_assertion:Nn #1#2
{
- \int_zero:N \l_regex_nesting_int
- \int_set_eq:NN \l_regex_current_index_int \l_regex_max_state_int
- \regex_query_set:nnn { } { -1 } { -2 }
- \int_set_eq:NN \l_regex_min_index_int \l_regex_current_index_int
- \tl_analysis_map_inline:nn {#1}
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
{
- \regex_query_set:nnn {##1} {"##2} {##3}
- \if_case:w "##2 \exp_stop_f:
- \or: \int_incr:N \l_regex_nesting_int
- \or: \int_decr:N \l_regex_nesting_int
- \fi:
+ \exp_not:n {#2}
+ \regex_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \regex_action_free:n
+ {
+ \int_eval:n
+ { \l_regex_right_state_int - \l_regex_left_state_int }
+ }
+ }
+ \bool_if:NT #1 { { } }
}
- \int_set_eq:NN \l_regex_max_index_int \l_regex_current_index_int
- \regex_query_set:nnn { } { -1 } { -2 }
- \regex_match_initial_setup:
- \regex_match_once:
}
-\cs_new_protected:Npn \regex_query_set:nnn #1#2#3
+\cs_new_protected:Npn \regex_anchor:N #1
{
- \tex_muskip:D \l_regex_current_index_int
- = \etex_gluetomu:D
- #3 sp
- plus #2 sp
- minus \l_regex_nesting_int sp
- \scan_stop:
- \tex_toks:D \l_regex_current_index_int {#1}
- \int_incr:N \l_regex_current_index_int
+ \if_num:w #1 = \l_regex_current_pos_int
+ \exp_after:wN \regex_break_true:w
+ \fi:
}
-\cs_new_protected_nopar:Npn \regex_query_get:
+\cs_new_protected_nopar:Npn \regex_b_test:
{
- \tl_set:Nx \l_regex_current_token_tl
- { \tex_the:D \tex_toks:D \l_regex_current_index_int }
- \l_regex_current_char_int
- = \etex_mutoglue:D \tex_muskip:D \l_regex_current_index_int
- \l_regex_current_catcode_int = \etex_gluestretch:D
- \etex_mutoglue:D \tex_muskip:D \l_regex_current_index_int
+ \group_begin:
+ \int_set_eq:NN \l_regex_current_char_int \l_regex_last_char_int
+ \regex_prop_w:
+ \regex_break_point:TF
+ { \group_end: \regex_item_reverse:n \regex_prop_w: }
+ { \group_end: \regex_prop_w: }
}
-\cs_new_protected_nopar:Npn \regex_match_once:
+\cs_new_protected_nopar:Npn \regex_command_K:
{
- \regex_match_setup:
- \regex_query_get:
- \regex_match_loop:
- \prg_break_point:n { }
- \bool_if:NT \l_regex_success_match_bool
+ \regex_build_new_state:
+ \regex_toks_put_right:Nx \l_regex_left_state_int
{
- \bool_gset_true:N \g_regex_success_bool
- \l_regex_every_match_tl
+ \regex_action_submatch:n { 0< }
+ \bool_set_true:N \l_regex_fresh_thread_bool
+ \regex_action_free:n
+ { \int_eval:n { \l_regex_right_state_int - \l_regex_left_state_int } }
+ \bool_set_false:N \l_regex_fresh_thread_bool
}
}
-\cs_new_protected_nopar:Npn \regex_match_initial_setup:
+\int_new:N \l_regex_min_pos_int
+\int_new:N \l_regex_max_pos_int
+\int_new:N \l_regex_current_pos_int
+\int_new:N \l_regex_start_pos_int
+\int_new:N \l_regex_success_pos_int
+\int_new:N \l_regex_current_char_int
+\int_new:N \l_regex_current_catcode_int
+\int_new:N \l_regex_last_char_int
+\int_new:N \l_regex_case_changed_char_int
+\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_step_int
+\int_new:N \l_regex_min_active_int
+\int_new:N \l_regex_max_active_int
+\tl_new:N \l_regex_every_match_tl
+\bool_new:N \l_regex_fresh_thread_bool
+\bool_new:N \l_regex_empty_success_bool
+\cs_new_eq:NN \regex_if_two_empty_matches:F \use:n
+\bool_new:N \g_regex_success_bool
+\bool_new:N \l_regex_saved_success_bool
+\bool_new:N \l_regex_match_success_bool
+\cs_new_protected:Npn \regex_match:n #1
{
- \int_set_eq:NN \l_regex_step_int \c_minus_one
- \prg_stepwise_inline:nnnn
- \c_zero \c_one { \l_regex_max_state_int - \c_one }
- { \tex_dimen:D ##1 \l_regex_step_int sp }
- \int_set:Nn \l_regex_start_index_int
- { \l_regex_min_index_int - \c_one }
- \int_set_eq:NN \l_regex_current_index_int \l_regex_min_index_int
- \int_set_eq:NN \l_regex_success_index_int \l_regex_min_index_int
- \int_set_eq:NN \l_regex_submatch_int \l_regex_max_state_int
- \bool_set_false:N \l_regex_success_empty_bool
+ \int_zero:N \l_regex_balance_int
+ \int_set:Nn \l_regex_current_pos_int { \c_two * \l_regex_max_state_int }
+ \regex_query_set:nnn { } { -1 } { -2 }
+ \int_set_eq:NN \l_regex_min_pos_int \l_regex_current_pos_int
+ \tl_analysis_map_inline:nn {#1}
+ { \regex_query_set:nnn {##1} {"##2} {##3} }
+ \int_set_eq:NN \l_regex_max_pos_int \l_regex_current_pos_int
+ \regex_query_set:nnn { } { -1 } { -2 }
\bool_gset_false:N \g_regex_success_bool
+ \prg_stepwise_inline:nnnn
+ \l_regex_min_state_int \c_one { \l_regex_max_state_int - \c_one }
+ { \tex_dimen:D ##1 \c_one sp \scan_stop: }
+ \int_set_eq:NN \l_regex_min_active_int \l_regex_max_state_int
+ \int_set_eq:NN \l_regex_step_int \c_zero
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_min_pos_int
+ \int_set:Nn \l_regex_submatch_int
+ { \c_two * \l_regex_max_state_int }
+ \bool_set_false:N \l_regex_empty_success_bool
+ \regex_match_once:
}
-\cs_new_protected_nopar:Npn \regex_match_setup:
+\cs_new_protected_nopar:Npn \regex_match_once:
{
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \cs_set_nopar:Npn \regex_if_two_empty_matches:F
+ { \int_compare:nNnF \l_regex_start_pos_int = \l_regex_current_pos_int }
+ \else:
+ \cs_set_eq:NN \regex_if_two_empty_matches:F \use:n
+ \fi:
+ \int_set_eq:NN \l_regex_start_pos_int \l_regex_success_pos_int
+ \bool_set_false:N \l_regex_match_success_bool
\prop_clear:N \l_regex_current_submatches_prop
- \bool_if:NTF \l_regex_success_empty_bool
- { \cs_set_eq:NN \regex_if_two_empty_matches:F \regex_if_match_empty:F }
- { \cs_set_eq:NN \regex_if_two_empty_matches:F \use:n }
- \int_set_eq:NN \l_regex_start_index_int \l_regex_success_index_int
- \int_set:Nn \l_regex_current_index_int
- { \l_regex_start_index_int - \c_one }
- \bool_set_false:N \l_regex_success_match_bool
- \int_zero:N \l_regex_max_active_int
- \regex_store_state:n {0} %^^A _state_int!
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \regex_store_state:n { \l_regex_min_state_int }
+ \int_set:Nn \l_regex_current_pos_int
+ { \l_regex_start_pos_int - \c_one }
+ \regex_query_get:
+ \regex_match_loop:
+ \l_regex_every_match_tl
+ }
+\cs_new_protected_nopar:Npn \regex_single_match:
+ {
+ \tl_set:Nn \l_regex_every_match_tl
+ { \bool_gset_eq:NN \g_regex_success_bool \l_regex_match_success_bool }
+ }
+\cs_new_protected:Npn \regex_multi_match:n #1
+ {
+ \tl_set:Nn \l_regex_every_match_tl
+ {
+ \if_meaning:w \c_true_bool \l_regex_match_success_bool
+ \bool_gset_true:N \g_regex_success_bool
+ #1
+ \exp_after:wN \regex_match_once:
+ \fi:
+ }
}
\cs_new_protected_nopar:Npn \regex_match_loop:
{
- \int_incr:N \l_regex_current_index_int
- \int_incr:N \l_regex_step_int
- \bool_set_false:N \l_regex_fresh_thread_bool
+ \tex_advance:D \l_regex_step_int \c_two
+ \int_incr:N \l_regex_current_pos_int
\int_set_eq:NN \l_regex_last_char_int \l_regex_current_char_int
+ \int_set_eq:NN \l_regex_case_changed_char_int \c_max_int
\regex_query_get:
- \regex_match_loop_case_hook:
\use:x
{
- \int_zero:N \l_regex_max_active_int
- \regex_match_one_active:w 1 ; \prg_break_point:n { }
- \exp_not:N \prg_break_point:n { }
+ \int_set_eq:NN \l_regex_max_active_int \l_regex_min_active_int
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \l_regex_min_active_int ;
}
- \if_num:w \l_regex_current_char_int < \c_minus_one
- \exp_after:wN \prg_map_break:
- \fi:
- \if_num:w \l_regex_max_active_int = \c_zero
- \exp_after:wN \prg_map_break:
+ \prg_break_point:n { \bool_set_false:N \l_regex_fresh_thread_bool }
+ \if_num:w \l_regex_max_active_int > \l_regex_min_active_int
+ \if_num:w \l_regex_current_pos_int < \l_regex_max_pos_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \regex_match_loop:
+ \fi:
\fi:
- \regex_match_loop:
}
\cs_new:Npn \regex_match_one_active:w #1;
{
- \if_num:w #1 > \l_regex_max_active_int
- \exp_after:wN \prg_map_break:
+ \if_num:w #1 < \l_regex_max_active_int
+ \regex_use_state_and_submatches:nn
+ { \int_value:w \tex_skip:D #1 }
+ { \tex_the:D \tex_toks:D #1 }
+ \exp_after:wN \regex_match_one_active:w
+ \int_use:N \int_eval:w #1 + \c_one \exp_after:wN ;
\fi:
- \regex_match_one_active_aux:n
- { \int_value:w \tex_skip:D #1 }
- \exp_after:wN \regex_match_one_active:w
- \int_use:N \int_eval:w #1 + \c_one ;
}
-\cs_new_protected:Npn \regex_match_one_active_aux:n #1
+\cs_new_protected:Npn \regex_query_set:nnn #1#2#3
{
- \int_set:Nn \l_regex_current_state_int {#1}
- \prop_clear:N \l_regex_current_submatches_prop
- \regex_state_use_with_submatches:
+ \tex_muskip:D \l_regex_current_pos_int
+ = \etex_gluetomu:D
+ #3 sp
+ plus #2 sp
+ minus \l_regex_balance_int sp
+ \scan_stop:
+ \tex_toks:D \l_regex_current_pos_int {#1}
+ \int_incr:N \l_regex_current_pos_int
+ \if_case:w #2 \exp_stop_f:
+ \or: \int_incr:N \l_regex_balance_int
+ \or: \int_decr:N \l_regex_balance_int
+ \fi:
}
-\cs_new_protected_nopar:Npn \regex_match_loop_case_hook: { }
-\cs_new_protected_nopar:Npn \regex_match_loop_caseless_hook:
+\cs_new_protected_nopar:Npn \regex_query_get:
{
- \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:
+ \l_regex_current_char_int
+ = \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ \l_regex_current_catcode_int = \etex_gluestretch:D
+ \etex_mutoglue:D \tex_muskip:D \l_regex_current_pos_int
+ }
+\cs_new_protected_nopar:Npn \regex_use_state:
+ {
+ \tex_dimen:D \l_regex_current_state_int
+ = \l_regex_step_int sp \scan_stop:
+ \tex_the:D \tex_toks:D \l_regex_current_state_int
+ \tex_dimen:D \l_regex_current_state_int
+ = \int_eval:w \l_regex_step_int + \c_one \int_eval_end: sp \scan_stop:
+ }
+\cs_new_protected:Npn \regex_use_state_and_submatches:nn #1 #2
+ {
+ \int_set:Nn \l_regex_current_state_int {#1}
+ \if_num:w \tex_dimen:D \l_regex_current_state_int < \l_regex_step_int
+ \tl_set:Nn \l_regex_current_submatches_prop {#2}
+ \exp_after:wN \regex_use_state:
\fi:
+ \scan_stop:
}
\cs_new_protected_nopar:Npn \regex_action_start_wildcard:
{
@@ -1846,96 +1949,145 @@
\bool_set_false:N \l_regex_fresh_thread_bool
\regex_action_cost:n {0}
}
+\cs_new_protected_nopar:Npn \regex_action_free:n
+ { \regex_action_free_aux:nn { > \l_regex_step_int \else: } }
+\cs_new_protected_nopar:Npn \regex_action_free_group:n
+ { \regex_action_free_aux:nn { < \l_regex_step_int } }
+\cs_new_protected:Npn \regex_action_free_aux:nn #1#2
+ {
+ \use:x
+ {
+ \int_add:Nn \l_regex_current_state_int {#2}
+ \exp_not:n
+ {
+ \if_num:w \tex_dimen:D \l_regex_current_state_int #1
+ \exp_after:wN \regex_use_state:
+ \fi:
+ }
+ \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 }
+ }
+ }
\cs_new_protected:Npn \regex_action_cost:n #1
{
- \exp_args:Nf \regex_store_state:n
- { \int_eval:n { \l_regex_current_state_int + #1 } }
+ \exp_args:No \regex_store_state:n
+ { \int_use:N \int_eval:w \l_regex_current_state_int + #1 }
+ }
+\cs_new_protected:Npn \regex_store_state:n #1
+ {
+ \regex_store_submatches:
+ \tex_skip:D \l_regex_max_active_int = #1 sp \scan_stop:
+ \int_incr:N \l_regex_max_active_int
+ }
+\cs_new_protected_nopar:Npn \regex_store_submatches:
+ {
+ \tex_toks:D \l_regex_max_active_int \exp_after:wN
+ { \l_regex_current_submatches_prop }
+ }
+\cs_new_protected_nopar:Npn \regex_disable_submatches:
+ {
+ \cs_set_protected_nopar:Npn \regex_store_submatches: { }
+ \cs_set_protected:Npn \regex_action_submatch:n ##1 { }
+ }
+\cs_new_protected:Npn \regex_action_submatch:n #1
+ {
+ \prop_put:Nno \l_regex_current_submatches_prop {#1}
+ { \int_use:N \l_regex_current_pos_int }
}
\cs_new_protected_nopar:Npn \regex_action_success:
{
\regex_if_two_empty_matches:F
{
- \bool_set_true:N \l_regex_success_match_bool
- \bool_set_eq:NN \l_regex_success_empty_bool
+ \bool_set_true:N \l_regex_match_success_bool
+ \bool_set_eq:NN \l_regex_empty_success_bool
\l_regex_fresh_thread_bool
- \int_set_eq:NN \l_regex_success_index_int \l_regex_current_index_int
+ \int_set_eq:NN \l_regex_success_pos_int \l_regex_current_pos_int
\prop_set_eq:NN \l_regex_success_submatches_prop
\l_regex_current_submatches_prop
\prg_map_break:
}
}
-\cs_new_protected:Npn \regex_action_free:n #1
+\int_new:N \l_regex_replacement_csnames_int
+\tl_new:N \l_regex_balance_tl
+\cs_new:Npn \regex_replacement_balance_one_match:n #1
+ { - \regex_submatch_balance:n {#1} }
+\cs_new:Npn \regex_replacement_do_one_match:n #1
{
- \use:x
- {
- \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_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D #1 }
+ { \tex_skip:D #1 }
}
-\cs_new_protected:Npn \regex_action_submatch:n #1
+\cs_new:Npn \regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+\cs_new:Npn \regex_query_range:nn #1#2
{
- \prop_put:Nno \l_regex_current_submatches_prop {#1}
- { \int_use:N \l_regex_current_index_int }
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 \exp_after:wN ;
+ \int_use:N \int_eval:w #2 ;
+ \prg_break_point:n { }
}
-\cs_new:Npn \regex_if_match_empty:T
- { \int_compare:nNnT \l_regex_start_index_int = \l_regex_current_index_int }
-\cs_new:Npn \regex_if_match_empty:F
- { \int_compare:nNnF \l_regex_start_index_int = \l_regex_current_index_int }
-\cs_new_protected:Npn \regex_submatch_nesting_aux:n #1
+\cs_new:Npn \regex_query_range_loop:ww #1 ; #2 ;
{
- + \etex_glueshrink:D \etex_mutoglue:D \etex_muexpr:D
- \tex_muskip:D \etex_gluestretch:D \tex_skip:D #1
- - \tex_muskip:D \tex_skip:D #1
- \scan_stop:
+ \if_num:w #1 < #2 \exp_stop_f:
+ \else:
+ \exp_after:wN \prg_map_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \exp_stop_f:
+ \exp_after:wN \regex_query_range_loop:ww
+ \int_use:N \int_eval:w #1 + \c_one ; #2 ;
}
-\cs_new:Npn \regex_nesting:n #1 { } %^^A move. Rename?
-\tl_new:N \l_regex_nesting_tl
-\cs_new_protected:Npn \regex_replacement:n #1
+\cs_new:Npn \regex_query_submatch:n #1
{
- \int_zero:N \l_regex_replacement_int
- \int_zero:N \l_regex_nesting_int
- \tl_clear:N \l_regex_nesting_tl
- \regex_escape_use:nnnn
- { \regex_replacement_unescaped:N ##1 }
- { \regex_replacement_escaped:N ##1 }
- { \regex_replacement_raw:N ##1 }
- {#1}
- \prg_do_nothing: \prg_do_nothing:
- \cs_set:Npx \regex_nesting:n ##1
- {
- + \int_use:N \l_regex_nesting_int
- \l_regex_nesting_tl
- - \regex_submatch_nesting_aux:n {##1}
- }
- \use:x
- {
- \exp_not:n { \cs_set:Npn \regex_replacement_tl:n ##1 }
- { \regex_toks_range:nn \c_zero \l_regex_replacement_int }
- }
- % ^^A rename!
+ \regex_query_range:nn
+ { \tex_skip:D \int_eval:w #1 }
+ { \etex_gluestretch:D \tex_skip:D \int_eval:w #1 }
}
-\cs_new_protected:Npn \regex_replacement_raw:N #1
- { \regex_replacement_put:n {#1} }
-\cs_new_protected:Npn \regex_replacement_put:n #1
+\cs_new_protected:Npn \regex_submatch_balance:n #1
{
- \tex_toks:D \l_regex_replacement_int {#1}
- \int_incr:N \l_regex_replacement_int
+ \etex_glueshrink:D \etex_mutoglue:D \etex_muexpr:D
+ \tex_muskip:D \etex_gluestretch:D \tex_skip:D #1
+ - \tex_muskip:D \tex_skip:D #1
+ \scan_stop:
}
-\cs_new_protected:Npn \regex_replacement_unescaped:N #1
+\cs_new_protected:Npn \regex_replacement:n #1
{
- \if_charcode:w \c_rbrace_str #1
- \if_num:w \l_regex_replacement_csnames_int > \c_zero
- \regex_replacement_put:n \cs_end:
- \else:
- \regex_replacement_put:n #1
+ \tl_set_build:Nw \l_regex_internal_a_tl
+ \int_zero:N \l_regex_balance_int
+ \tl_clear:N \l_regex_balance_tl
+ \regex_escape_use:nnnn
+ {
+ \if_charcode:w \c_rbrace_str ##1
+ \regex_replacement_rbrace:N \else: \tl_build_one:n \fi: ##1
+ }
+ { \regex_replacement_escaped:N ##1 }
+ { \tl_build_one:n ##1 }
+ {#1}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \msg_kernel_error:nnx { regex } { replacement-missing-rbrace }
+ { \int_use:N \l_regex_replacement_csnames_int }
+ \tl_build_one:x
+ { \prg_replicate:nn \l_regex_replacement_csnames_int \cs_end: }
\fi:
- \else:
- \regex_replacement_put:n #1
- \fi:
+ \cs_gset:Npx \regex_replacement_balance_one_match:n ##1
+ {
+ + \int_use:N \l_regex_balance_int
+ \l_regex_balance_tl
+ - \regex_submatch_balance:n {##1}
+ }
+ \tl_build_end:
+ \exp_args:No \regex_replacement_aux:n \l_regex_internal_a_tl
+ }
+\cs_new_protected:Npn \regex_replacement_aux:n #1
+ {
+ \cs_set:Npn \regex_replacement_do_one_match:n ##1
+ {
+ \regex_query_range:nn
+ { \etex_glueshrink:D \tex_skip:D ##1 }
+ { \tex_skip:D ##1 }
+ #1
+ }
}
\cs_new_protected:Npn \regex_replacement_escaped:N #1
{
@@ -1944,34 +2096,23 @@
\if_num:w \c_one < 1#1 \exp_stop_f:
\regex_replacement_put_submatch:n {#1}
\else:
- \regex_replacement_put:n #1
+ \tl_build_one:n #1
\fi:
}
}
\cs_new_protected:Npn \regex_replacement_put_submatch:n #1
{
- \regex_replacement_put:n
- { \regex_query_submatch:nn {#1} {##1} }
- \if_num:w \l_regex_replacement_csnames_int = \c_zero
- \tl_put_right:Nn \l_regex_nesting_tl
- {
- \exp_not:N \if_num:w #1 < \l_regex_capturing_group_int
- \regex_submatch_nesting_aux:n { \int_eval:w #1+##1 \int_eval_end: }
- \exp_not:N \fi:
- }
+ \if_num:w #1 < \l_regex_capturing_group_int
+ \tl_build_one:n { \regex_query_submatch:n { #1 + ##1 } }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \tl_put_right:Nn \l_regex_balance_tl
+ { + \regex_submatch_balance:n { \int_eval:w #1+##1 \int_eval_end: } }
+ \fi:
\fi:
}
-\cs_new_protected:Npn \regex_replacement_error:NNN #1#2#3
- {
- \msg_kernel_error:nnxx { regex } { #1-command }
- { replacement~text } {#3}
- #2 #3
- }
\cs_new_protected:Npn \regex_replacement_g:w #1#2
{
- \str_if_eq:xxTF
- { \exp_not:n { #1#2 } }
- { \regex_replacement_unescaped:N \c_lbrace_str }
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
{
\int_zero:N \l_regex_internal_a_int
\regex_replacement_g_digits:NN
@@ -1980,40 +2121,33 @@
}
\cs_new_protected:Npn \regex_replacement_g_digits:NN #1#2
{
- \token_if_eq_meaning:NNTF #1 \regex_replacement_unescaped:N
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
{
\if_num:w \c_one < 1#2 \exp_stop_f:
- \int_set:Nn \l_regex_internal_a_int { \c_ten * \l_regex_internal_a_int + #2 }
+ \int_set:Nn \l_regex_internal_a_int
+ { \c_ten * \l_regex_internal_a_int + #2 }
\exp_after:wN \use_i:nnn
\exp_after:wN \regex_replacement_g_digits:NN
\else:
- \if_charcode:w \c_rbrace_str #2
- \exp_args:No \regex_replacement_put_submatch:n
- { \int_use:N \l_regex_internal_a_int }
- \exp_after:wN \exp_after:wN \exp_after:wN \use_none:nn
- \else:
- \exp_after:wN \exp_after:wN
- \exp_after:wN \regex_replacement_error:NNN
- \exp_after:wN \exp_after:wN \exp_after:wN g
- \fi:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
\fi:
}
- { \regex_replacement_error:NNN g }
- #1 #2
- }
-\cs_new_protected:Npn \regex_replacement_u:w #1#2
- {
- \str_if_eq:xxTF { #1#2 } { \regex_replacement_unescaped:N \c_lbrace_str }
{
- \int_incr:N \l_regex_replacement_csnames_int
- \regex_replacement_put:n
- { \exp_not:n { \exp_after:wN \exp_not:V \cs:w } }
+ \if_meaning:w \regex_replacement_rbrace:N #1
+ \exp_args:No \regex_replacement_put_submatch:n
+ { \int_use:N \l_regex_internal_a_int }
+ \exp_after:wN \use_none:nn
+ \else:
+ \exp_after:wN \regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
}
- { \regex_replacement_error:NNN u #1#2 }
+ #1 #2
}
\cs_new_protected:Npn \regex_replacement_c:w #1#2
{
- \token_if_eq_meaning:NNTF #1 \regex_replacement_unescaped:N
+ \token_if_eq_meaning:NNTF #1 \tl_build_one:n
{
\cs_if_exist_use:cF { regex_replacement_c_#2:w }
{ \regex_replacement_error:NNN c #1#2 }
@@ -2022,121 +2156,173 @@
}
\cs_new_protected_nopar:cpn { regex_replacement_c_ \c_lbrace_str :w }
{
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n
+ { \exp_not:n { \exp_after:wN \regex_replacement_exp_not:N \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:N \cs:w } }
+ \fi:
\int_incr:N \l_regex_replacement_csnames_int
- \regex_replacement_put:n
- { \exp_not:n { \exp_after:wN \regex_replacement_exp_not:N \cs:w } }
}
-\cs_new:Npn \regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+\cs_new_protected:Npn \regex_replacement_u:w #1#2
+ {
+ \str_if_eq:xxTF { #1#2 } { \tl_build_one:n \c_lbrace_str }
+ {
+ \if_case:w \l_regex_replacement_csnames_int
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \exp_not:V \cs:w } }
+ \else:
+ \tl_build_one:n { \exp_not:n { \exp_after:wN \tl_to_str:V \cs:w } }
+ \fi:
+ \int_incr:N \l_regex_replacement_csnames_int
+ }
+ { \regex_replacement_error:NNN u #1#2 }
+ }
+\cs_new_protected:Npn \regex_replacement_rbrace:N #1
+ {
+ \if_int_compare:w \l_regex_replacement_csnames_int > \c_zero
+ \tl_build_one:n \cs_end:
+ \int_decr:N \l_regex_replacement_csnames_int
+ \else:
+ \tl_build_one:n #1
+ \fi:
+ }
\group_begin:
-
- \char_set_catcode_math_superscript:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_U:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_alignment:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_T:w
+ \cs_new_protected:Npn \regex_replacement_char:nNN #1#2#3
+ {
+ \if_meaning:w \prg_do_nothing: #3
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \tex_lccode:D \c_zero = `#3 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {#1} }
+ \fi:
+ }
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_A:w
+ { \regex_replacement_char:nNN { \exp_not:n { \exp_not:N ^^@ } } }
+ \char_set_catcode_group_begin:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_B:w
+ {
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_incr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_after:wN ^^@ \if_false: } \fi: } }
+ }
+ \cs_new_protected:Npn \regex_replacement_c_C:w #1#2
+ { \tl_build_one:n { \exp_not:N \exp_not:N \exp_not:c {#2} } }
+ \char_set_catcode_math_subscript:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_D:w
{ \regex_replacement_char:nNN { ^^@ } }
-
- \cs_new_protected:Npn \regex_replacement_c_S:w #1#2
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_E:w
{
- \int_compare:nNnTF { `#2 } = \c_zero
- { \regex_replacement_error:NNN c #1#2 }
- {
- \char_set_lccode:nn {32} { `#2 }
- \tl_to_lowercase:n { \regex_replacement_put:n {~} }
- }
+ \if_num:w \l_regex_replacement_csnames_int = \c_zero
+ \int_decr:N \l_regex_balance_int
+ \fi:
+ \regex_replacement_char:nNN
+ { \exp_not:n { \if_false: { \fi: ^^@ } }
}
-
- \char_set_catcode_parameter:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_P:w
- { \regex_replacement_char:nNN { ^^@^^@^^@^^@^^@^^@^^@^^@ } }
-
- \char_set_catcode_other:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_O:w
+ \char_set_catcode_letter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_L:w
{ \regex_replacement_char:nNN { ^^@ } }
-
\char_set_catcode_math_toggle:N \^^@
\cs_new_protected_nopar:Npn \regex_replacement_c_M:w
{ \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_letter:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_L:w
+ \char_set_catcode_other:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_O:w
{ \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_group_end:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_E:w
+ \char_set_catcode_parameter:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_P:w
{
- \int_decr:N \l_regex_nesting_int
- \regex_replacement_char:nNN { \if_false: { \fi: ^^@ }
+ \regex_replacement_char:nNN
+ { \exp_not:n { \exp_not:n { ^^@^^@^^@^^@ } } }
}
-
- \char_set_catcode_math_subscript:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_D:w
- { \regex_replacement_char:nNN { ^^@ } }
-
- \char_set_catcode_group_begin:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_B:w
+ \cs_new_protected:Npn \regex_replacement_c_S:w #1#2
{
- \int_incr:N \l_regex_nesting_int
- \regex_replacement_char:nNN { \exp_after:wN ^^@ \if_false: } \fi: }
+ \if_meaning:w \prg_do_nothing: #2
+ \msg_kernel_error:nn { regex } { replacement-catcode-end }
+ \else:
+ \if_num:w `#2 = \c_zero
+ \msg_kernel_error:nn { regex } { replacement-null-space }
+ \fi:
+ \tex_lccode:D 32 = `#2 \scan_stop:
+ \tl_to_lowercase:n { \tl_build_one:n {~} }
+ \fi:
}
-
- \char_set_catcode_active:N \^^@
- \cs_new_protected_nopar:Npn \regex_replacement_c_A:w
- { \regex_replacement_char:nNN { \exp_not:N ^^@ } }
-
+ \char_set_catcode_alignment:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_T:w
+ { \regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_math_superscript:N \^^@
+ \cs_new_protected_nopar:Npn \regex_replacement_c_U:w
+ { \regex_replacement_char:nNN { ^^@ } }
\group_end:
-
-\cs_new_protected:Npn \regex_replacement_char:nNN #1#2#3
+\cs_new_protected:Npn \regex_replacement_error:NNN #1#2#3
{
- \char_set_lccode:nn \c_zero { `#3 }
- \tl_to_lowercase:n
- { \regex_replacement_put:n { \exp_not:n {#1} } }
+ \msg_kernel_error:nnx { regex } { replacement-#1 } {#3}
+ #2 #3
}
-\cs_new_protected_nopar:Npn \regex_set:Nn
- { \regex_set_aux:NNn \tl_set:Nn }
-\cs_new_protected_nopar:Npn \regex_gset:Nn
- { \regex_set_aux:NNn \tl_gset:Nn }
-\cs_new_protected_nopar:Npn \regex_const:Nn
- { \regex_set_aux:NNn \tl_const:Nn }
-\cs_new_protected:Npn \regex_set_aux:NNn #1#2#3
+\cs_new_protected:Npn \regex_new:N #1
+ { \cs_new_eq:NN #1 \c_regex_no_match_regex }
+\cs_new_protected_nopar:Npn \regex_set:Nn #1#2
{
- \group_begin:
- \regex_build:n {#3}
- \use:x
- {
- \group_end:
- #1 \exp_not:N #2 { \regex_set_aux:N #2 }
- }
+ \regex_compile:n {#2}
+ \tl_set_eq:NN #1 \l_regex_internal_regex
}
-\cs_new:Npn \regex_set_aux:N #1
+\cs_new_protected_nopar:Npn \regex_gset:Nn #1#2
{
- \exp_not:n { \regex_nfa:Nw #1 }
- \l_regex_max_state_int
- = \int_use:N \l_regex_max_state_int
- \l_regex_capturing_group_int
- = \int_use:N \l_regex_capturing_group_int
- \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
- \c_zero \c_one {\l_regex_max_state_int - \c_one }
- \regex_set_aux:n
- \s_stop
+ \regex_compile:n {#2}
+ \tl_gset_eq:NN #1 \l_regex_internal_regex
+ }
+\cs_new_protected_nopar:Npn \regex_const:Nn #1#2
+ {
+ \regex_compile:n {#2}
+ \tl_const:Nx #1 { \exp_not:o \l_regex_internal_regex }
}
-\cs_new:Npn \regex_set_aux:n #1
- { \tex_toks:D #1 { \tex_the:D \tex_toks:D #1 } }
-\cs_new:Npn \regex_nfa:Nw #1
+\cs_new_protected:Npn \regex_show:n #1
{
- \msg_expandable_kernel_error:nnn { regex } { nfa-misused } {#1}
- \use_none_delimit_by_s_stop:w
+ \regex_compile:n {#1}
+ \regex_show_aux:Nx \l_regex_internal_regex
+ { { \tl_to_str:n {#1} } }
}
-\cs_new_protected_nopar:Npn \regex_use:N
- { \exp_last_unbraced:No \use_none:nn }
+\cs_new_protected:Npn \regex_show:N #1
+ { \regex_show_aux:Nx #1 { variable~\token_to_str:N #1 } }
+\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
+ {
+ \regex_match_aux:nn { \regex_build:n {#1} } {#2}
+ \regex_aux_return:
+ }
+\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \regex_match_aux:nn { \regex_build:N #1 } {#2}
+ \regex_aux_return:
+ }
+\cs_new_protected:Npn \regex_count:nnN #1
+ { \regex_count_aux:nnN { \regex_build:n {#1} } }
+\cs_new_protected:Npn \regex_count:NnN #1
+ { \regex_count_aux:nnN { \regex_build:N #1 } }
+\cs_set_protected:Npn \regex_tmp:w #1#2#3
+ {
+ \cs_new_protected:Npn #2 ##1 { #1 { \regex_build:n {##1} } }
+ \cs_new_protected:Npn #3 ##1 { #1 { \regex_build:N ##1 } }
+ \prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
+ { #1 { \regex_build:n {##1} } {##2} ##3 \regex_aux_return: }
+ \prg_new_protected_conditional:Npnn #3 ##1##2##3 { T , F , TF }
+ { #1 { \regex_build:N ##1 } {##2} ##3 \regex_aux_return: }
+ }
+\regex_tmp:w \regex_extract_once_aux:nnN
+ \regex_extract_once:nnN \regex_extract_once:NnN
+\regex_tmp:w \regex_extract_all_aux:nnN
+ \regex_extract_all:nnN \regex_extract_all:NnN
+\regex_tmp:w \regex_replace_once_aux:nnN
+ \regex_replace_once:nnN \regex_replace_once:NnN
+\regex_tmp:w \regex_replace_all_aux:nnN
+ \regex_replace_all:nnN \regex_replace_all:NnN
+\regex_tmp:w \regex_split_aux:nnN \regex_split:nnN \regex_split:NnN
+\int_new:N \l_regex_match_count_int
+\flag_new:n { regex_begin }
+\flag_new:n { regex_end }
+\int_new:N \l_regex_submatch_int
+\int_new:N \l_regex_zeroth_submatch_int
\cs_new_protected_nopar:Npn \regex_aux_return:
{
\if_meaning:w \c_true_bool \g_regex_success_bool
@@ -2145,79 +2331,67 @@
\prg_return_false:
\fi:
}
-\cs_new_protected:Npn \regex_aux_build_match:nn #1#2
+\cs_new_protected:Npn \regex_match_aux:nn #1#2
{
- \regex_build:n {#1}
- \regex_match:n {#2}
- }
-\cs_new_protected:Npn \regex_aux_use_match:Nn #1#2
- {
- \exp_args:No \tl_if_head_eq_meaning:nNTF {#1} \regex_nfa:Nw
- {
- \regex_use:N #1
- \regex_match:n {#2}
- }
- {
- \msg_kernel_error:nnx { regex } { not-nfa } { \token_to_str:N #1 }
- \bool_gset_false:N \g_regex_success_bool
- }
+ \group_begin:
+ \regex_disable_submatches:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \group_end:
}
-\cs_set_protected:Npn \regex_tmp:w #1#2#3
+\cs_new_protected:Npn \regex_count_aux:nnN #1#2#3
{
- \cs_new_protected_nopar:Npn #1
- { #3 \regex_aux_build_match:nn }
- \cs_new_protected_nopar:Npn #2
- { #3 \regex_aux_use_match:Nn }
- \prg_new_protected_conditional:Npnn #1 ##1##2##3 { T , F , TF }
- {
- #3 \regex_aux_build_match:nn {##1} {##2} ##3
- \regex_aux_return:
- }
- \prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
- {
- #3 \regex_aux_use_match:Nn {##1} {##2} ##3
- \regex_aux_return:
- }
+ \group_begin:
+ \regex_disable_submatches:
+ \int_zero:N \l_regex_match_count_int
+ \regex_multi_match:n { \int_incr:N \l_regex_match_count_int }
+ #1
+ \regex_match:n {#2}
+ \exp_args:NNNo
+ \group_end:
+ \int_set:Nn #3 { \int_use:N \l_regex_match_count_int }
}
-\tl_map_inline:nn
+\cs_new_protected:Npn \regex_extract_once_aux:nnN #1#2#3
{
- { extract_once } { extract_all }
- { replace_once } { replace_all }
- { split }
+ \group_begin:
+ \regex_single_match:
+ #1
+ \regex_match:n {#2}
+ \regex_extract:
+ \regex_group_end_extract_seq:N #3
}
+\cs_new_protected:Npn \regex_extract_all_aux:nnN #1#2#3
{
- \exp_args:Nccc \regex_tmp:w
- { regex_#1:nnN } { regex_#1:NnN } { regex_#1_aux:NnnN }
+ \group_begin:
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_match:n {#2}
+ \regex_group_end_extract_seq:N #3
}
-\cs_new_protected_nopar:Npn \regex_extract:
+\cs_new_protected:Npn \regex_split_aux:nnN #1#2#3
{
- \int_set_eq:NN \l_regex_submatch_start_int \l_regex_submatch_int
- \if_meaning:w \c_true_bool \g_regex_success_bool
- \prg_replicate:nn \l_regex_capturing_group_int
- {
- \tex_skip:D \l_regex_submatch_int \c_zero sp \scan_stop:
- \int_incr:N \l_regex_submatch_int
- }
- \prop_map_inline:Nn \l_regex_success_submatches_prop
+ \group_begin:
+ \regex_multi_match:n
{
- \if_num:w ##1 \c_max_int
- \exp_after:wN \regex_extract_aux_b:wn \int_use:N
- \else:
- \exp_after:wN \regex_extract_aux_e:wn \int_use:N
+ \if_num:w \l_regex_start_pos_int < \l_regex_success_pos_int
+ \regex_extract:
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \l_regex_start_pos_int sp
+ plus \tex_skip:D \l_regex_zeroth_submatch_int \scan_stop:
\fi:
- \int_eval:w \l_regex_submatch_start_int + ##1 {##2}
}
- \fi:
- }
-\cs_new_protected:Npn \regex_extract_aux_b:wn #1 < #2
- {
- \tex_skip:D #1 = #2 sp
- plus \etex_gluestretch:D \tex_skip:D #1 \scan_stop:
- }
-\cs_new_protected:Npn \regex_extract_aux_e:wn #1 > #2
- {
- \tex_skip:D #1
- = 1 \tex_skip:D #1 plus #2 sp \scan_stop:
+ #1
+ \regex_match:n {#2}
+ \tex_skip:D \l_regex_submatch_int
+ = \l_regex_start_pos_int sp plus \l_regex_max_pos_int sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
+ \if_meaning:w \c_true_bool \l_regex_empty_success_bool
+ \if_num:w \l_regex_start_pos_int = \l_regex_max_pos_int
+ \int_decr:N \l_regex_submatch_int
+ \fi:
+ \fi:
+ \regex_group_end_extract_seq:N #3
}
\cs_new_protected:Npn \regex_group_end_extract_seq:N #1
{
@@ -2227,7 +2401,7 @@
\tl_set:Nx \l_regex_internal_a_tl
{
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\c_one
{ \l_regex_submatch_int - \c_one }
\regex_extract_seq_aux:n
@@ -2235,17 +2409,24 @@
\int_compare:nNnF
{ \flag_height:n { regex_begin } + \flag_height:n { regex_end } }
= \c_zero
- { \msg_kernel_error:nn { regex } { sequence-unbalanced } }
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #1 \l_regex_internal_a_tl
+ {
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \flag_height:n { regex_end } }
+ { \flag_height:n { regex_begin } }
+ }
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1 { \l_regex_internal_a_tl }
+ }
}
\cs_new:Npn \regex_extract_seq_aux:n #1
{
\seq_item:n
{
\exp_after:wN \regex_extract_seq_aux:ww
- \int_value:w \regex_submatch_nesting_aux:n {#1} ; #1;
+ \int_value:w \regex_submatch_balance:n {#1} ; #1;
}
}
\cs_new:Npn \regex_extract_seq_aux:ww #1; #2;
@@ -2254,184 +2435,124 @@
\flag_raise:n { regex_end }
\prg_replicate:nn {-#1} { \exp_not:n { { \if_false: } \fi: } }
\fi:
- \regex_query_submatch:w #2;
+ \regex_query_submatch:n {#2}
\if_num:w #1 > \c_zero
\flag_raise:n { regex_begin }
\prg_replicate:nn {#1} { \exp_not:n { \if_false: { \fi: } } }
\fi:
}
-\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
- {
- \regex_match_aux:n
- { \regex_aux_build_match:nn {#1} {#2} }
- }
-\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
- {
- \regex_match_aux:n
- { \regex_aux_use_match:Nn #1 {#2} }
- }
-\cs_new_protected:Npn \regex_match_aux:n #1
- {
- \group_begin:
- \tl_clear:N \l_regex_every_match_tl
- \regex_disable_submatches:
- #1
- \group_end:
- \regex_aux_return:
- }
-\cs_new_protected_nopar:Npn \regex_count:nnN
- { \regex_count_aux:NnnN \regex_aux_build_match:nn }
-\cs_new_protected_nopar:Npn \regex_count:NnN
- { \regex_count_aux:NnnN \regex_aux_use_match:Nn }
-\cs_new_protected:Npn \regex_count_aux:NnnN #1#2#3#4
+\cs_new_protected_nopar:Npn \regex_extract:
{
- \group_begin:
- \regex_disable_submatches:
- \int_zero:N \l_regex_match_count_int
- \tl_set:Nn \l_regex_every_match_tl
+ \if_meaning:w \c_true_bool \g_regex_success_bool
+ \int_set_eq:NN \l_regex_zeroth_submatch_int \l_regex_submatch_int
+ \prg_replicate:nn \l_regex_capturing_group_int
{
- \int_incr:N \l_regex_match_count_int
- \regex_match_once:
+ \tex_skip:D \l_regex_submatch_int \c_zero sp \scan_stop:
+ \int_incr:N \l_regex_submatch_int
}
- #1 {#2} {#3}
- \exp_args:NNNo
- \group_end:
- \int_set:Nn #4 { \int_use:N \l_regex_match_count_int }
- }
-\cs_new_protected:Npn \regex_extract_once_aux:NnnN #1#2#3#4
- {
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl { \regex_extract: }
- #1 {#2} {#3}
- \regex_group_end_extract_seq:N #4
+ \prop_map_inline:Nn \l_regex_success_submatches_prop
+ {
+ \if_num:w ##1 \c_minus_one
+ \exp_after:wN \regex_extract_aux_e:wn \int_value:w
+ \else:
+ \exp_after:wN \regex_extract_aux_b:wn \int_value:w
+ \fi:
+ \int_eval:w \l_regex_zeroth_submatch_int + ##1 {##2}
+ }
+ \tex_skip:D \l_regex_zeroth_submatch_int
+ = \tex_the:D \tex_skip:D \l_regex_zeroth_submatch_int
+ minus \l_regex_start_pos_int sp \scan_stop:
+ \fi:
}
-\cs_new_protected:Npn \regex_extract_all_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_extract_aux_b:wn #1 < #2
{
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- { \regex_extract: \regex_match_once: }
- #1 {#2} {#3}
- \regex_group_end_extract_seq:N #4
+ \tex_skip:D #1 = #2 sp
+ plus \etex_gluestretch:D \tex_skip:D #1 \scan_stop:
}
-\cs_new_protected:Npn \regex_split_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_extract_aux_e:wn #1 > #2
{
- \group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- {
- \if_num:w \l_regex_start_index_int < \l_regex_success_index_int
- \regex_extract:
- \tex_skip:D \l_regex_submatch_start_int
- = \l_regex_start_index_int sp
- plus \tex_skip:D \l_regex_submatch_start_int \scan_stop:
- \fi:
- \regex_match_once:
- }
- #1 {#2} {#3}
- \tex_skip:D \l_regex_submatch_int
- = \l_regex_start_index_int sp
- plus \l_regex_current_index_int sp \scan_stop:
- \int_incr:N \l_regex_submatch_int
- \if_num:w \l_regex_start_index_int = \l_regex_current_index_int
- \if_meaning:w \c_true_bool \l_regex_success_empty_bool
- \int_decr:N \l_regex_submatch_int
- \fi:
- \fi:
- \regex_group_end_extract_seq:N #4
+ \tex_skip:D #1
+ = 1 \tex_skip:D #1 plus #2 sp \scan_stop:
}
-\cs_new_protected:Npn \regex_replace_once_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_replace_once_aux:nnN #1#2#3
{
\group_begin:
- \tl_clear:N \l_regex_every_match_tl
- \regex_replacement:n {#3}
- \exp_args:Nno #1 {#2} #4
- \if_meaning:w \c_true_bool \g_regex_success_bool
+ \regex_single_match:
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n { #3 }
+ \if_meaning:w \c_false_bool \g_regex_success_bool
+ \group_end:
+ \else:
\regex_extract:
- \int_set:Nn \l_regex_internal_a_int
- { \regex_nesting:n { \l_regex_submatch_start_int } }
- \if_num:w \l_regex_internal_a_int = \c_zero
- \else:
- \msg_kernel_error:nnx { regex } { replace-unbalanced }
- { \l_regex_internal_a_int }
- \fi:
+ \int_set:Nn \l_regex_balance_int
+ {
+ \regex_replacement_balance_one_match:n
+ { \l_regex_zeroth_submatch_int }
+ }
\tl_set:Nx \l_regex_internal_a_tl
{
- \if_num:w \l_regex_internal_a_int < \c_zero
- \prg_replicate:nn { - \l_regex_internal_a_int }
- { \exp_not:n { { \if_false: } \fi: } }
- \fi:
- \regex_toks_range:nn
- { \l_regex_min_index_int }
- { \tex_skip:D \l_regex_submatch_start_int }
- \regex_replacement_tl:n { \l_regex_submatch_start_int }
- \regex_toks_range:nn
- { \etex_gluestretch:D \tex_skip:D \l_regex_submatch_start_int }
- { \l_regex_max_index_int }
- \if_num:w \l_regex_internal_a_int > \c_zero
- \prg_replicate:nn { \l_regex_internal_a_int }
- { \exp_not:n { \if_false: { \fi: } } }
- \fi:
+ \regex_replacement_do_one_match:n { \l_regex_zeroth_submatch_int }
+ \regex_query_range:nn
+ { \etex_gluestretch:D \tex_skip:D \l_regex_zeroth_submatch_int }
+ { \l_regex_max_pos_int }
}
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #4 \l_regex_internal_a_tl
- \else:
- \group_end:
+ \regex_group_end_replace:N #3
\fi:
}
-\cs_new_protected:Npn \regex_replace_all_aux:NnnN #1#2#3#4
+\cs_new_protected:Npn \regex_replace_all_aux:nnN #1#2#3
{
\group_begin:
- \tl_set:Nn \l_regex_every_match_tl
- {
- \regex_extract:
- \tex_skip:D \l_regex_submatch_start_int
- = \tex_the:D \tex_skip:D \l_regex_submatch_start_int
- minus \l_regex_start_index_int sp \scan_stop:
- \regex_match_once:
- }
- \regex_replacement:n {#3}
- \exp_args:Nno #1 {#2} #4
- \int_set:Nn \l_regex_internal_a_int
+ \regex_multi_match:n { \regex_extract: }
+ #1
+ \regex_replacement:n {#2}
+ \exp_args:No \regex_match:n {#3}
+ \int_set:Nn \l_regex_balance_int
{
0
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\l_regex_capturing_group_int
{ \l_regex_submatch_int - \c_one }
- \regex_nesting:n
+ \regex_replacement_balance_one_match:n
}
- \if_num:w \l_regex_internal_a_int = \c_zero
- \else:
- \msg_kernel_error:nnx { regex } { replace-unbalanced }
- { \l_regex_internal_a_int }
- \fi:
\tl_set:Nx \l_regex_internal_a_tl
{
- \if_num:w \l_regex_internal_a_int < \c_zero
- \prg_replicate:nn { - \l_regex_internal_a_int }
- { \exp_not:n { { \if_false: } \fi: } }
- \fi:
\prg_stepwise_function:nnnN
- \l_regex_max_state_int
+ { \c_two * \l_regex_max_state_int }
\l_regex_capturing_group_int
{ \l_regex_submatch_int - \c_one }
- \regex_replace_all_aux:n
- \regex_toks_range:nn
- \l_regex_start_index_int \l_regex_max_index_int
- \if_num:w \l_regex_internal_a_int > \c_zero
- \prg_replicate:nn { \l_regex_internal_a_int }
- { \exp_not:n { \if_false: { \fi: } } }
- \fi:
+ \regex_replacement_do_one_match:n
+ \regex_query_range:nn
+ \l_regex_start_pos_int \l_regex_max_pos_int
}
- \tl_set:Nx \l_regex_internal_a_tl { \l_regex_internal_a_tl }
- \exp_args:NNNo \group_end:
- \tl_set:Nn #4 \l_regex_internal_a_tl
+ \regex_group_end_replace:N #3
}
-\cs_new:Npn \regex_replace_all_aux:n #1
+\cs_new_protected_nopar:Npn \regex_group_end_replace:N #1
{
- \regex_toks_range:nn
- { \etex_glueshrink:D \tex_skip:D #1 } { \tex_skip:D #1 }
- \regex_replacement_tl:n {#1}
+ \if_num:w \l_regex_balance_int = \c_zero
+ \else:
+ \msg_kernel_error:nnxxx { regex } { result-unbalanced }
+ { replacing }
+ { \int_max:nn { - \l_regex_balance_int } { \c_zero } }
+ { \int_max:nn { \l_regex_balance_int } { \c_zero } }
+ \fi:
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1
+ {
+ \if_int_compare:w \l_regex_balance_int < \c_zero
+ \prg_replicate:nn { - \l_regex_balance_int }
+ { { \if_false: } \fi: }
+ \fi:
+ \l_regex_internal_a_tl
+ \if_int_compare:w \l_regex_balance_int > \c_zero
+ \prg_replicate:nn { \l_regex_balance_int }
+ { \if_false: { \fi: } }
+ \fi:
+ }
+ }
}
\msg_kernel_new:nnnn { regex } { trailing-backslash }
{ Trailing~escape~character~\iow_char:N\\. }
@@ -2453,6 +2574,13 @@
`\iow_char:N\\x\{\int_to_hexadecimal:n{#1}\}'.~
The~character~code~'#1'~is~larger~than~\int_use:N \c_max_char_int.
}
+\msg_kernel_new:nnnn { regex } { invalid-quantifier }
+ { Braced~quantifier~'#1'~may~not~be~followed~by~'#2'. }
+ {
+ The~character~'#2'~is~invalid~in~the~braced~quantifier~'#1'.~
+ The~only~valid~quantifiers~are~'*',~'?',~'+',~'{<int>}',~
+ '{<min>,}'~and~'{<min>,<max>}',~followed~or~not~by~'?'.
+ }
\msg_kernel_new:nnnn { regex } { missing-rbrack }
{ Missing~right~bracket~inserted~in~regular~expression. }
{
@@ -2475,19 +2603,29 @@
LaTeX~came~across~a~closing~parenthesis~when~no~submatch~group~
was~open.~The~parenthesis~will~be~ignored.
}
-\msg_kernel_new:nnnn { regex } { class-bad-escape }
- { Invalid~escape~\c_backslash_str #1~in~character~class. }
+\msg_kernel_new:nnnn { regex } { bad-escape }
{
- The~escape~sequence~\iow_char:N\\#1~may~not~appear~within~
- a~character~class.~For~instance,~assertions~(which~match~zero~
- characters)~would~not~make~sense~there.
+ Invalid~escape~\c_backslash_str #1~
+ \regex_if_in_cs:TF { within~a~control~sequence. }
+ {
+ \regex_if_in_class:TF
+ { in~a~character~class. }
+ { following~a~category~test. }
+ }
}
-\msg_kernel_new:nnnn { regex } { catcode-bad-escape }
- { Invalid~escape~\c_backslash_str #1~following~category~test. }
{
- The~escape~sequence~\iow_char:N\\#1~may~not~appear~following~
- a~category~test~such~as~\iow_char:N\\cL~or~
- \iow_char:N\\c[\iow_char:N\^BE].
+ The~escape~sequence~\iow_char:N\\#1~may~not~appear~
+ \regex_if_in_cs:TF
+ {
+ within~a~control~sequence~test~introduced~by~
+ \iow_char:N\\c\iow_char:N\{.
+ }
+ {
+ \regex_if_in_class:TF
+ { within~a~character~class~ }
+ { following~a~category~test~such~as~\iow_char:N\\cL~ }
+ because~it~does~not~match~exactly~one~character.
+ }
}
\msg_kernel_new:nnnn { regex } { range-missing-end }
{ Invalid~end-point~for~range~'#1-#2'~in~character~class. }
@@ -2525,7 +2663,7 @@
regular~expression,~but~the~closing~']'~is~not~present.
}
\msg_kernel_new:nnnn { regex } { c-missing-category }
- { Invalid~character~'#1'~following~\iow_char\\c~escape. }
+ { Invalid~character~'#1'~following~\iow_char:N\\c~escape. }
{
In~regular~expressions,~the~\iow_char:N\\c~escape~sequence~
may~only~be~followed~by~a~left~brace,~a~left~bracket,~or~a~
@@ -2547,78 +2685,102 @@
{ encountered~an~escaped~alphanumeric~character '\iow_char:N\\#2'~ }
when~parsing~the~argument~of~an~'\iow_char:N\\u\iow_char:N\{...\}'~escape.
}
-\msg_kernel_new:nnnn { regex } { sequence-unbalanced }
+\msg_kernel_new:nnnn { regex } { posix-unsupported }
+ { POSIX~collating~element~'[#1 ~ #1]'~not~supported. }
{
- Missing~
- \flag_if_raised:nTF { regex_end }
- {
- left~
- \flag_if_raised:nTF { regex_begin }
- { and~right~braces } { brace }
- }
- { right~brace }
- \ inserted~in~extracted~match.
+ The~[.foo.]~and~[=bar=]~syntaxes~have~a~special~meaning~in~POSIX~
+ regular~expressions.~This~is~not~supported~by~LaTeX.~Maybe~you~
+ forgot~to~escape~a~left~bracket~in~a~character~class?
}
+\msg_kernel_new:nnnn { regex } { posix-unknown }
+ { POSIX~class~[:#1:]~unknown. }
{
- LaTeX~was~asked~to~extract~submatches~or~split~a~token~list~
- according~to~a~given~regular~expression,~but~some~of~the~resulting~
- items~were~not~balanced.
+ [:#1:]~is~not~among~the~known~POSIX~classes~
+ [:alnum:],~[:alpha:],~[:ascii:],~[:blank:],~
+ [:cntrl:],~[:digit:],~[:graph:],~[:lower:],~
+ [:print:],~[:punct:],~[:space:],~[:upper:],~
+ [:word:],~and~[:xdigit:].
}
-\msg_kernel_new:nnnn { regex } { replace-unbalanced }
- { The~result~of~a~replacement~does~not~have~balanced~braces. }
+\msg_kernel_new:nnnn { regex } { posix-missing-close }
+ { Missing~closing~':]'~for~POSIX~class. }
+ { The~POSIX~syntax~'#1'~must~be~followed~by~':]',~not~'#2'. }
+\msg_kernel_new:nnnn { regex } { result-unbalanced }
+ { Missing~brace~inserted~when~#1. }
{
- LaTeX~was~asked~to~do~some~regular~expression~replacement,~
+ LaTeX~was~asked~to~do~some~regular~expression~operation,~
and~the~resulting~token~list~would~not~have~the~same~number~
- of~begin-group~and~end-group~tokens. \\ \\
- \ \ \ \
- \prg_case_int:nnn {#1}
- {
- { -1 } { A~left~brace~was }
- { 1 } { A~right~brace~was }
- }
- {
- \int_abs:n {#1} ~
- \int_compare:nNnTF {#1} < \c_zero { left } { right } ~
- braces ~ were
- }
- \ inserted.
+ of~begin-group~and~end-group~tokens.~Braces~were~inserted:~
+ #2~left,~#3~right.
}
-\msg_kernel_new:nnnn { regex } { not-nfa }
- { This~is~not~a~regular~expression~variable. }
+\msg_kernel_new:nnnn { regex } { unknown-option }
+ { Unknown~option~`#1'~for~regular~expressions. }
{
- LaTeX~was~expecting~`#1'~to~be~a~regular~expression~variable.\\
- This~control~sequence~is~not~a~regex~variable.~It's~current~meaning~
- is~\\\\
- \ \ \ \ \token_to_str:N #1 = \token_to_meaning:N #1 .
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
}
-\msg_kernel_new:nnn { regex } { nfa-misused }
- { Automaton~#1 used~incorrectly. }
-\msg_kernel_new:nnnn { regex } { c-command }
- { Misused~\iow_char:N\\c~or\iow_char:N\\C~command~in~a~#1. }
+\msg_kernel_new:nnnn { regex } { replacement-c }
+ { Misused~\iow_char:N\\c~command~in~a~replacement~text. }
{
- In~a~#1,~the~\iow_char:N\\C~escape~sequence~
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~
- or~a~brace~group,~not~by~'#2'.
- }
-\msg_kernel_new:nnnn { regex } { unknown-option }
- { Unknown~option~`#1'~for~regular~expressions. }
- {
- LaTeX~came~across~something~like~`(?#1)'~in~a~regular~expression,~
- but~the~option~`#1'~is~not~known.~It~will~be~ignored.
+ or~a~brace~group,~not~by~'#1'.
}
-\msg_kernel_new:nnnn { regex } { invalid-in-option }
- { Invalid~character~in~option~of~a~regular~expression. }
+\msg_kernel_new:nnnn { regex } { replacement-u }
+ { Misused~\iow_char:N\\u~command~in~a~replacement~text. }
{
- The~character~or~escape~sequence~`#1'~is~not~defined~
- as~an~option~within~regular~expressions.
+ In~a~replacement~text,~the~\iow_char:N\\u~escape~sequence~
+ must~be~~followed~by~a~brace~group~holding~the~name~of~the~
+ variable~to~use.
}
-\msg_kernel_new:nnnn { regex } { g-command }
+\msg_kernel_new:nnnn { regex } { replacement-g }
{ Missing~brace~for~the~\iow_char:N\\g~construction~in~a~replacement~text. }
{
In~the~replacement~text~for~a~regular~expression~search,~
submatches~are~represented~either~as~\iow_char:N \\g{dd..d},~
or~\\d,~where~`d'~are~single~digits.~Here,~a~brace~is~missing.
}
+\msg_kernel_new:nnnn { regex } { replacement-catcode-end }
+ {
+ Missing~character~for~the~\iow_char:N\\c<category><character>~
+ construction~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~\iow_char:N\\c~escape~sequence~
+ can~be~followed~by~one~of~the~letters~ABCDELMOPSTU~representing~
+ the~character~category.~Then,~a~character~must~follow.~LaTeX~
+ reached~the~end~of~the~replacement~when~looking~for~that.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-null-space }
+ { TeX~cannot~build~a~space~token~with~character~code~0. }
+ {
+ You~asked~for~a~character~token~with~category~'space',~
+ and~character~code~0,~for~instance~through~
+ '\iow_char:N\\cS\iow_char:N\\x00'.~
+ This~specific~case~is~impossible~and~will~be~replaced~
+ by~a~normal~space.
+ }
+\msg_kernel_new:nnnn { regex } { replacement-missing-rbrace }
+ { Missing~right~brace~inserted~in~replacement~text. }
+ {
+ There~were~\int_use:N \l_regex_replacement_csnames_int \
+ missing~right~braces.
+ }
+\cs_new:Npn \regex_msg_repeated:nnN #1#2#3
+ {
+ \str_if_eq:xxF { #1 #2 } { 1 0 }
+ {
+ , ~ repeated ~
+ \prg_case_int:nnn {#2}
+ {
+ { -1 } { #1~or~more~times,~\bool_if:NTF #3 { lazy } { greedy } }
+ { 0 } { #1~times }
+ }
+ {
+ between~#1~and~\int_eval:n {#1+#2}~times,~
+ \bool_if:NTF #3 { lazy } { greedy }
+ }
+ }
+ }
%%
%%
%% 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 d9ef6271899..88042a5337d 100644
--- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty
@@ -46,13 +46,15 @@
%%
%% -----------------------------------------------------------------------
\RequirePackage{expl3}
-\GetIdInfo$Id: l3str.dtx 3257 2012-01-29 13:37:03Z bruno $
+\GetIdInfo$Id: l3str.dtx 3460 2012-02-26 12:16:41Z will $
{L3 Experimental Strings}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
-\RequirePackage{l3tl-analysis,l3flag}
+\RequirePackage{l3tl-analysis,l3tl-build,l3flag}
\cs_if_exist:NF \use_ii_i:nn
{ \cs_new:Npn \use_ii_i:nn #1#2 { #2 #1 } }
+\cs_new_eq:NN \str_new:N \tl_new:N
+\cs_generate_variant:Nn \str_new:N { c }
\tl_map_inline:nn
{
{ set }
@@ -435,15 +437,17 @@
\prg_return_true:
\fi:
}
+\cs_new_eq:NN \str_show:n \tl_show:n
+\cs_new_eq:NN \str_show:N \tl_show:N
+\cs_generate_variant:Nn \str_show:N { c }
\group_begin:
- \tl_clear:N \l_str_internal_tl
\char_set_catcode_other:n { \c_zero }
- \tl_gset:Nx \g_str_result_tl { \tl_to_str:n { 0123456789ABCDEF } }
- \exp_args:No \tl_map_inline:nn { \g_str_result_tl " }
+ \tl_set:Nx \l_str_internal_tl { \tl_to_str:n { 0123456789ABCDEF } }
+ \exp_args:No \tl_map_inline:nn { \l_str_internal_tl " }
{ \char_set_lccode:nn {`#1} { \c_zero } }
- \tl_map_inline:Nn \g_str_result_tl
+ \tl_map_inline:Nn \l_str_internal_tl
{
- \tl_map_inline:Nn \g_str_result_tl
+ \tl_map_inline:Nn \l_str_internal_tl
{
\char_set_lccode:nn { \c_zero } {"#1##1}
\tl_to_lowercase:n
@@ -451,12 +455,9 @@
\tl_const:cx
{ c_str_byte_ \int_eval:n {"#1##1} _tl }
{ ^^@ #1 ##1 }
- \tl_put_right:Nn \l_str_internal_tl { ^^@ }
}
}
}
- \tl_const:Nx \c_str_positive_bytes_tl
- { \exp_after:wN \use_none:n \l_str_internal_tl }
\group_end:
\tl_const:cn { c_str_byte_-1_tl } { { } \use_none:n { } }
\cs_new:Npn \str_output_byte:n #1
@@ -744,53 +745,20 @@
{
\cs_new_protected_nopar:Npn \str_convert_encode_:
{
- \tex_lccode:D \c_zero \c_zero
- \int_zero:N \l_str_internal_int
- \tl_set_eq:NN \l_str_internal_tl \g_str_result_tl
- \tl_gclear:N \g_str_result_tl
- \exp_after:wN \str_encode_native_loop:w
- \l_str_internal_tl \s_tl { \q_stop \prg_map_break: } \s_tl
- \prg_break_point:n { \str_encode_native_flush: }
+ \int_zero:N \l_tl_build_offset_int
+ \tl_gset_build_x:Nw \g_str_result_tl
+ \exp_after:wN \str_encode_native_loop:w
+ \g_str_result_tl \s_tl { \q_stop \prg_map_break: } \s_tl
+ \prg_break_point:n { }
+ \tl_build_end:
}
\cs_new_protected:Npn \str_encode_native_loop:w #1 \s_tl #2 \s_tl
{
\use_none_delimit_by_q_stop:w #2 \q_stop
- \if_num:w \l_str_internal_int = \c_two_hundred_fifty_five
- \str_encode_native_flush:
- \l_str_internal_int \c_zero
- \fi:
- \tex_advance:D \l_str_internal_int \c_one
\tex_lccode:D \l_str_internal_int \int_eval:w #2 \int_eval_end:
+ \tl_to_lowercase:n { \tl_build_one:n { ^^@ } }
\str_encode_native_loop:w
}
- \cs_new_protected_nopar:Npn \str_encode_native_flush:
- {
- \use:x
- {
- \tl_to_lowercase:n
- {
- \tl_gput_right:Nx \exp_not:N \g_str_result_tl
- {
- \exp_after:wN \str_encode_native_filter:N
- \c_str_positive_bytes_tl
- { ? = \c_zero \fi: \prg_map_break: }
- \prg_break_point:n { }
- }
- }
- }
- }
- \cs_new:Npn \str_encode_native_filter:N #1
- {
- \if_num:w `#1 > \l_str_internal_int
- \exp_after:wN \prg_map_break:
- \fi:
- \if_num:w \tex_lccode:D `#1 = \c_zero
- ^^@
- \else:
- #1
- \fi:
- \str_encode_native_filter:N
- }
}
\group_end:
\cs_new_protected:Npn \str_declare_eight_bit_encoding:nnn #1#2#3
diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-analysis.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-analysis.sty
index 6f82415d186..545d7ce7069 100644
--- a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-analysis.sty
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-analysis.sty
@@ -52,6 +52,7 @@
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
\RequirePackage{l3str}
\scan_new:N \s_tl
+\tl_new:N \l_tl_analysis_internal_tl
\cs_new_eq:NN \l_tl_analysis_token ?
\cs_new_eq:NN \l_tl_analysis_char_token ?
\int_new:N \l_tl_analysis_normal_int
@@ -370,8 +371,8 @@
}
\cs_new_protected:Npn \tl_show_analysis:n #1
{
- \tl_set:Nn \l_tl_tmpa_tl {#1}
- \tl_show_analysis:N \l_tl_tmpa_tl
+ \tl_set:Nn \l_tl_analysis_internal_tl {#1}
+ \tl_show_analysis:N \l_tl_analysis_internal_tl
}
\cs_new:Npn \tl_show_analysis_loop:wNw #1 \s_tl #2 #3 \s_tl
{
@@ -396,37 +397,55 @@
\exp_after:wN \token_to_str:N #1 ~
( \exp_after:wN \token_to_meaning:N #1 )
}
+\cs_new:Npn \tl_show_analysis_value:N #1
+ {
+ \token_if_expandable:NF #1
+ {
+ \token_if_chardef:NTF #1 \prg_map_break: { }
+ \token_if_mathchardef:NTF #1 \prg_map_break: { }
+ \token_if_dim_register:NTF #1 \prg_map_break: { }
+ \token_if_int_register:NTF #1 \prg_map_break: { }
+ \token_if_skip_register:NTF #1 \prg_map_break: { }
+ \token_if_toks_register:NTF #1 \prg_map_break: { }
+ \use_none:nnnn
+ \prg_break_point:n { }
+ \use:n { = \tex_the:D #1 }
+ }
+ }
\cs_new:Npn \tl_show_analysis_cs:n #1
{ \exp_args:No \tl_show_analysis_long:nn {#1} { control~sequence= } }
\cs_new:Npn \tl_show_analysis_active:n #1
{ \exp_args:No \tl_show_analysis_long:nn {#1} { active~character= } }
\cs_new:Npn \tl_show_analysis_long:nn #1
{
- \exp_args:Noo \tl_show_analysis_long_aux:nnn
+ \tl_show_analysis_long_aux:oofn
{ \token_to_str:N #1 }
{ \token_to_meaning:N #1 }
+ { \tl_show_analysis_value:N #1 }
}
-\cs_new:Npn \tl_show_analysis_long_aux:nnn #1#2#3
+\cs_new:Npn \tl_show_analysis_long_aux:nnnn #1#2#3#4
{
\int_compare:nNnTF
- { \str_length:n { #1 ~ ( #3 #2 ) } }
+ { \str_length:n { #1 ~ ( #4 #2 #3 ) } }
> { \l_iow_line_length_int - \c_three }
{
- \str_substr:nnn { #1 ~ ( #3 #2 ) } \c_zero
+ \str_substr:nnn { #1 ~ ( #4 #2 #3 ) } \c_zero
{
\l_iow_line_length_int - \c_three
- \str_length:N \c_tl_show_analysis_etc_str
}
\c_tl_show_analysis_etc_str
}
- { #1 ~ ( #3 #2 ) }
+ { #1 ~ ( #4 #2 #3 ) }
}
+\cs_generate_variant:Nn \tl_show_analysis_long_aux:nnnn { oof }
\tl_const:Nx \c_tl_show_analysis_etc_str % (
{ \token_to_str:N \ETC.) }
\msg_kernel_new:nnn { tl-analysis } { show }
{
The~token~list~
- \str_if_eq:nnF {#1} { \l_tl_tmpa_tl } { \token_to_str:N #1 ~ }
+ \str_if_eq:nnF {#1} { \l_tl_analysis_internal_tl }
+ { \token_to_str:N #1 ~ }
\tl_if_empty:NTF #1
{ is~empty }
{ contains~the~tokens: }
diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-build.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-build.sty
new file mode 100644
index 00000000000..415a89f4897
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3tl-build.sty
@@ -0,0 +1,120 @@
+%%
+%% This is file `l3tl-build.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3tl-build.dtx (with options: `package')
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+%% File: l3tl-build.dtx Copyright (C) 2011-2012 The LaTeX3 Project
+%%
+%% It may be distributed and/or modified under the conditions of the
+%% LaTeX Project Public License (LPPL), either version 1.3c of this
+%% license or (at your option) any later version. The latest version
+%% of this license is in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3experimental bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX3 Project.
+%%
+%% -----------------------------------------------------------------------
+\RequirePackage{expl3}
+\GetIdInfo$Id: l3tl-build.dtx 3039 2011-12-08 09:22:35Z bruno $
+ {L3 Experimental Build of Token lists}
+\ProvidesExplPackage
+ {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+\int_new:N \l_tl_build_start_index_int
+\int_new:N \l_tl_build_index_int
+\tl_new:N \l_tl_build_result_tl
+\cs_new_protected_nopar:Npn \tl_build_unpack:
+ {
+ \tl_put_right:Nx \l_tl_build_result_tl
+ {
+ \exp_after:wN \tl_build_unpack_loop:w
+ \int_use:N \l_tl_build_start_index_int ;
+ \prg_break_point:n { }
+ }
+ }
+\cs_new:Npn \tl_build_unpack_loop:w #1 ;
+ {
+ \if_num:w #1 = \l_tl_build_index_int
+ \exp_after:wN \prg_map_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \exp_stop_f:
+ \exp_after:wN \tl_build_unpack_loop:w
+ \int_use:N \int_eval:w #1 + \c_one ;
+ }
+\cs_new_protected_nopar:Npn \tl_set_build:Nw
+ { \tl_set_build_aux:NNw \tl_set:Nn }
+\cs_new_protected_nopar:Npn \tl_set_build_x:Nw
+ { \tl_set_build_aux:NNw \tl_set:Nx }
+\cs_new_protected_nopar:Npn \tl_gset_build:Nw
+ { \tl_set_build_aux:NNw \tl_gset:Nn }
+\cs_new_protected_nopar:Npn \tl_gset_build_x:Nw
+ { \tl_set_build_aux:NNw \tl_gset:Nx }
+\cs_new_protected:Npn \tl_set_build_aux:NNw #1#2
+ {
+ \group_begin:
+ \cs_set_nopar:Npn \tl_build_end_assignment:n
+ { \group_end: #1 #2 }
+ \int_zero:N \l_tl_build_start_index_int
+ \int_zero:N \l_tl_build_index_int
+ \tl_clear:N \l_tl_build_result_tl
+ }
+\cs_new_protected_nopar:Npn \tl_build_end:
+ {
+ \tl_build_unpack:
+ \exp_args:No
+ \tl_build_end_assignment:n \l_tl_build_result_tl
+ }
+\cs_new_eq:NN \tl_build_end_assignment:n \use_none:n
+\cs_new_protected:Npn \tl_build_one:n #1
+ {
+ \tex_toks:D \l_tl_build_index_int {#1}
+ \tex_advance:D \l_tl_build_index_int \c_one
+ \if_num:w \l_tl_build_index_int > \c_max_register_int
+ \tl_build_unpack:
+ \l_tl_build_index_int \l_tl_build_start_index_int
+ \fi:
+ }
+\cs_new_protected:Npn \tl_build_one:o #1
+ {
+ \tex_toks:D \l_tl_build_index_int \exp_after:wN {#1}
+ \tex_advance:D \l_tl_build_index_int \c_one
+ \if_num:w \l_tl_build_index_int > \c_max_register_int
+ \tl_build_unpack:
+ \l_tl_build_index_int \l_tl_build_start_index_int
+ \fi:
+ }
+\cs_new_protected:Npn \tl_build_one:x #1
+ { \use:x { \tl_build_one:n {#1} } }
+%%
+%%
+%% End of file `l3tl-build.sty'.
diff --git a/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty b/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty
index 9d4a42407e3..ab210356d5c 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 2963 2011-11-15 22:06:41Z joseph $
+\GetIdInfo$Id: xcoffins.dtx 3385 2012-02-12 20:37:09Z bruno $
{L3 Experimental design-level coffins}
\ProvidesExplPackage
{\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}