Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,9 @@ private RegexNode ReduceLoops()
// If the Loop or Lazyloop now only has one child node and its a Set, One, or Notone,
// reduce to just Setloop/lazy, Oneloop/lazy, or Notoneloop/lazy. The parser will
// generally have only produced the latter, but other reductions could have exposed
// this.
// this. We can also reduce or eliminate certain loops that are nops, e.g.
// a loop with a minimum of 0 that wraps a zero-width assertion is either asserting something
// or not, and is thus useless.
if (u.ChildCount() == 1)
{
RegexNode child = u.Child(0);
Expand All @@ -906,6 +908,17 @@ private RegexNode ReduceLoops()
child.MakeRep(u.Kind == RegexNodeKind.Lazyloop ? RegexNodeKind.Onelazy : RegexNodeKind.Oneloop, u.M, u.N);
u = child;
break;

case RegexNodeKind.Empty:
case RegexNodeKind.PositiveLookaround or RegexNodeKind.NegativeLookaround or
RegexNodeKind.Beginning or RegexNodeKind.Start or
RegexNodeKind.Bol or RegexNodeKind.Eol or
RegexNodeKind.End or RegexNodeKind.EndZ or
RegexNodeKind.Boundary or RegexNodeKind.ECMABoundary or
RegexNodeKind.NonBoundary or RegexNodeKind.NonECMABoundary
when u.M == 0:
u = new RegexNode(RegexNodeKind.Empty, Options);
break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ public class RegexFindOptimizationsTests
[InlineData(@"(?=\b)^abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)]
[InlineData(@"(?=\b)(?=^.*$)abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)]
[InlineData(@"(?=\b)(?=\B)^abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)]
// The next two could be improved slightly to be LeadingString_LeftToRight.
[InlineData(@"(?=^.*def)?abc", 0, (int)FindNextStartingPositionMode.FixedDistanceChar_LeftToRight)]
[InlineData(@"(?=^)?(?=^)abc", 0, (int)FindNextStartingPositionMode.FixedDistanceChar_LeftToRight)]
[InlineData(@"(?=^.*def)abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)]
[InlineData(@"(?=^)(?=^)abc", 0, (int)FindNextStartingPositionMode.LeadingAnchor_LeftToRight_Beginning)]

[InlineData(@"^", (int)RegexOptions.RightToLeft, (int)FindNextStartingPositionMode.LeadingAnchor_RightToLeft_Beginning)]
[InlineData(@"hello^", (int)RegexOptions.RightToLeft, (int)FindNextStartingPositionMode.LeadingAnchor_RightToLeft_Beginning)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,19 @@ public class RegexReductionTests
// Large loop patterns
[InlineData("a*a*a*a*a*a*a*b*b*?a+a*", "a*b*b*?a+")]
[InlineData("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "a{0,30}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")]
// Nop loops
[InlineData("(?:)*", "")]
[InlineData("a(?=abc)*b", "ab")]
[InlineData("a(?<=abc)*b", "ab")]
[InlineData("a(?<!abc)*b", "ab")]
[InlineData("a$*b", "ab")]
[InlineData("a^?b", "ab")]
[InlineData(@"a\b*b", "ab")]
[InlineData(@"a\B*b", "ab")]
[InlineData(@"a\z?b", "ab")]
[InlineData(@"a\Z?b", "ab")]
[InlineData(@"a\A?b", "ab")]
[InlineData(@"a\G?b", "ab")]
// Group elimination
[InlineData("(?:(?:(?:(?:(?:(?:a*))))))", "a*")]
// Nested loops
Expand Down Expand Up @@ -542,6 +555,11 @@ public void PatternsReduceIdentically(string actual, string expected)
[InlineData("a*(?(xyz)bcd)", "(?>a*)(?(xyz)bcd)")]
// Different prefixes on alternation branches
[InlineData("^abcd|$abce", "^abcd|^abce")]
// Zero-width assertions in non-removable loops
[InlineData("a(?=abc)+b", "ab")]
[InlineData("a(?<=abc)+b", "ab")]
[InlineData("a(?<!abc){1,2}b", "ab")]
[InlineData("a${3,}b", "ab")]
// Anchors
[InlineData(@"\b\B", "\b")]
[InlineData(@"^$", "^")]
Expand Down
Loading