regexp_parser 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/lib/regexp_parser/expression.rb +6 -43
  4. data/lib/regexp_parser/expression/classes/conditional.rb +3 -2
  5. data/lib/regexp_parser/expression/classes/escape.rb +0 -4
  6. data/lib/regexp_parser/expression/methods/match.rb +13 -0
  7. data/lib/regexp_parser/expression/methods/options.rb +35 -0
  8. data/lib/regexp_parser/expression/methods/strfregexp.rb +0 -1
  9. data/lib/regexp_parser/expression/methods/tests.rb +6 -15
  10. data/lib/regexp_parser/expression/sequence.rb +3 -2
  11. data/lib/regexp_parser/expression/sequence_operation.rb +2 -6
  12. data/lib/regexp_parser/lexer.rb +0 -21
  13. data/lib/regexp_parser/parser.rb +22 -21
  14. data/lib/regexp_parser/scanner.rb +1159 -1329
  15. data/lib/regexp_parser/scanner/char_type.rl +0 -3
  16. data/lib/regexp_parser/scanner/scanner.rl +82 -190
  17. data/lib/regexp_parser/version.rb +1 -1
  18. data/spec/expression/base_spec.rb +14 -0
  19. data/spec/expression/methods/match_length_spec.rb +13 -0
  20. data/spec/expression/methods/match_spec.rb +25 -0
  21. data/spec/expression/methods/tests_spec.rb +2 -0
  22. data/spec/expression/options_spec.rb +128 -0
  23. data/spec/expression/root_spec.rb +9 -0
  24. data/spec/expression/sequence_spec.rb +9 -0
  25. data/spec/lexer/conditionals_spec.rb +49 -119
  26. data/spec/lexer/escapes_spec.rb +8 -32
  27. data/spec/lexer/keep_spec.rb +5 -17
  28. data/spec/lexer/literals_spec.rb +73 -110
  29. data/spec/lexer/nesting_spec.rb +86 -117
  30. data/spec/lexer/refcalls_spec.rb +51 -50
  31. data/spec/parser/all_spec.rb +13 -1
  32. data/spec/parser/anchors_spec.rb +9 -23
  33. data/spec/parser/conditionals_spec.rb +9 -9
  34. data/spec/parser/errors_spec.rb +22 -43
  35. data/spec/parser/escapes_spec.rb +33 -44
  36. data/spec/parser/groups_spec.rb +98 -257
  37. data/spec/parser/keep_spec.rb +2 -15
  38. data/spec/parser/posix_classes_spec.rb +5 -24
  39. data/spec/parser/properties_spec.rb +42 -54
  40. data/spec/parser/quantifiers_spec.rb +41 -283
  41. data/spec/parser/refcalls_spec.rb +60 -185
  42. data/spec/parser/set/intersections_spec.rb +17 -17
  43. data/spec/parser/set/ranges_spec.rb +17 -17
  44. data/spec/parser/sets_spec.rb +5 -5
  45. data/spec/parser/types_spec.rb +11 -36
  46. data/spec/scanner/anchors_spec.rb +13 -28
  47. data/spec/scanner/conditionals_spec.rb +121 -173
  48. data/spec/scanner/errors_spec.rb +65 -87
  49. data/spec/scanner/escapes_spec.rb +49 -50
  50. data/spec/scanner/free_space_spec.rb +102 -165
  51. data/spec/scanner/groups_spec.rb +45 -64
  52. data/spec/scanner/keep_spec.rb +5 -28
  53. data/spec/scanner/literals_spec.rb +45 -81
  54. data/spec/scanner/meta_spec.rb +13 -33
  55. data/spec/scanner/properties_spec.rb +43 -286
  56. data/spec/scanner/quantifiers_spec.rb +13 -28
  57. data/spec/scanner/refcalls_spec.rb +32 -48
  58. data/spec/scanner/sets_spec.rb +88 -102
  59. data/spec/scanner/types_spec.rb +10 -25
  60. data/spec/spec_helper.rb +1 -0
  61. data/spec/support/shared_examples.rb +77 -0
  62. data/spec/syntax/syntax_spec.rb +4 -0
  63. data/spec/syntax/versions/1.8.6_spec.rb +12 -33
  64. data/spec/syntax/versions/1.9.1_spec.rb +5 -18
  65. data/spec/syntax/versions/1.9.3_spec.rb +4 -17
  66. data/spec/syntax/versions/2.0.0_spec.rb +8 -23
  67. data/spec/syntax/versions/2.2.0_spec.rb +4 -17
  68. data/spec/syntax/versions/aliases_spec.rb +25 -109
  69. metadata +14 -6
  70. data/spec/scanner/scripts_spec.rb +0 -49
  71. data/spec/scanner/unicode_blocks_spec.rb +0 -28
@@ -2,7 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe(Regexp::Parser) do
4
4
  specify('parse returns a root expression') do
