mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 14:49:36 +00:00
feat(regular_expression): support buffer boundary assertions (#27048)
Port https://github.com/jviereck/regjsparser/pull/166 with spec-conforming buffer boundaries and all 90 upstream cases. AI-assisted.
This commit is contained in:
5 files changed
+117
-1
No files matched your search
@@ -10,3 +10,4 @@ And, Stage 4 proposals
|
||||
|
||||
- https://github.com/tc39/proposal-duplicate-named-capturing-groups
|
||||
- https://github.com/tc39/proposal-regexp-modifiers
|
||||
- https://github.com/tc39/proposal-regexp-buffer-boundaries
|
||||
@@ -55,7 +55,7 @@ pub enum Term<'a> {
|
||||
}
|
||||
|
||||
/// Simple form of assertion.
|
||||
/// e.g. `^`, `$`, `\b`, `\B`
|
||||
/// e.g. `^`, `$`, `\b`, `\B`, `\A`, `\z`, `\Z`
|
||||
#[ast]
|
||||
#[derive(Debug)]
|
||||
#[generate_derive(CloneIn, ContentEq)]
|
||||
@@ -72,6 +72,13 @@ pub enum BoundaryAssertionKind {
|
||||
End = 1,
|
||||
Boundary = 2,
|
||||
NegativeBoundary = 3,
|
||||
/// `\A`: start of input, regardless of multiline mode. Unicode mode only.
|
||||
StartBuffer = 4,
|
||||
/// `\z`: end of input, regardless of multiline mode. Unicode mode only.
|
||||
EndBuffer = 5,
|
||||
/// `\Z`: end of input or before a final line terminator or CRLF sequence,
|
||||
/// regardless of multiline mode. Unicode mode only.
|
||||
EndBufferOptionalNewline = 6,
|
||||
}
|
||||
|
||||
/// Lookaround assertion.
|
||||
|
||||
@@ -74,6 +74,9 @@ impl Display for BoundaryAssertionKind {
|
||||
Self::End => "$",
|
||||
Self::Boundary => r"\b",
|
||||
Self::NegativeBoundary => r"\B",
|
||||
Self::StartBuffer => r"\A",
|
||||
Self::EndBuffer => r"\z",
|
||||
Self::EndBufferOptionalNewline => r"\Z",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
@@ -533,6 +536,10 @@ mod test {
|
||||
(r"/(?im-s:.)/u", None),
|
||||
(r"/(?m-is:.)/v", None),
|
||||
(r"/(?smi:.)/v", Some(r"/(?ims:.)/v")),
|
||||
// Buffer boundaries
|
||||
(r"/\A\z\Z/", None),
|
||||
(r"/\A\z\Z/u", None),
|
||||
(r"/\A\z\Z/v", None),
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -123,6 +123,83 @@ mod test {
|
||||
(r"(?si-m:.)", ""),
|
||||
(r"(?im-s:.)", "v"),
|
||||
(r"(?ims-:.)", ""),
|
||||
// Buffer boundaries
|
||||
(r"\A", ""),
|
||||
(r"\A", "u"),
|
||||
(r"\A", "v"),
|
||||
(r"\z", ""),
|
||||
(r"\z", "u"),
|
||||
(r"\z", "v"),
|
||||
(r"\Z", ""),
|
||||
(r"\Z", "u"),
|
||||
(r"\Z", "v"),
|
||||
(r"\A\z", ""),
|
||||
(r"\A\z", "u"),
|
||||
(r"\A\z", "v"),
|
||||
(r"\A\Z", ""),
|
||||
(r"\A\Z", "u"),
|
||||
(r"\A\Z", "v"),
|
||||
(r"\A\Z\z", ""),
|
||||
(r"\A\Z\z", "u"),
|
||||
(r"\A\Z\z", "v"),
|
||||
(r"\Afoo\z", ""),
|
||||
(r"\Afoo\z", "u"),
|
||||
(r"\Afoo\z", "v"),
|
||||
(r"\Afoo\Z", ""),
|
||||
(r"\Afoo\Z", "u"),
|
||||
(r"\Afoo\Z", "v"),
|
||||
(r"\A|\z", ""),
|
||||
(r"\A|\z", "u"),
|
||||
(r"\A|\z", "v"),
|
||||
(r"(\A)", ""),
|
||||
(r"(\A)", "u"),
|
||||
(r"(\A)", "v"),
|
||||
(r"(?=\A)", ""),
|
||||
(r"(?=\A)", "u"),
|
||||
(r"(?=\A)", "v"),
|
||||
(r"\A*", ""),
|
||||
(r"\z+", ""),
|
||||
(r"\Z{2}", ""),
|
||||
(r"[\A]", ""),
|
||||
(r"[\z\Z]", ""),
|
||||
(r"\\A", ""),
|
||||
(r"\\A", "u"),
|
||||
(r"\\A", "v"),
|
||||
(r"a\Zb", ""),
|
||||
(r"a\Zb", "u"),
|
||||
(r"a\Zb", "v"),
|
||||
(r"(?i:\A)", ""),
|
||||
(r"(?i:\A)", "u"),
|
||||
(r"(?i:\A)", "v"),
|
||||
(r"(?m:\z)", ""),
|
||||
(r"(?m:\z)", "u"),
|
||||
(r"(?m:\z)", "v"),
|
||||
(r"(?s:\Z)", ""),
|
||||
(r"(?s:\Z)", "u"),
|
||||
(r"(?s:\Z)", "v"),
|
||||
(r"(?-i:\A)", ""),
|
||||
(r"(?-i:\A)", "u"),
|
||||
(r"(?-i:\A)", "v"),
|
||||
(r"(?-m:\Z)", ""),
|
||||
(r"(?-m:\Z)", "u"),
|
||||
(r"(?-m:\Z)", "v"),
|
||||
(r"(?m-s:\Afoo\z)", ""),
|
||||
(r"(?m-s:\Afoo\z)", "u"),
|
||||
(r"(?m-s:\Afoo\z)", "v"),
|
||||
(r"(?ims:a\Zb)", ""),
|
||||
(r"(?ims:a\Zb)", "u"),
|
||||
(r"(?ims:a\Zb)", "v"),
|
||||
(r"(?i:\A|\z)", ""),
|
||||
(r"(?i:\A|\z)", "u"),
|
||||
(r"(?i:\A|\z)", "v"),
|
||||
(r"(?m:\Z)+", ""),
|
||||
(r"(?m:\Z)+", "u"),
|
||||
(r"(?m:\Z)+", "v"),
|
||||
(r"(?m:\A*)", ""),
|
||||
(r"(?i:[\z])", ""),
|
||||
(r"(?m:(?-m:\A)\z)", ""),
|
||||
(r"(?m:(?-m:\A)\z)", "u"),
|
||||
(r"(?m:(?-m:\A)\z)", "v"),
|
||||
] {
|
||||
let res =
|
||||
LiteralParser::new(&allocator, pattern_text, Some(flags_text), Options::default())
|
||||
@@ -218,6 +295,21 @@ mod test {
|
||||
(r"(?i", ""),
|
||||
(r"(?i-", ""),
|
||||
(r"(?i-s", ""),
|
||||
// Buffer boundaries
|
||||
(r"\A*", "u"),
|
||||
(r"\A*", "v"),
|
||||
(r"\z+", "u"),
|
||||
(r"\z+", "v"),
|
||||
(r"\Z{2}", "u"),
|
||||
(r"\Z{2}", "v"),
|
||||
(r"[\A]", "u"),
|
||||
(r"[\A]", "v"),
|
||||
(r"[\z\Z]", "u"),
|
||||
(r"[\z\Z]", "v"),
|
||||
(r"(?m:\A*)", "u"),
|
||||
(r"(?m:\A*)", "v"),
|
||||
(r"(?i:[\z])", "u"),
|
||||
(r"(?i:[\z])", "v"),
|
||||
] {
|
||||
assert!(
|
||||
LiteralParser::new(&allocator, pattern_text, Some(flags_text), Options::default())
|
||||
|
||||
@@ -231,6 +231,9 @@ impl<'a> PatternParser<'a> {
|
||||
// Assertion[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
|
||||
// ^
|
||||
// $
|
||||
// [+UnicodeMode] \A
|
||||
// [+UnicodeMode] \z
|
||||
// [+UnicodeMode] \Z
|
||||
// \b
|
||||
// \B
|
||||
// [+UnicodeMode] (?= Disjunction[+UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
@@ -255,6 +258,12 @@ impl<'a> PatternParser<'a> {
|
||||
Some(ast::BoundaryAssertionKind::Boundary)
|
||||
} else if self.reader.eat2('\\', 'B') {
|
||||
Some(ast::BoundaryAssertionKind::NegativeBoundary)
|
||||
} else if self.state.unicode_mode && self.reader.eat2('\\', 'A') {
|
||||
Some(ast::BoundaryAssertionKind::StartBuffer)
|
||||
} else if self.state.unicode_mode && self.reader.eat2('\\', 'z') {
|
||||
Some(ast::BoundaryAssertionKind::EndBuffer)
|
||||
} else if self.state.unicode_mode && self.reader.eat2('\\', 'Z') {
|
||||
Some(ast::BoundaryAssertionKind::EndBufferOptionalNewline)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user