Skip to content
Merged
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
53 changes: 51 additions & 2 deletions core/regexp/linear_time_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,56 @@
}.should complain(/warning: flags ignored/)
end

it "returns true for positive lookarounds" do
Regexp.linear_time?(/(?:(?=a*)a)*/).should == true
it "returns true for positive lookahead" do
Regexp.linear_time?(/a*(?:(?=a*)a)*b/).should == true
end

it "returns true for positive lookbehind" do
Regexp.linear_time?(/a*(?:(?<=a)a*)*b/).should == true
end

it "returns true for negative lookbehind" do
Regexp.linear_time?(/a*(?:(?<!a)a*)*b/).should == true
end

# There are two known ways to make Regexp linear:
# * Using a DFA (deterministic finite-state automaton) Regexp engine, which always matches in linear time (e.g. TruffleRuby with TRegex)
# * Caching position and state to avoid catastrophic backtracking (e.g. CRuby: https://bugs.ruby-lang.org/issues/19104)
#
# Both approach should be allowed and given that DFA Regexp engines
# are much faster there should be no specs preventing using them.
uses_regexp_caching = RUBY_ENGINE == 'ruby'
uses_dfa_regexp_engine = !uses_regexp_caching

# The following specs should not be relied upon,
# they are here only to illustrate differences between Regexp engines.
guard -> { uses_regexp_caching } do
it "returns true for negative lookahead" do
Regexp.linear_time?(/a*(?:(?!a*)a*)*b/).should == true
end

it "returns true for atomic groups" do
Regexp.linear_time?(/a*(?:(?>a)a*)*b/).should == true
end

it "returns true for possessive quantifiers" do
Regexp.linear_time?(/a*(?:(?:a)?+a*)*b/).should == true
end

it "returns true for positive lookbehind with capture group" do
Regexp.linear_time?(/.(?<=(a))/).should == true
end
end

# The following specs should not be relied upon,
# they are here only to illustrate differences between Regexp engines.
guard -> { uses_dfa_regexp_engine } do
it "returns true for non-recursive subexpression call" do
Regexp.linear_time?(/(?<a>a){0}\g<a>/).should == true
end

it "returns true for positive lookahead with capture group" do
Regexp.linear_time?(/x+(?=(a))/).should == true
end
end
end