5
- expect(RP.parse('abc')).to be_instance_of(Regexp::Expression::Root)
5
+ expect(RP.parse('abc')).to be_instance_of(Root)
6
+ end
7
+
8
+ specify('parse can be called with block') do
9
+ expect(RP.parse('abc') { |root| root.class }).to eq Root
6
10
  end
7
11
 
8
12
  specify('parse root contains expressions') do
@@ -10,6 +14,14 @@ RSpec.describe(Regexp::Parser) do
10
14
  expect(root.expressions).to all(be_a Regexp::Expression::Base)
11
15
  end
12
16
 
17
+ specify('parse root options mi') do
18
+ root = RP.parse(/[abc]/mi, 'ruby/1.8')
19
+
20
+ expect(root.m?).to be true
21
+ expect(root.i?).to be true
22
+ expect(root.x?).to be false
23
+ end
24
+
13
25
  specify('parse node types') do
14
26
  root = RP.parse('^(one){2,3}([^d\\]efm-qz\\,\\-]*)(ghi)+$')
15
27
 
@@ -1,31 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe('Anchor parsing') do
4
- tests = {
5
- '^a' => [0, :anchor, :bol, Anchor::BOL],
6
- 'a$' => [1, :anchor, :eol, Anchor::EOL],
4
+ include_examples 'parse', /^a/, 0 => [:anchor, :bol, Anchor::BOL]
5
+ include_examples 'parse', /a$/, 1 => [:anchor, :eol, Anchor::EOL]
7
6
 
8
- '\Aa' => [0, :anchor, :bos, Anchor::BOS],
9
- 'a\z' => [1, :anchor, :eos, Anchor::EOS],
10
- 'a\Z' => [1, :anchor, :eos_ob_eol, Anchor::EOSobEOL],
7
+ include_examples 'parse', /\Aa/, 0 => [:anchor, :bos, Anchor::BOS]
8
+ include_examples 'parse', /a\z/, 1 => [:anchor, :eos, Anchor::EOS]
9
+ include_examples 'parse', /a\Z/, 1 => [:anchor, :eos_ob_eol, Anchor::EOSobEOL]
11
10
 
12
- 'a\b' => [1, :anchor, :word_boundary, Anchor::WordBoundary],
13
- 'a\B' => [1, :anchor, :nonword_boundary, Anchor::NonWordBoundary],
11
+ include_examples 'parse', /a\b/, 1 => [:anchor, :word_boundary, Anchor::WordBoundary]
12
+ include_examples 'parse', /a\B/, 1 => [:anchor, :nonword_boundary, Anchor::NonWordBoundary]
14
13
 
15
- 'a\G' => [1, :anchor, :match_start, Anchor::MatchStart],
14
+ include_examples 'parse', /a\G/, 1 => [:anchor, :match_start, Anchor::MatchStart]
16
15
 
17
- "\\\\Aa" => [0, :escape, :backslash, EscapeSequence::Literal],
18
- }
19
-
20
- tests.each_with_index do |(pattern, (index, type, token, klass)), count|
21
- specify("parse_anchor_#{token}_#{count}") do
22
- root = RP.parse(pattern, 'ruby/1.9')
23
- exp = root.expressions.at(index)
24
-
25
- expect(exp).to be_a(klass)
26
-
27
- expect(exp.type).to eq type
28
- expect(exp.token).to eq token
29
- end
30
- end
16
+ include_examples 'parse', /\\A/, 0 => [:escape, :backslash, EscapeSequence::Literal]
31
17
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe('Conditional parsing') do
4
4
  specify('parse conditional') do
5
- regexp = Regexp.new('(?<A>a)(?(<A>)T|F)/')
5
+ regexp = /(?<A>a)(?(<A>)T|F)/
6
6
 
7
7
  root = RP.parse(regexp, 'ruby/2.0')
8
8
  exp = root[1]
@@ -16,7 +16,7 @@ RSpec.describe('Conditional parsing') do
16
16
  end
17
17
 
18
18
  specify('parse conditional condition') do
19
- regexp = Regexp.new('(?<A>a)(?(<A>)T|F)/')
19
+ regexp = /(?<A>a)(?(<A>)T|F)/
20
20
 
21
21
  root = RP.parse(regexp, 'ruby/2.0')
22
22
  exp = root[1].condition
@@ -31,7 +31,7 @@ RSpec.describe('Conditional parsing') do
31
31
  end
32
32
 
33
33
  specify('parse conditional condition with number ref') do
34
- regexp = Regexp.new('(a)(?(1)T|F)/')
34
+ regexp = /(a)(?(1)T|F)/
35
35
 
36
36
  root = RP.parse(regexp, 'ruby/2.0')
37
37
  exp = root[1].condition
@@ -46,7 +46,7 @@ RSpec.describe('Conditional parsing') do
46
46
  end
47
47
 
48
48
  specify('parse conditional nested groups') do
