|
| 1 | +require_relative '../../spec_helper' |
| 2 | +require_relative '../fixtures/classes' |
| 3 | + |
| 4 | +describe "empty checks in Regexps" do |
| 5 | + |
| 6 | + it "allow extra empty iterations" do |
| 7 | + /()?/.match("").to_a.should == ["", ""] |
| 8 | + /(a*)?/.match("").to_a.should == ["", ""] |
| 9 | + /(a*)*/.match("").to_a.should == ["", ""] |
| 10 | + # The bounds are high to avoid DFA-based matchers in implementations |
| 11 | + # and to check backtracking behavior. |
| 12 | + /(?:a|()){500,1000}/.match("a" * 500).to_a.should == ["a" * 500, ""] |
| 13 | + |
| 14 | + # Variations with non-greedy loops. |
| 15 | + /()??/.match("").to_a.should == ["", nil] |
| 16 | + /(a*?)?/.match("").to_a.should == ["", ""] |
| 17 | + /(a*)??/.match("").to_a.should == ["", nil] |
| 18 | + /(a*?)??/.match("").to_a.should == ["", nil] |
| 19 | + /(a*?)*/.match("").to_a.should == ["", ""] |
| 20 | + /(a*)*?/.match("").to_a.should == ["", nil] |
| 21 | + /(a*?)*?/.match("").to_a.should == ["", nil] |
| 22 | + end |
| 23 | + |
| 24 | + it "allow empty iterations in the middle of a loop" do |
| 25 | + # One empty iteration between a's and b's. |
| 26 | + /(a|\2b|())*/.match("aaabbb").to_a.should == ["aaabbb", "", ""] |
| 27 | + /(a|\2b|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", ""] |
| 28 | + |
| 29 | + # Two empty iterations between a's and b's. |
| 30 | + /(a|\2b|\3()|())*/.match("aaabbb").to_a.should == ["aaabbb", "", "", ""] |
| 31 | + /(a|\2b|\3()|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", nil, ""] |
| 32 | + |
| 33 | + # Check that the empty iteration correctly updates the loop counter. |
| 34 | + /(a|\2b|()){20,24}/.match("a" * 20 + "b" * 5).to_a.should == ["a" * 20 + "b" * 3, "b", ""] |
| 35 | + |
| 36 | + # Variations with non-greedy loops. |
| 37 | + /(a|\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil] |
| 38 | + /(a|\2b|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", ""] |
| 39 | + /(a|\2b|\3()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil] |
| 40 | + /(a|\2b|\3()|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", nil, ""] |
| 41 | + /(a|\2b|()){20,24}/.match("a" * 20 + "b" * 5).to_a.should == ["a" * 20 + "b" * 3, "b", ""] |
| 42 | + end |
| 43 | + |
| 44 | + it "make the Regexp proceed past the quantified expression on failure" do |
| 45 | + # If the contents of the ()* quantified group are empty (i.e., they fail |
| 46 | + # the empty check), the loop will abort. It will not try to backtrack |
| 47 | + # and try other alternatives (e.g. matching the "a") like in other Regexp |
| 48 | + # dialects such as ECMAScript. |
| 49 | + /(?:|a)*/.match("aaa").to_a.should == [""] |
| 50 | + /(?:()|a)*/.match("aaa").to_a.should == ["", ""] |
| 51 | + /(|a)*/.match("aaa").to_a.should == ["", ""] |
| 52 | + /(()|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 53 | + |
| 54 | + # Same expressions, but with backreferences, to force the use of non-DFA-based |
| 55 | + # engines. |
| 56 | + /()\1(?:|a)*/.match("aaa").to_a.should == ["", ""] |
| 57 | + /()\1(?:()|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 58 | + /()\1(|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 59 | + /()\1(()|a)*/.match("aaa").to_a.should == ["", "", "", ""] |
| 60 | + |
| 61 | + # Variations with other zero-width contents of the quantified |
| 62 | + # group: backreferences, capture groups, lookarounds |
| 63 | + /()(?:\1|a)*/.match("aaa").to_a.should == ["", ""] |
| 64 | + /()(?:()\1|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 65 | + /()(?:(\1)|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 66 | + /()(?:\1()|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 67 | + /()(\1|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 68 | + /()(()\1|a)*/.match("aaa").to_a.should == ["", "", "", ""] |
| 69 | + /()((\1)|a)*/.match("aaa").to_a.should == ["", "", "", ""] |
| 70 | + /()(\1()|a)*/.match("aaa").to_a.should == ["", "", "", ""] |
| 71 | + |
| 72 | + /(?:(?=a)|a)*/.match("aaa").to_a.should == [""] |
| 73 | + /(?:(?=a)()|a)*/.match("aaa").to_a.should == ["", ""] |
| 74 | + /(?:()(?=a)|a)*/.match("aaa").to_a.should == ["", ""] |
| 75 | + /(?:((?=a))|a)*/.match("aaa").to_a.should == ["", ""] |
| 76 | + /()\1(?:(?=a)|a)*/.match("aaa").to_a.should == ["", ""] |
| 77 | + /()\1(?:(?=a)()|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 78 | + /()\1(?:()(?=a)|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 79 | + /()\1(?:((?=a))|a)*/.match("aaa").to_a.should == ["", "", ""] |
| 80 | + |
| 81 | + # Variations with non-greedy loops. |
| 82 | + /(?:|a)*?/.match("aaa").to_a.should == [""] |
| 83 | + /(?:()|a)*?/.match("aaa").to_a.should == ["", nil] |
| 84 | + /(|a)*?/.match("aaa").to_a.should == ["", nil] |
| 85 | + /(()|a)*?/.match("aaa").to_a.should == ["", nil, nil] |
| 86 | + |
| 87 | + /()\1(?:|a)*?/.match("aaa").to_a.should == ["", ""] |
| 88 | + /()\1(?:()|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 89 | + /()\1(|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 90 | + /()\1(()|a)*?/.match("aaa").to_a.should == ["", "", nil, nil] |
| 91 | + |
| 92 | + /()(?:\1|a)*?/.match("aaa").to_a.should == ["", ""] |
| 93 | + /()(?:()\1|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 94 | + /()(?:(\1)|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 95 | + /()(?:\1()|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 96 | + /()(\1|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 97 | + /()(()\1|a)*?/.match("aaa").to_a.should == ["", "", nil, nil] |
| 98 | + /()((\1)|a)*?/.match("aaa").to_a.should == ["", "", nil, nil] |
| 99 | + /()(\1()|a)*?/.match("aaa").to_a.should == ["", "", nil, nil] |
| 100 | + |
| 101 | + /(?:(?=a)|a)*?/.match("aaa").to_a.should == [""] |
| 102 | + /(?:(?=a)()|a)*?/.match("aaa").to_a.should == ["", nil] |
| 103 | + /(?:()(?=a)|a)*?/.match("aaa").to_a.should == ["", nil] |
| 104 | + /(?:((?=a))|a)*?/.match("aaa").to_a.should == ["", nil] |
| 105 | + /()\1(?:(?=a)|a)*?/.match("aaa").to_a.should == ["", ""] |
| 106 | + /()\1(?:(?=a)()|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 107 | + /()\1(?:()(?=a)|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 108 | + /()\1(?:((?=a))|a)*?/.match("aaa").to_a.should == ["", "", nil] |
| 109 | + end |
| 110 | + |
| 111 | + it "shouldn't cause the Regexp parser to get stuck in a loop" do |
| 112 | + /(|a|\2b|())*/.match("aaabbb").to_a.should == ["", "", nil] |
| 113 | + /(a||\2b|())*/.match("aaabbb").to_a.should == ["aaa", "", nil] |
| 114 | + /(a|\2b||())*/.match("aaabbb").to_a.should == ["aaa", "", nil] |
| 115 | + /(a|\2b|()|)*/.match("aaabbb").to_a.should == ["aaabbb", "", ""] |
| 116 | + /(()|a|\3b|())*/.match("aaabbb").to_a.should == ["", "", "", nil] |
| 117 | + /(a|()|\3b|())*/.match("aaabbb").to_a.should == ["aaa", "", "", nil] |
| 118 | + /(a|\2b|()|())*/.match("aaabbb").to_a.should == ["aaabbb", "", "", nil] |
| 119 | + /(a|\3b|()|())*/.match("aaabbb").to_a.should == ["aaa", "", "", nil] |
| 120 | + /(a|()|())*/.match("aaa").to_a.should == ["aaa", "", "", nil] |
| 121 | + /^(()|a|())*$/.match("aaa").to_a.should == ["aaa", "", "", nil] |
| 122 | + |
| 123 | + # Variations with non-greedy loops. |
| 124 | + /(|a|\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil] |
| 125 | + /(a||\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil] |
| 126 | + /(a|\2b||())*?/.match("aaabbb").to_a.should == ["", nil, nil] |
| 127 | + /(a|\2b|()|)*?/.match("aaabbb").to_a.should == ["", nil, nil] |
| 128 | + /(()|a|\3b|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil] |
| 129 | + /(a|()|\3b|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil] |
| 130 | + /(a|\2b|()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil] |
| 131 | + /(a|\3b|()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil] |
| 132 | + /(a|()|())*?/.match("aaa").to_a.should == ["", nil, nil, nil] |
| 133 | + /^(()|a|())*?$/.match("aaa").to_a.should == ["aaa", "a", "", nil] |
| 134 | + end |
| 135 | +end |
0 commit comments