mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
parent
9dc4ee9c98
commit
b5b0af98cb
16 changed files with 886 additions and 114 deletions
|
|
@ -1522,18 +1522,23 @@ const _: () = {
|
|||
assert!(offset_of!(CapturingGroup, name) == 8usize);
|
||||
assert!(offset_of!(CapturingGroup, body) == 24usize);
|
||||
|
||||
assert!(size_of::<IgnoreGroup>() == 56usize);
|
||||
assert!(size_of::<IgnoreGroup>() == 64usize);
|
||||
assert!(align_of::<IgnoreGroup>() == 8usize);
|
||||
assert!(offset_of!(IgnoreGroup, span) == 0usize);
|
||||
assert!(offset_of!(IgnoreGroup, enabling_modifiers) == 8usize);
|
||||
assert!(offset_of!(IgnoreGroup, disabling_modifiers) == 11usize);
|
||||
assert!(offset_of!(IgnoreGroup, body) == 16usize);
|
||||
assert!(offset_of!(IgnoreGroup, modifiers) == 8usize);
|
||||
assert!(offset_of!(IgnoreGroup, body) == 24usize);
|
||||
|
||||
assert!(size_of::<ModifierFlags>() == 3usize);
|
||||
assert!(align_of::<ModifierFlags>() == 1usize);
|
||||
assert!(offset_of!(ModifierFlags, ignore_case) == 0usize);
|
||||
assert!(offset_of!(ModifierFlags, sticky) == 1usize);
|
||||
assert!(offset_of!(ModifierFlags, multiline) == 2usize);
|
||||
assert!(size_of::<Modifiers>() == 16usize);
|
||||
assert!(align_of::<Modifiers>() == 4usize);
|
||||
assert!(offset_of!(Modifiers, span) == 0usize);
|
||||
assert!(offset_of!(Modifiers, enabling) == 8usize);
|
||||
assert!(offset_of!(Modifiers, disabling) == 11usize);
|
||||
|
||||
assert!(size_of::<Modifier>() == 3usize);
|
||||
assert!(align_of::<Modifier>() == 1usize);
|
||||
assert!(offset_of!(Modifier, ignore_case) == 0usize);
|
||||
assert!(offset_of!(Modifier, multiline) == 1usize);
|
||||
assert!(offset_of!(Modifier, sticky) == 2usize);
|
||||
|
||||
assert!(size_of::<IndexedReference>() == 12usize);
|
||||
assert!(align_of::<IndexedReference>() == 4usize);
|
||||
|
|
@ -3059,18 +3064,23 @@ const _: () = {
|
|||
assert!(offset_of!(CapturingGroup, name) == 8usize);
|
||||
assert!(offset_of!(CapturingGroup, body) == 16usize);
|
||||
|
||||
assert!(size_of::<IgnoreGroup>() == 40usize);
|
||||
assert!(size_of::<IgnoreGroup>() == 48usize);
|
||||
assert!(align_of::<IgnoreGroup>() == 4usize);
|
||||
assert!(offset_of!(IgnoreGroup, span) == 0usize);
|
||||
assert!(offset_of!(IgnoreGroup, enabling_modifiers) == 8usize);
|
||||
assert!(offset_of!(IgnoreGroup, disabling_modifiers) == 11usize);
|
||||
assert!(offset_of!(IgnoreGroup, body) == 16usize);
|
||||
assert!(offset_of!(IgnoreGroup, modifiers) == 8usize);
|
||||
assert!(offset_of!(IgnoreGroup, body) == 24usize);
|
||||
|
||||
assert!(size_of::<ModifierFlags>() == 3usize);
|
||||
assert!(align_of::<ModifierFlags>() == 1usize);
|
||||
assert!(offset_of!(ModifierFlags, ignore_case) == 0usize);
|
||||
assert!(offset_of!(ModifierFlags, sticky) == 1usize);
|
||||
assert!(offset_of!(ModifierFlags, multiline) == 2usize);
|
||||
assert!(size_of::<Modifiers>() == 16usize);
|
||||
assert!(align_of::<Modifiers>() == 4usize);
|
||||
assert!(offset_of!(Modifiers, span) == 0usize);
|
||||
assert!(offset_of!(Modifiers, enabling) == 8usize);
|
||||
assert!(offset_of!(Modifiers, disabling) == 11usize);
|
||||
|
||||
assert!(size_of::<Modifier>() == 3usize);
|
||||
assert!(align_of::<Modifier>() == 1usize);
|
||||
assert!(offset_of!(Modifier, ignore_case) == 0usize);
|
||||
assert!(offset_of!(Modifier, multiline) == 1usize);
|
||||
assert!(offset_of!(Modifier, sticky) == 2usize);
|
||||
|
||||
assert!(size_of::<IndexedReference>() == 12usize);
|
||||
assert!(align_of::<IndexedReference>() == 4usize);
|
||||
|
|
|
|||
|
|
@ -351,21 +351,32 @@ pub struct CapturingGroup<'a> {
|
|||
pub struct IgnoreGroup<'a> {
|
||||
#[serde(flatten)]
|
||||
pub span: Span,
|
||||
pub enabling_modifiers: Option<ModifierFlags>,
|
||||
pub disabling_modifiers: Option<ModifierFlags>,
|
||||
pub modifiers: Option<Modifiers>,
|
||||
pub body: Disjunction<'a>,
|
||||
}
|
||||
|
||||
/// Pattern modifiers in [`IgnoreGroup`].
|
||||
/// e.g. `(?i:...)`, `(?-s:...)`
|
||||
/// Modifiers in [`IgnoreGroup`].
|
||||
/// e.g. `i` in `(?i:...)`, `-s` in `(?-s:...)`
|
||||
#[ast]
|
||||
#[derive(Debug)]
|
||||
#[generate_derive(CloneIn, ContentEq, ContentHash)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||
pub struct ModifierFlags {
|
||||
pub struct Modifiers {
|
||||
#[serde(flatten)]
|
||||
pub span: Span,
|
||||
pub enabling: Option<Modifier>,
|
||||
pub disabling: Option<Modifier>,
|
||||
}
|
||||
|
||||
/// Each part of modifier in [`Modifiers`].
|
||||
#[ast]
|
||||
#[derive(Debug)]
|
||||
#[generate_derive(CloneIn, ContentEq, ContentHash)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||
pub struct Modifier {
|
||||
pub ignore_case: bool,
|
||||
pub sticky: bool,
|
||||
pub multiline: bool,
|
||||
pub sticky: bool,
|
||||
}
|
||||
|
||||
/// Backreference by index.
|
||||
|
|
|
|||
|
|
@ -258,30 +258,29 @@ impl<'a> Display for CapturingGroup<'a> {
|
|||
|
||||
impl<'a> Display for IgnoreGroup<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn write_flags(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
prefix: char,
|
||||
flags: &ModifierFlags,
|
||||
) -> fmt::Result {
|
||||
fn write_flags(f: &mut fmt::Formatter<'_>, flags: &Modifier) -> fmt::Result {
|
||||
if flags.ignore_case {
|
||||
write!(f, "{prefix}i")?;
|
||||
}
|
||||
if flags.sticky {
|
||||
write!(f, "{prefix}y")?;
|
||||
write!(f, "i")?;
|
||||
}
|
||||
if flags.multiline {
|
||||
write!(f, "{prefix}m")?;
|
||||
write!(f, "m")?;
|
||||
}
|
||||
if flags.sticky {
|
||||
write!(f, "s")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
write!(f, "(?")?;
|
||||
|
||||
if let Some(enabling) = &self.enabling_modifiers {
|
||||
write_flags(f, '\0', enabling)?;
|
||||
}
|
||||
if let Some(disabling) = &self.disabling_modifiers {
|
||||
write_flags(f, '-', disabling)?;
|
||||
if let Some(modifiers) = &self.modifiers {
|
||||
if let Some(enabling) = &modifiers.enabling {
|
||||
write_flags(f, enabling)?;
|
||||
}
|
||||
if let Some(disabling) = &modifiers.disabling {
|
||||
write!(f, "-")?;
|
||||
write_flags(f, disabling)?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ":{})", self.body)
|
||||
|
|
@ -541,6 +540,12 @@ mod test {
|
|||
(r"/[\-]/u", None),
|
||||
(r"/[\-]/v", None),
|
||||
(r"/([\-a-z]{0,31})/iu", None),
|
||||
// ES2025 ---
|
||||
(r"/(?i:.)/", None),
|
||||
(r"/(?-s:.)/", None),
|
||||
(r"/(?im-s:.)/u", None),
|
||||
(r"/(?m-is:.)/v", None),
|
||||
(r"/(?smi:.)/v", Some(r"/(?ims:.)/v")),
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -109,11 +109,6 @@ pub fn character_class_contents_invalid_operands(span: Span) -> OxcDiagnostic {
|
|||
.with_label(span)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn missing_capturing_group_name(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Missing capturing group name")).with_label(span)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn too_large_number_in_braced_quantifier(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Number is too large in braced quantifier"))
|
||||
|
|
@ -152,3 +147,13 @@ pub fn invalid_unicode_escape_sequence(span: Span) -> OxcDiagnostic {
|
|||
pub fn invalid_surrogate_pair(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Invalid surrogate pair")).with_label(span)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn invalid_modifiers(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Invalid modifiers")).with_label(span)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn unknown_modifiers(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Unknown modifiers")).with_label(span)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,20 +296,30 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for IgnoreGroup<'old_alloc> {
|
|||
fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
|
||||
IgnoreGroup {
|
||||
span: CloneIn::clone_in(&self.span, allocator),
|
||||
enabling_modifiers: CloneIn::clone_in(&self.enabling_modifiers, allocator),
|
||||
disabling_modifiers: CloneIn::clone_in(&self.disabling_modifiers, allocator),
|
||||
modifiers: CloneIn::clone_in(&self.modifiers, allocator),
|
||||
body: CloneIn::clone_in(&self.body, allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc> CloneIn<'alloc> for ModifierFlags {
|
||||
type Cloned = ModifierFlags;
|
||||
impl<'alloc> CloneIn<'alloc> for Modifiers {
|
||||
type Cloned = Modifiers;
|
||||
fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned {
|
||||
ModifierFlags {
|
||||
Modifiers {
|
||||
span: CloneIn::clone_in(&self.span, allocator),
|
||||
enabling: CloneIn::clone_in(&self.enabling, allocator),
|
||||
disabling: CloneIn::clone_in(&self.disabling, allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc> CloneIn<'alloc> for Modifier {
|
||||
type Cloned = Modifier;
|
||||
fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned {
|
||||
Modifier {
|
||||
ignore_case: CloneIn::clone_in(&self.ignore_case, allocator),
|
||||
sticky: CloneIn::clone_in(&self.sticky, allocator),
|
||||
multiline: CloneIn::clone_in(&self.multiline, allocator),
|
||||
sticky: CloneIn::clone_in(&self.sticky, allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,17 +230,23 @@ impl<'a> ContentEq for CapturingGroup<'a> {
|
|||
|
||||
impl<'a> ContentEq for IgnoreGroup<'a> {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
ContentEq::content_eq(&self.enabling_modifiers, &other.enabling_modifiers)
|
||||
&& ContentEq::content_eq(&self.disabling_modifiers, &other.disabling_modifiers)
|
||||
ContentEq::content_eq(&self.modifiers, &other.modifiers)
|
||||
&& ContentEq::content_eq(&self.body, &other.body)
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentEq for ModifierFlags {
|
||||
impl ContentEq for Modifiers {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
ContentEq::content_eq(&self.enabling, &other.enabling)
|
||||
&& ContentEq::content_eq(&self.disabling, &other.disabling)
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentEq for Modifier {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
ContentEq::content_eq(&self.ignore_case, &other.ignore_case)
|
||||
&& ContentEq::content_eq(&self.sticky, &other.sticky)
|
||||
&& ContentEq::content_eq(&self.multiline, &other.multiline)
|
||||
&& ContentEq::content_eq(&self.sticky, &other.sticky)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,17 +179,23 @@ impl<'a> ContentHash for CapturingGroup<'a> {
|
|||
|
||||
impl<'a> ContentHash for IgnoreGroup<'a> {
|
||||
fn content_hash<H: Hasher>(&self, state: &mut H) {
|
||||
ContentHash::content_hash(&self.enabling_modifiers, state);
|
||||
ContentHash::content_hash(&self.disabling_modifiers, state);
|
||||
ContentHash::content_hash(&self.modifiers, state);
|
||||
ContentHash::content_hash(&self.body, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentHash for ModifierFlags {
|
||||
impl ContentHash for Modifiers {
|
||||
fn content_hash<H: Hasher>(&self, state: &mut H) {
|
||||
ContentHash::content_hash(&self.enabling, state);
|
||||
ContentHash::content_hash(&self.disabling, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentHash for Modifier {
|
||||
fn content_hash<H: Hasher>(&self, state: &mut H) {
|
||||
ContentHash::content_hash(&self.ignore_case, state);
|
||||
ContentHash::content_hash(&self.sticky, state);
|
||||
ContentHash::content_hash(&self.multiline, state);
|
||||
ContentHash::content_hash(&self.sticky, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,10 +111,21 @@ mod test {
|
|||
(r"\1()", default()),
|
||||
(r"\1()", with_unicode_mode()),
|
||||
(r"(?<n1>..)(?<n2>..)", default()),
|
||||
// TODO: ES2025 Duplicate named capturing groups
|
||||
// ES2025 ---
|
||||
// TODO: Duplicate named capturing groups
|
||||
// (r"(?<n1>..)|(?<n1>..)", default()),
|
||||
// (r"(?<year>[0-9]{4})-[0-9]{2}|[0-9]{2}-(?<year>[0-9]{4})", default()),
|
||||
// (r"(?:(?<a>x)|(?<a>y))\k<a>", default()),
|
||||
// Modifiers
|
||||
(r"(?:.)", default()),
|
||||
(r"(?s:.)", default()),
|
||||
(r"(?ism:.)", default()),
|
||||
(r"(?-s:.)", default()),
|
||||
(r"(?-smi:.)", default()),
|
||||
(r"(?s-im:.)", default()),
|
||||
(r"(?si-m:.)", default()),
|
||||
(r"(?im-s:.)", with_unicode_sets_mode()),
|
||||
(r"(?ims-:.)", default()),
|
||||
] {
|
||||
let res = Parser::new(&allocator, source_text, *options).parse();
|
||||
if let Err(err) = res {
|
||||
|
|
@ -161,6 +172,7 @@ mod test {
|
|||
("a(?:", default()),
|
||||
("(a", default()),
|
||||
("(?<a>", default()),
|
||||
("(?<", default()),
|
||||
(r"(?<a\>.)", default()),
|
||||
(r"(?<a\>.)", with_unicode_mode()),
|
||||
(r"(?<\>.)", default()),
|
||||
|
|
@ -184,13 +196,26 @@ mod test {
|
|||
(r"[a--b&&c]", with_unicode_sets_mode()),
|
||||
(r"[\q{]", with_unicode_sets_mode()),
|
||||
(r"[\q{\a}]", with_unicode_sets_mode()),
|
||||
// TODO: ES2025 Duplicate named capturing groups
|
||||
// ES2025 ---
|
||||
// TODO: Duplicate named capturing groups
|
||||
(r"(?<n>..)|(?<n>..)", default()), // This will be valid
|
||||
// (r"(?<a>|(?<a>))", default()), // Nested, still invalid
|
||||
// (r"(?<a>|(?<a>))", default()), // Nested, still invalid
|
||||
// Modifiers
|
||||
(r"(?a:.)", default()),
|
||||
(r"(?-S:.)", default()),
|
||||
(r"(?-:.)", default()),
|
||||
(r"(?iM:.)", default()),
|
||||
(r"(?imms:.)", default()),
|
||||
(r"(?-sI:.)", default()),
|
||||
(r"(?ii-s:.)", default()),
|
||||
(r"(?i-msm:.)", default()),
|
||||
(r"(?i", default()),
|
||||
(r"(?i-", default()),
|
||||
(r"(?i-s", default()),
|
||||
] {
|
||||
assert!(
|
||||
Parser::new(&allocator, source_text, *options).parse().is_err(),
|
||||
"{source_text} should fail to parse with {options:?}!"
|
||||
"{source_text} should fail to parse with {options:?}, but passed!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -234,11 +259,16 @@ mod test {
|
|||
(r"[[z-a]]", with_unicode_sets_mode(), true),
|
||||
(r"[[[[[^[[[[\q{ng}]]]]]]]]]", with_unicode_sets_mode(), true),
|
||||
(r"[^[[[[[[[[[[[[[[[[\q{ng}]]]]]]]]]]]]]]]]]", with_unicode_sets_mode(), true),
|
||||
// ES2025 ---
|
||||
// Modifiers
|
||||
(r"(?ii:.)", default(), true),
|
||||
(r"(?-ss:.)", default(), true),
|
||||
(r"(?im-im:.)", default(), true),
|
||||
] {
|
||||
assert_eq!(
|
||||
Parser::new(&allocator, source_text, *options).parse().is_err(),
|
||||
*is_err,
|
||||
"{source_text} should early error with {options:?}!"
|
||||
"{source_text} should fail with early error with {options:?}, but passed!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,7 +304,8 @@ impl<'a> Parser<'a> {
|
|||
// \ AtomEscape[?UnicodeMode, ?NamedCaptureGroups]
|
||||
// CharacterClass[?UnicodeMode, ?UnicodeSetsMode]
|
||||
// ( GroupSpecifier[?UnicodeMode][opt] Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (?: Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ```
|
||||
fn parse_atom(&mut self) -> Result<Option<ast::Term<'a>>> {
|
||||
let span_start = self.reader.offset();
|
||||
|
|
@ -345,11 +346,6 @@ impl<'a> Parser<'a> {
|
|||
))));
|
||||
}
|
||||
|
||||
// (?: Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(ignore_group) = self.parse_ignore_group()? {
|
||||
return Ok(Some(ast::Term::IgnoreGroup(Box::new_in(ignore_group, self.allocator))));
|
||||
}
|
||||
|
||||
// ( GroupSpecifier[?UnicodeMode][opt] Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ( Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(capturing_group) = self.parse_capturing_group()? {
|
||||
|
|
@ -359,6 +355,13 @@ impl<'a> Parser<'a> {
|
|||
))));
|
||||
}
|
||||
|
||||
// (?: Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(ignore_group) = self.parse_ignore_group()? {
|
||||
return Ok(Some(ast::Term::IgnoreGroup(Box::new_in(ignore_group, self.allocator))));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +372,8 @@ impl<'a> Parser<'a> {
|
|||
// \ [lookahead = c]
|
||||
// CharacterClass[~UnicodeMode, ~UnicodeSetsMode]
|
||||
// ( GroupSpecifier[~UnicodeMode][opt] Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (?: Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// InvalidBracedQuantifier
|
||||
// ExtendedPatternCharacter
|
||||
// ```
|
||||
|
|
@ -414,13 +418,8 @@ impl<'a> Parser<'a> {
|
|||
))));
|
||||
}
|
||||
|
||||
// (?: Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(ignore_group) = self.parse_ignore_group()? {
|
||||
return Ok(Some(ast::Term::IgnoreGroup(Box::new_in(ignore_group, self.allocator))));
|
||||
}
|
||||
|
||||
// ( GroupSpecifier[~UnicodeMode][opt] Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ( Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ( GroupSpecifier[?UnicodeMode][opt] Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ( Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(capturing_group) = self.parse_capturing_group()? {
|
||||
return Ok(Some(ast::Term::CapturingGroup(Box::new_in(
|
||||
capturing_group,
|
||||
|
|
@ -428,6 +427,13 @@ impl<'a> Parser<'a> {
|
|||
))));
|
||||
}
|
||||
|
||||
// (?: Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
if let Some(ignore_group) = self.parse_ignore_group()? {
|
||||
return Ok(Some(ast::Term::IgnoreGroup(Box::new_in(ignore_group, self.allocator))));
|
||||
}
|
||||
|
||||
// InvalidBracedQuantifier
|
||||
let span_start = self.reader.offset();
|
||||
if self.consume_quantifier()?.is_some() {
|
||||
|
|
@ -1491,6 +1497,7 @@ impl<'a> Parser<'a> {
|
|||
// ```
|
||||
fn parse_capturing_group(&mut self) -> Result<Option<ast::CapturingGroup<'a>>> {
|
||||
let span_start = self.reader.offset();
|
||||
let checkpoint = self.reader.checkpoint();
|
||||
|
||||
if self.reader.eat('(') {
|
||||
let mut group_name = None;
|
||||
|
|
@ -1498,10 +1505,11 @@ impl<'a> Parser<'a> {
|
|||
// GroupSpecifier is optional, but if it exists, `?` is also required
|
||||
if self.reader.eat('?') {
|
||||
let Some(name) = self.consume_group_name()? else {
|
||||
return Err(diagnostics::missing_capturing_group_name(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
));
|
||||
// If GroupSpecifier is not found, fallback to `parse_ignore_group()` branch
|
||||
self.reader.rewind(checkpoint);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
group_name = Some(name);
|
||||
}
|
||||
|
||||
|
|
@ -1525,32 +1533,134 @@ impl<'a> Parser<'a> {
|
|||
|
||||
// ```
|
||||
// (?: Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[~UnicodeMode, ~UnicodeSetsMode, ?NamedCaptureGroups] )
|
||||
// ```
|
||||
fn parse_ignore_group(&mut self) -> Result<Option<ast::IgnoreGroup<'a>>> {
|
||||
let span_start = self.reader.offset();
|
||||
|
||||
if self.reader.eat3('(', '?', ':') {
|
||||
let disjunction = self.parse_disjunction()?;
|
||||
if self.reader.eat2('(', '?') {
|
||||
let modifiers = if self.reader.peek().filter(|&cp| cp == ':' as u32).is_some() {
|
||||
None
|
||||
} else {
|
||||
self.parse_modifiers()?
|
||||
};
|
||||
|
||||
if !self.reader.eat(')') {
|
||||
return Err(diagnostics::unterminated_pattern(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
"ignore group",
|
||||
));
|
||||
if self.reader.eat(':') {
|
||||
let disjunction = self.parse_disjunction()?;
|
||||
|
||||
if !self.reader.eat(')') {
|
||||
return Err(diagnostics::unterminated_pattern(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
"ignore group",
|
||||
));
|
||||
}
|
||||
|
||||
return Ok(Some(ast::IgnoreGroup {
|
||||
span: self.span_factory.create(span_start, self.reader.offset()),
|
||||
modifiers,
|
||||
body: disjunction,
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(Some(ast::IgnoreGroup {
|
||||
span: self.span_factory.create(span_start, self.reader.offset()),
|
||||
// TODO: Stage3 ModifierFlags
|
||||
enabling_modifiers: None,
|
||||
disabling_modifiers: None,
|
||||
body: disjunction,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// ```
|
||||
// RegularExpressionModifiers ::
|
||||
// [empty]
|
||||
// RegularExpressionModifiers RegularExpressionModifier
|
||||
//
|
||||
// RegularExpressionModifier :: one of
|
||||
// `i` `m` `s`
|
||||
// ```
|
||||
fn parse_modifiers(&mut self) -> Result<Option<ast::Modifiers>> {
|
||||
let span_start = self.reader.offset();
|
||||
|
||||
// Currently only `[i, m, s]` are supported
|
||||
let mut enabling_flags = [0, 0, 0];
|
||||
let mut disabling_flags = [0, 0, 0];
|
||||
|
||||
// Enabling
|
||||
while self.reader.peek().filter(|&cp| cp == ':' as u32 || cp == '-' as u32).is_none() {
|
||||
if self.reader.eat('i') {
|
||||
enabling_flags[0] += 1;
|
||||
continue;
|
||||
}
|
||||
if self.reader.eat('m') {
|
||||
enabling_flags[1] += 1;
|
||||
continue;
|
||||
}
|
||||
if self.reader.eat('s') {
|
||||
enabling_flags[2] += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(diagnostics::unknown_modifiers(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
));
|
||||
}
|
||||
|
||||
// Disabling
|
||||
if self.reader.eat('-') {
|
||||
while self.reader.peek().filter(|&cp| cp == ':' as u32).is_none() {
|
||||
if self.reader.eat('i') {
|
||||
disabling_flags[0] += 1;
|
||||
continue;
|
||||
}
|
||||
if self.reader.eat('m') {
|
||||
disabling_flags[1] += 1;
|
||||
continue;
|
||||
}
|
||||
if self.reader.eat('s') {
|
||||
disabling_flags[2] += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(diagnostics::unknown_modifiers(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let (enabling_iter, disabling_iter) = (enabling_flags.iter(), disabling_flags.iter());
|
||||
|
||||
// [SS:EE] Atom :: (? RegularExpressionModifiers : Disjunction )
|
||||
// It is a Syntax Error if the source text matched by RegularExpressionModifiers contains the same code point more than once.
|
||||
// [SS:EE] Atom :: (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction )
|
||||
// It is a Syntax Error if the source text matched by the first RegularExpressionModifiers contains the same code point more than once.
|
||||
// It is a Syntax Error if the source text matched by the second RegularExpressionModifiers contains the same code point more than once.
|
||||
// It is a Syntax Error if any code point in the source text matched by the first RegularExpressionModifiers is also contained in the source text matched by the second RegularExpressionModifiers.
|
||||
let flags_iter = enabling_iter.clone().zip(disabling_iter.clone());
|
||||
if flags_iter.clone().any(|flags| !matches!(flags, (0 | 1, 0) | (0, 1))) {
|
||||
return Err(diagnostics::invalid_modifiers(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
));
|
||||
}
|
||||
// NOTE: Spec is not yet fixed and merged, so these may change:
|
||||
// https://github.com/tc39/ecma262/pull/3221#pullrequestreview-2341169958
|
||||
if flags_iter.clone().all(|flags| matches!(flags, (0, 0))) {
|
||||
return Err(diagnostics::invalid_modifiers(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Some(ast::Modifiers {
|
||||
span: self.span_factory.create(span_start, self.reader.offset()),
|
||||
enabling: enabling_iter.clone().any(|f| *f == 1).then(|| ast::Modifier {
|
||||
ignore_case: enabling_flags[0] == 1,
|
||||
multiline: enabling_flags[1] == 1,
|
||||
sticky: enabling_flags[2] == 1,
|
||||
}),
|
||||
disabling: disabling_iter.clone().any(|f| *f == 1).then(|| ast::Modifier {
|
||||
ignore_case: disabling_flags[0] == 1,
|
||||
multiline: disabling_flags[1] == 1,
|
||||
sticky: disabling_flags[2] == 1,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// ```
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
commit: d62fa93c
|
||||
|
||||
codegen_test262 Summary:
|
||||
AST Parsed : 43776/43776 (100.00%)
|
||||
Positive Passed: 43776/43776 (100.00%)
|
||||
AST Parsed : 43914/43914 (100.00%)
|
||||
Positive Passed: 43914/43914 (100.00%)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
commit: d62fa93c
|
||||
|
||||
minifier_test262 Summary:
|
||||
AST Parsed : 43776/43776 (100.00%)
|
||||
Positive Passed: 43776/43776 (100.00%)
|
||||
AST Parsed : 43914/43914 (100.00%)
|
||||
Positive Passed: 43914/43914 (100.00%)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
commit: d62fa93c
|
||||
|
||||
parser_test262 Summary:
|
||||
AST Parsed : 43776/43776 (100.00%)
|
||||
Positive Passed: 43775/43776 (100.00%)
|
||||
Negative Passed: 4237/4239 (99.95%)
|
||||
AST Parsed : 43914/43914 (100.00%)
|
||||
Positive Passed: 43913/43914 (100.00%)
|
||||
Negative Passed: 4320/4322 (99.95%)
|
||||
Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attributes/json-invalid.js
|
||||
Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attributes/json-named-bindings.js
|
||||
Expect to Parse: tasks/coverage/test262/test/built-ins/String/prototype/split/separator-regexp.js
|
||||
|
|
@ -20611,6 +20611,412 @@ Expect to Parse: tasks/coverage/test262/test/built-ins/String/prototype/split/se
|
|||
30 │ /
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-add-remove-i.js:20:4]
|
||||
19 │
|
||||
20 │ /(?i-i:a)//*{ global-modifiers }*/;
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-add-remove-m.js:20:4]
|
||||
19 │
|
||||
20 │ /(?m-m:a)//*{ global-modifiers }*/;
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-add-remove-multi-duplicate.js:20:4]
|
||||
19 │
|
||||
20 │ /(?ims-m:a)//*{ global-modifiers }*/;
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-add-remove-s-escape.js:20:4]
|
||||
19 │
|
||||
20 │ /(?\u{0073}-s:a)//*{ global-modifiers }*/;
|
||||
· ▲
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-add-remove-s.js:20:4]
|
||||
19 │
|
||||
20 │ /(?s-s:a)//*{ global-modifiers }*/;
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-both-empty.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-:a)//*{ global-modifiers }*/;
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-code-point-repeat-i-1.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-ii:a)//*{ global-modifiers }*/;
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Invalid modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-code-point-repeat-i-2.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-imsi:a)//*{ global-modifiers }*/;
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-no-colon-1.js:19:4]
|
||||
18 │
|
||||
19 │ /(?ms-i)/;
|
||||
· ────
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-no-colon-2.js:19:4]
|
||||
18 │
|
||||
19 │ /(?-s)/;
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-no-colon-3.js:19:4]
|
||||
18 │
|
||||
19 │ /(?i-)/;
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-arbitrary.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-1:a)//*{ global-modifiers }*/;
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-combining-i.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-iͥ:a)//*{ global-modifiers }*/;
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-combining-m.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-mͫ:a)//*{ global-modifiers }*/;
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-combining-s.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-s̀:a)//*{ global-modifiers }*/;
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-d.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-d:a)//*{ global-modifiers }*/;
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-g.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-g:a)//*{ global-modifiers }*/;
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Unknown modifiers
|
||||
╭─[test262/test/language/literals/regexp/early-err-arithmetic-modifiers-other-code-point-non-display-1.js:20:4]
|
||||
19 │
|
||||
20 │ /(?-s | ||||