49
- regexp = Regexp.new('((a)|(b)|((?(2)(c(d|e)+)?|(?(3)f|(?(4)(g|(h)(i)))))))/')
49
+ regexp = /((a)|(b)|((?(2)(c(d|e)+)?|(?(3)f|(?(4)(g|(h)(i)))))))/
50
50
 
51
51
  root = RP.parse(regexp, 'ruby/2.0')
52
52
 
@@ -80,7 +80,7 @@ RSpec.describe('Conditional parsing') do
80
80
  end
81
81
 
82
82
  specify('parse conditional nested') do
83
- regexp = Regexp.new('(a(b(c(d)(e))))(?(1)(?(2)d|(?(3)e|f))|(?(4)(?(5)g|h)))/')
83
+ regexp = /(a(b(c(d)(e))))(?(1)(?(2)d|(?(3)e|f))|(?(4)(?(5)g|h)))/
84
84
 
85
85
  root = RP.parse(regexp, 'ruby/2.0')
86
86
 
@@ -102,7 +102,7 @@ RSpec.describe('Conditional parsing') do
102
102
  end
103
103
 
104
104
  specify('parse conditional nested alternation') do
105
- regexp = Regexp.new('(a)(?(1)(b|c|d)|(e|f|g))(h)(?(2)(i|j|k)|(l|m|n))|o|p/')
105
+ regexp = /(a)(?(1)(b|c|d)|(e|f|g))(h)(?(2)(i|j|k)|(l|m|n))|o|p/
106
106
 
107
107
  root = RP.parse(regexp, 'ruby/2.0')
108
108
 
@@ -125,7 +125,7 @@ RSpec.describe('Conditional parsing') do
125
125
  end
126
126
 
127
127
  specify('parse conditional extra separator') do
128
- regexp = Regexp.new('(?<A>a)(?(<A>)T|)/')
128
+ regexp = /(?<A>a)(?(<A>)T|)/
129
129
 
130
130
  root = RP.parse(regexp, 'ruby/2.0')
131
131
  branches = root[1].branches
@@ -146,7 +146,7 @@ RSpec.describe('Conditional parsing') do
146
146
  end
147
147
 
148
148
  specify('parse conditional quantified') do
149
- regexp = Regexp.new('(foo)(?(1)\d|(\w)){42}/')
149
+ regexp = /(foo)(?(1)\d|(\w)){42}/
150
150
 
151
151
  root = RP.parse(regexp, 'ruby/2.0')
152
152
  conditional = root[1]
@@ -158,7 +158,7 @@ RSpec.describe('Conditional parsing') do
158
158
  end
159
159
 
160
160
  specify('parse conditional branch content quantified') do
161
- regexp = Regexp.new('(foo)(?(1)\d{23}|(\w){42})/')
161
+ regexp = /(foo)(?(1)\d{23}|(\w){42})/
162
162
 
163
163
  root = RP.parse(regexp, 'ruby/2.0')
164
164
  conditional = root[1]
@@ -1,51 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe('Parsing errors') do
4
- let(:rp) { Regexp::Parser.new }
5
- before { rp.parse(/foo/) }
4
+ let(:parser) { Regexp::Parser.new }
5
+ before { parser.parse(/foo/) } # initializes ivars
6
6
 
7
- specify('parser unknown token type') do
8
- expect { rp.send(:parse_token, Regexp::Token.new(:foo, :bar)) }
7
+ it('raises UnknownTokenTypeError for unknown token types') do
8
+ expect { parser.send(:parse_token, Regexp::Token.new(:foo, :bar)) }
9
9
  .to raise_error(Regexp::Parser::UnknownTokenTypeError)
10
10
  end
11
11
 
12
- specify('parser unknown set token') do
13
- expect { rp.send(:parse_token, Regexp::Token.new(:set, :foo)) }
14
- .to raise_error(Regexp::Parser::UnknownTokenError)
15
- end
16
-
17
- specify('parser unknown meta token') do
18
- expect { rp.send(:parse_token, Regexp::Token.new(:meta, :foo)) }
19
- .to raise_error(Regexp::Parser::UnknownTokenError)
20
- end
21
-
22
- specify('parser unknown character type token') do
23
- expect { rp.send(:parse_token, Regexp::Token.new(:type, :foo)) }
24
- .to raise_error(Regexp::Parser::UnknownTokenError)
25
- end
26
-
27
- specify('parser unknown unicode property token') do
28
- expect { rp.send(:parse_token, Regexp::Token.new(:property, :foo)) }
29
- .to raise_error(Regexp::Parser::UnknownTokenError)
30
- end
31
-
32
- specify('parser unknown unicode nonproperty token') do
33
- expect { rp.send(:parse_token, Regexp::Token.new(:nonproperty, :foo)) }
34
- .to raise_error(Regexp::Parser::UnknownTokenError)
35
- end
36
-
37
- specify('parser unknown anchor token') do
38
- expect { rp.send(:parse_token, Regexp::Token.new(:anchor, :foo)) }
39
- .to raise_error(Regexp::Parser::UnknownTokenError)
40
- end
41
-
42
- specify('parser unknown quantifier token') do
43
- expect { rp.send(:parse_token, Regexp::Token.new(:quantifier, :foo)) }
44
- .to raise_error(Regexp::Parser::UnknownTokenError)
45
- end
46
-
47
- specify('parser unknown group open token') do
48
- expect { rp.send(:parse_token, Regexp::Token.new(:group, :foo)) }
49
- .to raise_error(Regexp::Parser::UnknownTokenError)
50
- end
12
+ RSpec.shared_examples 'UnknownTokenError' do |type, token|
13
+ it "raises for unkown tokens of type #{type}" do
14
+ expect { parser.send(:parse_token, Regexp::Token.new(type, :foo)) }
15
+ .to raise_error(Regexp::Parser::UnknownTokenError)
16
+ end
17
+ end
18
+
19
+ include_examples 'UnknownTokenError', :anchor
20
+ include_examples 'UnknownTokenError', :backref
21
+ include_examples 'UnknownTokenError', :conditional
22
+ include_examples 'UnknownTokenError', :free_space
23
+ include_examples 'UnknownTokenError', :group
24
+ include_examples 'UnknownTokenError', :meta
25
+ include_examples 'UnknownTokenError', :nonproperty
26
+ include_examples 'UnknownTokenError', :property
27
+ include_examples 'UnknownTokenError', :quantifier
28
+ include_examples 'UnknownTokenError', :set
29
+ include_examples 'UnknownTokenError', :type
51
30
  end
@@ -1,49 +1,35 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe('Escape parsing') do
4
- tests = {
5
- /a\ac/ => [1, :escape, :bell, EscapeSequence::Bell],
6
- /a\ec/ => [1, :escape, :escape, EscapeSequence::AsciiEscape],
7
- /a\fc/ => [1, :escape, :form_feed, EscapeSequence::FormFeed],
8
- /a\nc/ => [1, :escape, :newline, EscapeSequence::Newline],
9
- /a\rc/ => [1, :escape, :carriage, EscapeSequence::Return],
10
- /a\tc/ => [1, :escape, :tab, EscapeSequence::Tab],
11
- /a\vc/ => [1, :escape, :vertical_tab, EscapeSequence::VerticalTab],
12
-
13
- # meta character escapes
14
- /a\.c/ => [1, :escape, :dot, EscapeSequence::Literal],
15
- /a\?c/ => [1, :escape, :zero_or_one, EscapeSequence::Literal],
16
- /a\*c/ => [1, :escape, :zero_or_more, EscapeSequence::Literal],
17
- /a\+c/ => [1, :escape, :one_or_more, EscapeSequence::Literal],
18
- /a\|c/ => [1, :escape, :alternation, EscapeSequence::Literal],
19
- /a\(c/ => [1, :escape, :group_open, EscapeSequence::Literal],
20
- /a\)c/ => [1, :escape, :group_close, EscapeSequence::Literal],
21
- /a\{c/ => [1, :escape, :interval_open, EscapeSequence::Literal],
22
- /a\}c/ => [1, :escape, :interval_close, EscapeSequence::Literal],
23
-
24
- # unicode escapes
25
- /a\u0640/ => [1, :escape, :codepoint, EscapeSequence::Codepoint],
26
- /a\u{41 1F60D}/ => [1, :escape, :codepoint_list, EscapeSequence::CodepointList],
27
- /a\u{10FFFF}/ => [1, :escape, :codepoint_list, EscapeSequence::CodepointList],
28
-
29
- # hex escapes
30
- /a\xFF/n => [1, :escape, :hex, EscapeSequence::Hex],
31
-
32
- # octal escapes
33
- /a\177/n => [1, :escape, :octal, EscapeSequence::Octal],
34
- }
35
-
36
- tests.each_with_index do |(pattern, (index, type, token, klass)), count|
37
- specify("parse_escape_#{token}_#{count = (count + 1)}") do
38
- root = RP.parse(pattern, 'ruby/1.9')
39
- exp = root.expressions.at(index)
40
-
41
- expect(exp).to be_a(klass)
42
-
43
- expect(exp.type).to eq type
44
- expect(exp.token).to eq token
45
- end
46
- end
3
+ RSpec.describe('EscapeSequence parsing') do
4
+ include_examples 'parse', /a\ac/, 1 => [:escape, :bell, EscapeSequence::Bell]
5
+ include_examples 'parse', /a\ec/, 1 => [:escape, :escape, EscapeSequence::AsciiEscape]
6
+ include_examples 'parse', /a\fc/, 1 => [:escape, :form_feed, EscapeSequence::FormFeed]
7
+ include_examples 'parse', /a\nc/, 1 => [:escape, :newline, EscapeSequence::Newline]
8
+ include_examples 'parse', /a\rc/, 1 => [:escape, :carriage, EscapeSequence::Return]
9
+ include_examples 'parse', /a\tc/, 1 => [:escape, :tab, EscapeSequence::Tab]
10
+ include_examples 'parse', /a\vc/, 1 => [:escape, :vertical_tab, EscapeSequence::VerticalTab]
11
+
12
+ # meta character escapes
13
+ include_examples 'parse', /a\.c/, 1 => [:escape, :dot, EscapeSequence::Literal]
14
+ include_examples 'parse', /a\?c/, 1 => [:escape, :zero_or_one, EscapeSequence::Literal]
15
+ include_examples 'parse', /a\*c/, 1 => [:escape, :zero_or_more, EscapeSequence::Literal]
16
+ include_examples 'parse', /a\+c/, 1 => [:escape, :one_or_more, EscapeSequence::Literal]
17
+ include_examples 'parse', /a\|c/, 1 => [:escape, :alternation, EscapeSequence::Literal]
18
+ include_examples 'parse', /a\(c/, 1 => [:escape, :group_open, EscapeSequence::Literal]
19
+ include_examples 'parse', /a\)c/, 1 => [:escape, :group_close, EscapeSequence::Literal]
20
+ include_examples 'parse', /a\{c/, 1 => [:escape, :interval_open, EscapeSequence::Literal]
21
+ include_examples 'parse', /a\}c/, 1 => [:escape, :interval_close, EscapeSequence::Literal]
22
+
23
+ # unicode escapes
24
+ include_examples 'parse', /a\u0640/, 1 => [:escape, :codepoint, EscapeSequence::Codepoint]
25
+ include_examples 'parse', /a\u{41 1F60D}/, 1 => [:escape, :codepoint_list, EscapeSequence::CodepointList]
26
+ include_examples 'parse', /a\u{10FFFF}/, 1 => [:escape, :codepoint_list, EscapeSequence::CodepointList]
27
+
28
+ # hex escapes
29
+ include_examples 'parse', /a\xFF/n, 1 => [:escape, :hex, EscapeSequence::Hex]
30
+
31
+ # octal escapes
32
+ include_examples 'parse', /a\177/n, 1 => [:escape, :octal, EscapeSequence::Octal]
47
33
 
48
34
  specify('parse chars and codepoints') do
49
35
  root = RP.parse(/\n\?\101\x42\u0043\u{44 45}/)
@@ -65,6 +51,9 @@ RSpec.describe('Escape parsing') do
65
51
 
66
52
  expect(root[5].chars).to eq %w[D E]
67
53
  expect(root[5].codepoints).to eq [68, 69]
54
+
55
+ expect { root[5].char }.to raise_error(/#chars/)
56
+ expect { root[5].codepoint }.to raise_error(/#codepoints/)
68
57
  end
69
58
 
70
59
  specify('parse escape control sequence lower') do
@@ -1,267 +1,108 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe('Group parsing') do
4
- specify('parse root options mi') do
5
- t = RP.parse(/[abc]/mi, 'ruby/1.8')
6
-
7
- expect(t.m?).to be true
8
- expect(t.i?).to be true
9
- expect(t.x?).to be false
10
- end
11
-
12
- specify('parse option group') do
13
- t = RP.parse(/(?m:a)/, 'ruby/1.8')
14
-
15
- expect(t[0]).to be_instance_of(Group::Options)
16
- expect(t[0].token).to eq :options
17
-
18
- expect(t[0].m?).to be true
19
- expect(t[0].i?).to be false
20
- expect(t[0].x?).to be false
21
-
22
- expect(t[0].option_changes[:m]).to be true
23
- expect(t[0].option_changes[:i]).to be_nil
24
- end
25
-
26
- specify('parse self defeating option group') do
27
- t = RP.parse(/(?m-m:a)/, 'ruby/1.8')
28
-
29
- expect(t[0].m?).to be false
30
- expect(t[0].i?).to be false
31
- expect(t[0].x?).to be false
32
-
33
- expect(t[0].option_changes[:m]).to be false
34
- expect(t[0].option_changes[:i]).to be_nil
35
- end
36
-
37
- specify('parse nested options activate one') do
38
- t = RP.parse(/(?x-mi:a(?m:b))/, 'ruby/1.8')
39
-
40
- expect(t[0].m?).to be false
41
- expect(t[0].i?).to be false
42
- expect(t[0].x?).to be true
43
-
44
- expect(t[0][1].m?).to be true
45
- expect(t[0][1].i?).to be false
46
- expect(t[0][1].x?).to be true
47
-
48
- expect(t[0][1].option_changes[:m]).to be true
49
- expect(t[0][1].option_changes[:i]).to be_nil
50
- expect(t[0][1].option_changes[:x]).to be_nil
51
- end
52
-
53
- specify('parse nested options deactivate one') do
54
- t = RP.parse(/(?ix-m:a(?-i:b))/, 'ruby/1.8')
55
-
56
- expect(t[0].m?).to be false
57
- expect(t[0].i?).to be true
58
- expect(t[0].x?).to be true
59
-
60
- expect(t[0][1].m?).to be false
61
- expect(t[0][1].i?).to be false
62
- expect(t[0][1].x?).to be true
63
-
64
- expect(t[0][1].option_changes[:i]).to be false
65
- expect(t[0][1].option_changes[:m]).to be_nil
66
- expect(t[0][1].option_changes[:x]).to be_nil
67
- end
68
-
69
- specify('parse nested options invert all') do
70
- t = RP.parse('(?xi-m:a(?m-ix:b))', 'ruby/1.8')
71
-
72
- expect(t[0].m?).to be false
73
- expect(t[0].i?).to be true
74
- expect(t[0].x?).to be true
75
-
76
- expect(t[0][1].m?).to be true
77
- expect(t[0][1].i?).to be false
78
- expect(t[0][1].x?).to be false
79
-
80
- expect(t[0][1].option_changes[:m]).to be true
81
- expect(t[0][1].option_changes[:i]).to be false
82
- expect(t[0][1].option_changes[:x]).to be false
83
- end
84
-
85
- specify('parse nested options affect literal subexpressions') do
86
- t = RP.parse(/(?x-mi:a(?m:b))/, 'ruby/1.8')
87
-
88
- expect(t[0][0].m?).to be false
89
- expect(t[0][0].i?).to be false
90
- expect(t[0][0].x?).to be true
91
-
92
- expect(t[0][1][0].m?).to be true
93
- expect(t[0][1][0].i?).to be false
94
- expect(t[0][1][0].x?).to be true
95
- end
96
-
97
- specify('parse option switch group') do
98
- t = RP.parse(/a(?i-m)b/m, 'ruby/1.8')
99
-
100
- expect(t[1]).to be_instance_of(Group::Options)
101
- expect(t[1].token).to eq :options_switch
102
-
103
- expect(t[1].m?).to be false
104
- expect(t[1].i?).to be true
105
- expect(t[1].x?).to be false
106
-
107
- expect(t[1].option_changes[:i]).to be true
108
- expect(t[1].option_changes[:m]).to be false
109
- expect(t[1].option_changes[:x]).to be_nil
110
- end
111
-
112
- specify('parse option switch affects following expressions') do
113
- t = RP.parse(/a(?i-m)b/m, 'ruby/1.8')
114
-
115
- expect(t[0].m?).to be true
116
- expect(t[0].i?).to be false
117
- expect(t[0].x?).to be false
118
-
119
- expect(t[2].m?).to be false
120
- expect(t[2].i?).to be true
121
- expect(t[2].x?).to be false
122
- end
123
-
124
- specify('parse option switch in group') do
125
- t = RP.parse(/(a(?i-m)b)c/m, 'ruby/1.8')
126
-
127
- group1 = t[0]
128
-
129
- expect(group1.m?).to be true
130
- expect(group1.i?).to be false
131
- expect(group1.x?).to be false
132
-
133
- expect(group1[0].m?).to be true
134
- expect(group1[0].i?).to be false
135
- expect(group1[0].x?).to be false
136
-
137
- expect(group1[1].m?).to be false
138
- expect(group1[1].i?).to be true
139
- expect(group1[1].x?).to be false
140
-
141
- expect(group1[2].m?).to be false
142
- expect(group1[2].i?).to be true
143
- expect(group1[2].x?).to be false
144
-
145
- expect(t[1].m?).to be true
146
- expect(t[1].i?).to be false
147
- expect(t[1].x?).to be false
148
- end
149
-
150
- specify('parse nested option switch in group') do
151
- t = RP.parse(/((?i-m)(a(?-i)b))/m, 'ruby/1.8')
152
-
153
- group2 = t[0][1]
154
-
155
- expect(group2.m?).to be false
156
- expect(group2.i?).to be true
157
- expect(group2.x?).to be false
158
-
159
- expect(group2[0].m?).to be false
160
- expect(group2[0].i?).to be true
161
- expect(group2[0].x?).to be false
162
-
163
- expect(group2[1].m?).to be false
164
- expect(group2[1].i?).to be false
165
- expect(group2[1].x?).to be false
166
-
167
- expect(group2[2].m?).to be false
168
- expect(group2[2].i?).to be false
169
- expect(group2[2].x?).to be false
170
- end
171
-
172
- specify('parse options dau') do
173
- t = RP.parse('(?dua:abc)')
174
-
175
- expect(t[0].d?).to be false
176
- expect(t[0].a?).to be true
177
- expect(t[0].u?).to be false
178
- end
179
-
180
- specify('parse nested options dau') do
181
- t = RP.parse('(?u:a(?d:b))')
182
-
183
- expect(t[0].u?).to be true
184
- expect(t[0].d?).to be false
185
- expect(t[0].a?).to be false
186
-
187
- expect(t[0][1].d?).to be true
188
- expect(t[0][1].a?).to be false
189
- expect(t[0][1].u?).to be false
190
- end
191
-
192
- specify('parse nested options da') do
193
- t = RP.parse('(?di-xm:a(?da-x:b))')
194
-
195
- expect(t[0].d?).to be true
196
- expect(t[0].i?).to be true
197
- expect(t[0].m?).to be false
198
- expect(t[0].x?).to be false
199
- expect(t[0].a?).to be false
200
- expect(t[0].u?).to be false
201
-
202
- expect(t[0][1].d?).to be false
203
- expect(t[0][1].a?).to be true
204
- expect(t[0][1].u?).to be false
205
- expect(t[0][1].x?).to be false
206
- expect(t[0][1].m?).to be false
207
- expect(t[0][1].i?).to be true
208
- end
209
-
210
- specify('parse lookahead') do
211
- t = RP.parse('(?=abc)(?!def)', 'ruby/1.8')
212
-
213
- expect(t[0]).to be_a(Assertion::Lookahead)
214
-
215
- expect(t[1]).to be_a(Assertion::NegativeLookahead)
216
- end
217
-
218
- specify('parse lookbehind') do
219
- t = RP.parse('(?<=abc)(?<!def)', 'ruby/1.9')
220
-
221
- expect(t[0]).to be_a(Assertion::Lookbehind)
222
-
223
- expect(t[1]).to be_a(Assertion::NegativeLookbehind)
224
- end
225
-
226
- specify('parse comment') do
227
- t = RP.parse('a(?# is for apple)b(?# for boy)c(?# cat)')
228
-
229
- [1, 3, 5].each do |i|
230
- expect(t[i]).to be_a(Group::Comment)
231
-
232
- expect(t[i].type).to eq :group
233
- expect(t[i].token).to eq :comment
234
- end
235
- end
236
-
237
- specify('parse absence group', if: ruby_version_at_least('2.4.1')) do
238
- t = RP.parse('a(?~b)c(?~d)e')
239
-
240
- [1, 3].each do |i|
241
- expect(t[i]).to be_a(Group::Absence)
242
-
243
- expect(t[i].type).to eq :group
244
- expect(t[i].token).to eq :absence
245
- end
246
- end
4
+ include_examples 'parse', /(?=abc)(?!def)/,
5
+ 0 => [:assertion, :lookahead, Assertion::Lookahead],
6
+ 1 => [:assertion, :nlookahead, Assertion::NegativeLookahead]
7
+
8
+ include_examples 'parse', /(?<=abc)(?<!def)/,
9
+ 0 => [:assertion, :lookbehind, Assertion::Lookbehind],
10
+ 1 => [:assertion, :nlookbehind, Assertion::NegativeLookbehind]
11
+
12
+ include_examples 'parse', /a(?# is for apple)b(?# for boy)c(?# cat)/,
13
+ 1 => [:group, :comment, Group::Comment],
14
+ 3 => [:group, :comment, Group::Comment],
15
+ 5 => [:group, :comment, Group::Comment]
16
+
17
+ if ruby_version_at_least('2.4.1')
18
+ include_examples 'parse', 'a(?~b)c(?~d)e',
19
+ 1 => [:group, :absence, Group::Absence],
20
+ 3 => [:group, :absence, Group::Absence]
21
+ end
22
+
23
+ include_examples 'parse', /(?m:a)/,
24
+ 0 => [:group, :options, Group::Options, options: { m: true }, option_changes: { m: true }]
25
+
26
+ # self-defeating group option
27
+ include_examples 'parse', /(?m-m:a)/,
28
+ 0 => [:group, :options, Group::Options, options: {}, option_changes: { m: false }]
29
+
30
+ # activate one option in nested group
31
+ include_examples 'parse', /(?x-mi:a(?m:b))/,
32
+ 0 => [:group, :options, Group::Options, options: { x: true }, option_changes: { i: false, m: false, x: true }],
33
+ [0, 1] => [:group, :options, Group::Options, options: { m: true, x: true }, option_changes: { m: true }]
34
+
35
+ # deactivate one option in nested group
36
+ include_examples 'parse', /(?ix-m:a(?-i:b))/,
37
+ 0 => [:group, :options, Group::Options, options: { i: true, x: true }, option_changes: { i: true, m: false, x: true }],
38
+ [0, 1] => [:group, :options, Group::Options, options: { x: true }, option_changes: { i: false }]
39
+
40
+ # invert all options in nested group
41
+ include_examples 'parse', /(?xi-m:a(?m-ix:b))/,
42
+ 0 => [:group, :options, Group::Options, options: { i: true, x: true }, option_changes: { i: true, m: false, x: true }],
43
+ [0, 1] => [:group, :options, Group::Options, options: { m: true }, option_changes: { i: false, m: true, x: false }]
44
+
45
+ # nested options affect literal subexpressions
46
+ include_examples 'parse', /(?x-mi:a(?m:b))/,
47
+ [0, 0] => [:literal, :literal, Literal, text: 'a', options: { x: true }],
48
+ [0, 1, 0] => [:literal, :literal, Literal, text: 'b', options: { m: true, x: true }]
49
+
50
+ # option switching group
51
+ include_examples 'parse', /a(?i-m)b/m,
52
+ 0 => [:literal, :literal, Literal, text: 'a', options: { m: true }],
53
+ 1 => [:group, :options_switch, Group::Options, options: { i: true }, option_changes: { i: true, m: false }],
54
+ 2 => [:literal, :literal, Literal, text: 'b', options: { i: true }]
55
+
56
+ # option switch in group
57
+ include_examples 'parse', /(a(?i-m)b)c/m,
58
+ 0 => [:group, :capture, Group::Capture, options: { m: true }],
59
+ [0, 0] => [:literal, :literal, Literal, text: 'a', options: { m: true }],
60
+ [0, 1] => [:group, :options_switch, Group::Options, options: { i: true }, option_changes: { i: true, m: false }],
61
+ [0, 2] => [:literal, :literal, Literal, text: 'b', options: { i: true }],
62
+ 1 => [:literal, :literal, Literal, text: 'c', options: { m: true }]
63
+
64
+ # nested option switch in group
65
+ include_examples 'parse', /((?i-m)(a(?-i)b))/m,
66
+ [0, 1] => [:group, :capture, Group::Capture, options: { i: true }],
67
+ [0, 1, 0] => [:literal, :literal, Literal, text: 'a', options: { i: true }],
68
+ [0, 1, 1] => [:group, :options_switch, Group::Options, options: {}, option_changes: { i: false }],
69
+ [0, 1, 2] => [:literal, :literal, Literal, text: 'b', options: {}]
70
+
71
+ # options dau
72
+ include_examples 'parse', /(?dua:abc)/,
73
+ 0 => [:group, :options, Group::Options, options: { a: true }, option_changes: { a: true }]
74
+
75
+ # nested options dau
76
+ include_examples 'parse', /(?u:a(?d:b))/,
77
+ 0 => [:group, :options, Group::Options, options: { u: true }, option_changes: { u: true }],
78
+ [0, 1] => [:group, :options, Group::Options, options: { d: true }, option_changes: { d: true, u: false }],
79
+ [0, 1, 0] => [:literal, :literal, Literal, text: 'b', options: { d: true }]
80
+
81
+ # nested options da
82
+ include_examples 'parse', /(?di-xm:a(?da-x:b))/,
83
+ 0 => [:group, :options, Group::Options, options: { d: true, i:true }],
84
+ [0, 1] => [:group, :options, Group::Options, options: { a: true, i: true }, option_changes: { a: true, d: false, x: false}],
85
+ [0, 1, 0] => [:literal, :literal, Literal, text: 'b', options: { a: true, i: true }]
247
86
 
248
87
  specify('parse group number') do
249
- t = RP.parse('(a)(?=b)((?:c)(d|(e)))')
250
- expect(t[0].number).to eq 1
251
- expect(t[1]).not_to respond_to(:number)
252
- expect(t[2].number).to eq 2
253
- expect(t[2][0]).not_to respond_to(:number)
254
- expect(t[2][1].number).to eq 3
255
- expect(t[2][1][0][1][0].number).to eq 4
88
+ root = RP.parse(/(a)(?=b)((?:c)(d|(e)))/)
89
+
90
+ expect(root[0].number).to eq 1
91
+ expect(root[1]).not_to respond_to(:number)
92
+ expect(root[2].number).to eq 2
93
+ expect(root[2][0]).not_to respond_to(:number)
94
+ expect(root[2][1].number).to eq 3
95
+ expect(root[2][1][0][1][0].number).to eq 4
256
96
  end
257
97
 
258
98
  specify('parse group number at level') do
259
- t = RP.parse('(a)(?=b)((?:c)(d|(e)))')
260
- expect(t[0].number_at_level).to eq 1
261
- expect(t[1]).not_to respond_to(:number_at_level)
262
- expect(t[2].number_at_level).to eq 2
263
- expect(t[2][0]).not_to respond_to(:number_at_level)
264
- expect(t[2][1].number_at_level).to eq 1
265
- expect(t[2][1][0][1][0].number_at_level).to eq 1
99
+ root = RP.parse(/(a)(?=b)((?:c)(d|(e)))/)
100
+
101
+ expect(root[0].number_at_level).to eq 1
102
+ expect(root[1]).not_to respond_to(:number_at_level)
103
+ expect(root[2].number_at_level).to eq 2
104
+ expect(root[2][0]).not_to respond_to(:number_at_level)
105
+ expect(root[2][1].number_at_level).to eq 1
106
+ expect(root[2][1][0][1][0].number_at_level).to eq 1
266
107
  end
267
108
  end