regexp_parser 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/Gemfile +1 -1
  4. data/README.md +9 -13
  5. data/lib/regexp_parser/expression.rb +33 -21
  6. data/lib/regexp_parser/expression/classes/backref.rb +18 -10
  7. data/lib/regexp_parser/expression/classes/conditional.rb +4 -0
  8. data/lib/regexp_parser/expression/classes/group.rb +4 -2
  9. data/lib/regexp_parser/expression/classes/keep.rb +1 -3
  10. data/lib/regexp_parser/expression/methods/match_length.rb +172 -0
  11. data/lib/regexp_parser/expression/quantifier.rb +2 -2
  12. data/lib/regexp_parser/expression/sequence.rb +0 -4
  13. data/lib/regexp_parser/expression/subexpression.rb +3 -5
  14. data/lib/regexp_parser/lexer.rb +31 -24
  15. data/lib/regexp_parser/parser.rb +25 -3
  16. data/lib/regexp_parser/syntax/tokens.rb +2 -10
  17. data/lib/regexp_parser/version.rb +1 -1
  18. data/regexp_parser.gemspec +2 -2
  19. data/spec/expression/base_spec.rb +80 -0
  20. data/spec/expression/clone_spec.rb +120 -0
  21. data/spec/expression/conditional_spec.rb +89 -0
  22. data/spec/expression/free_space_spec.rb +27 -0
  23. data/spec/expression/methods/match_length_spec.rb +141 -0
  24. data/spec/expression/methods/strfregexp_spec.rb +224 -0
  25. data/spec/expression/methods/tests_spec.rb +97 -0
  26. data/spec/expression/methods/traverse_spec.rb +140 -0
  27. data/spec/expression/subexpression_spec.rb +50 -0
  28. data/spec/expression/to_h_spec.rb +26 -0
  29. data/spec/expression/to_s_spec.rb +100 -0
  30. data/spec/lexer/all_spec.rb +22 -0
  31. data/{test/lexer/test_conditionals.rb → spec/lexer/conditionals_spec.rb} +31 -35
  32. data/spec/lexer/escapes_spec.rb +38 -0
  33. data/spec/lexer/keep_spec.rb +22 -0
  34. data/{test/lexer/test_literals.rb → spec/lexer/literals_spec.rb} +20 -24
  35. data/{test/lexer/test_nesting.rb → spec/lexer/nesting_spec.rb} +11 -13
  36. data/spec/lexer/refcalls_spec.rb +54 -0
  37. data/spec/parser/all_spec.rb +31 -0
  38. data/spec/parser/alternation_spec.rb +88 -0
  39. data/{test/parser/test_anchors.rb → spec/parser/anchors_spec.rb} +7 -10
  40. data/spec/parser/conditionals_spec.rb +179 -0
  41. data/spec/parser/errors_spec.rb +51 -0
  42. data/spec/parser/escapes_spec.rb +132 -0
  43. data/spec/parser/free_space_spec.rb +130 -0
  44. data/spec/parser/groups_spec.rb +267 -0
  45. data/spec/parser/keep_spec.rb +19 -0
  46. data/spec/parser/posix_classes_spec.rb +27 -0
  47. data/spec/parser/properties_spec.rb +127 -0
  48. data/spec/parser/quantifiers_spec.rb +293 -0
  49. data/spec/parser/refcalls_spec.rb +237 -0
  50. data/spec/parser/set/intersections_spec.rb +127 -0
  51. data/spec/parser/set/ranges_spec.rb +111 -0
  52. data/spec/parser/sets_spec.rb +178 -0
  53. data/{test/parser/test_types.rb → spec/parser/types_spec.rb} +13 -20
  54. data/spec/scanner/all_spec.rb +18 -0
  55. data/{test/scanner/test_anchors.rb → spec/scanner/anchors_spec.rb} +8 -10
  56. data/{test/scanner/test_conditionals.rb → spec/scanner/conditionals_spec.rb} +49 -53
  57. data/spec/scanner/errors_spec.rb +90 -0
  58. data/{test/scanner/test_escapes.rb → spec/scanner/escapes_spec.rb} +8 -10
  59. data/{test/scanner/test_free_space.rb → spec/scanner/free_space_spec.rb} +48 -52
  60. data/{test/scanner/test_groups.rb → spec/scanner/groups_spec.rb} +33 -41
  61. data/spec/scanner/keep_spec.rb +33 -0
  62. data/{test/scanner/test_literals.rb → spec/scanner/literals_spec.rb} +8 -12
  63. data/{test/scanner/test_meta.rb → spec/scanner/meta_spec.rb} +8 -10
  64. data/{test/scanner/test_properties.rb → spec/scanner/properties_spec.rb} +14 -19
  65. data/{test/scanner/test_quantifiers.rb → spec/scanner/quantifiers_spec.rb} +7 -9
  66. data/{test/scanner/test_refcalls.rb → spec/scanner/refcalls_spec.rb} +9 -9
  67. data/{test/scanner/test_scripts.rb → spec/scanner/scripts_spec.rb} +8 -12
  68. data/{test/scanner/test_sets.rb → spec/scanner/sets_spec.rb} +14 -17
  69. data/spec/scanner/types_spec.rb +29 -0
  70. data/spec/scanner/unicode_blocks_spec.rb +28 -0
  71. data/spec/spec_helper.rb +14 -0
  72. data/{test → spec}/support/runner.rb +9 -8
  73. data/{test → spec}/support/warning_extractor.rb +5 -7
  74. data/spec/syntax/syntax_spec.rb +44 -0
  75. data/spec/syntax/syntax_token_map_spec.rb +23 -0
  76. data/spec/syntax/versions/1.8.6_spec.rb +38 -0
  77. data/spec/syntax/versions/1.9.1_spec.rb +23 -0
  78. data/spec/syntax/versions/1.9.3_spec.rb +22 -0
  79. data/spec/syntax/versions/2.0.0_spec.rb +28 -0
  80. data/spec/syntax/versions/2.2.0_spec.rb +22 -0
  81. data/spec/syntax/versions/aliases_spec.rb +119 -0
  82. data/spec/token/token_spec.rb +85 -0
  83. metadata +131 -140
  84. data/test/expression/test_all.rb +0 -12
  85. data/test/expression/test_base.rb +0 -90
  86. data/test/expression/test_clone.rb +0 -89
  87. data/test/expression/test_conditionals.rb +0 -113
  88. data/test/expression/test_free_space.rb +0 -35
  89. data/test/expression/test_set.rb +0 -84
  90. data/test/expression/test_strfregexp.rb +0 -230
  91. data/test/expression/test_subexpression.rb +0 -58
  92. data/test/expression/test_tests.rb +0 -99
  93. data/test/expression/test_to_h.rb +0 -59
  94. data/test/expression/test_to_s.rb +0 -104
  95. data/test/expression/test_traverse.rb +0 -161
  96. data/test/helpers.rb +0 -10
  97. data/test/lexer/test_all.rb +0 -41
  98. data/test/lexer/test_keep.rb +0 -24
  99. data/test/lexer/test_refcalls.rb +0 -56
  100. data/test/parser/set/test_intersections.rb +0 -127
  101. data/test/parser/set/test_ranges.rb +0 -111
  102. data/test/parser/test_all.rb +0 -64
  103. data/test/parser/test_alternation.rb +0 -92
  104. data/test/parser/test_conditionals.rb +0 -187
  105. data/test/parser/test_errors.rb +0 -63
  106. data/test/parser/test_escapes.rb +0 -134
  107. data/test/parser/test_free_space.rb +0 -139
  108. data/test/parser/test_groups.rb +0 -289
  109. data/test/parser/test_keep.rb +0 -21
  110. data/test/parser/test_posix_classes.rb +0 -27
  111. data/test/parser/test_properties.rb +0 -134
  112. data/test/parser/test_quantifiers.rb +0 -301
  113. data/test/parser/test_refcalls.rb +0 -186
  114. data/test/parser/test_sets.rb +0 -179
  115. data/test/scanner/test_all.rb +0 -38
  116. data/test/scanner/test_errors.rb +0 -91
  117. data/test/scanner/test_keep.rb +0 -35
  118. data/test/scanner/test_types.rb +0 -35
  119. data/test/scanner/test_unicode_blocks.rb +0 -30
  120. data/test/support/disable_autotest.rb +0 -8
  121. data/test/syntax/test_all.rb +0 -6
  122. data/test/syntax/test_syntax.rb +0 -61
  123. data/test/syntax/test_syntax_token_map.rb +0 -25
  124. data/test/syntax/versions/test_1.8.rb +0 -55
  125. data/test/syntax/versions/test_1.9.1.rb +0 -36
  126. data/test/syntax/versions/test_1.9.3.rb +0 -32
  127. data/test/syntax/versions/test_2.0.0.rb +0 -37
  128. data/test/syntax/versions/test_2.2.0.rb +0 -32
  129. data/test/syntax/versions/test_aliases.rb +0 -129
  130. data/test/syntax/versions/test_all.rb +0 -5
  131. data/test/test_all.rb +0 -5
  132. data/test/token/test_all.rb +0 -2
  133. data/test/token/test_token.rb +0 -107
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Parsing errors') do
4
+ let(:rp) { Regexp::Parser.new }
5
+ before { rp.parse(/foo/) }
6
+
7
+ specify('parser unknown token type') do
8
+ expect { rp.send(:parse_token, Regexp::Token.new(:foo, :bar)) }
9
+ .to raise_error(Regexp::Parser::UnknownTokenTypeError)
10
+ end
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
51
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
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
47
+
48
+ specify('parse chars and codepoints') do
49
+ root = RP.parse(/\n\?\101\x42\u0043\u{44 45}/)
50
+
51
+ expect(root[0].char).to eq "\n"
52
+ expect(root[0].codepoint).to eq 10
53
+
54
+ expect(root[1].char).to eq '?'
55
+ expect(root[1].codepoint).to eq 63
56
+
57
+ expect(root[2].char).to eq 'A'
58
+ expect(root[2].codepoint).to eq 65
59
+
60
+ expect(root[3].char).to eq 'B'
61
+ expect(root[3].codepoint).to eq 66
62
+
63
+ expect(root[4].char).to eq 'C'
64
+ expect(root[4].codepoint).to eq 67
65
+
66
+ expect(root[5].chars).to eq %w[D E]
67
+ expect(root[5].codepoints).to eq [68, 69]
68
+ end
69
+
70
+ specify('parse escape control sequence lower') do
71
+ root = RP.parse(/a\\\c2b/)
72
+
73
+ expect(root[2]).to be_instance_of(EscapeSequence::Control)
74
+ expect(root[2].text).to eq '\\c2'
75
+ expect(root[2].char).to eq "\x12"
76
+ expect(root[2].codepoint).to eq 18
77
+ end
78
+
79
+ specify('parse escape control sequence upper') do
80
+ root = RP.parse(/\d\\\C-C\w/)
81
+
82
+ expect(root[2]).to be_instance_of(EscapeSequence::Control)
83
+ expect(root[2].text).to eq '\\C-C'
84
+ expect(root[2].char).to eq "\x03"
85
+ expect(root[2].codepoint).to eq 3
86
+ end
87
+
88
+ specify('parse escape meta sequence') do
89
+ root = RP.parse(/\Z\\\M-Z/n)
90
+
91
+ expect(root[2]).to be_instance_of(EscapeSequence::Meta)
92
+ expect(root[2].text).to eq '\\M-Z'
93
+ expect(root[2].char).to eq "\u00DA"
94
+ expect(root[2].codepoint).to eq 218
95
+ end
96
+
97
+ specify('parse escape meta control sequence') do
98
+ root = RP.parse(/\A\\\M-\C-X/n)
99
+
100
+ expect(root[2]).to be_instance_of(EscapeSequence::MetaControl)
101
+ expect(root[2].text).to eq '\\M-\\C-X'
102
+ expect(root[2].char).to eq "\u0098"
103
+ expect(root[2].codepoint).to eq 152
104
+ end
105
+
106
+ specify('parse lower c meta control sequence') do
107
+ root = RP.parse(/\A\\\M-\cX/n)
108
+
109
+ expect(root[2]).to be_instance_of(EscapeSequence::MetaControl)
110
+ expect(root[2].text).to eq '\\M-\\cX'
111
+ expect(root[2].char).to eq "\u0098"
112
+ expect(root[2].codepoint).to eq 152
113
+ end
114
+
115
+ specify('parse escape reverse meta control sequence') do
116
+ root = RP.parse(/\A\\\C-\M-X/n)
117
+
118
+ expect(root[2]).to be_instance_of(EscapeSequence::MetaControl)
119
+ expect(root[2].text).to eq '\\C-\\M-X'
120
+ expect(root[2].char).to eq "\u0098"
121
+ expect(root[2].codepoint).to eq 152
122
+ end
123
+
124
+ specify('parse escape reverse lower c meta control sequence') do
125
+ root = RP.parse(/\A\\\c\M-X/n)
126
+
127
+ expect(root[2]).to be_instance_of(EscapeSequence::MetaControl)
128
+ expect(root[2].text).to eq '\\c\\M-X'
129
+ expect(root[2].char).to eq "\u0098"
130
+ expect(root[2].codepoint).to eq 152
131
+ end
132
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('FreeSpace parsing') do
4
+ specify('parse free space spaces') do
5
+ regexp = /a ? b * c + d{2,4}/x
6
+ root = RP.parse(regexp)
7
+
8
+ 0.upto(6) do |i|
9
+ if i.odd?
10
+ expect(root[i]).to be_instance_of(WhiteSpace)
11
+ expect(root[i].text).to eq ' '
12
+ else
13
+ expect(root[i]).to be_instance_of(Literal)
14
+ expect(root[i]).to be_quantified
15
+ end
16
+ end
17
+ end
18
+
19
+ specify('parse non free space literals') do
20
+ regexp = /a b c d/
21
+ root = RP.parse(regexp)
22
+
23
+ expect(root.first).to be_instance_of(Literal)
24
+ expect(root.first.text).to eq 'a b c d'
25
+ end
26
+
27
+ specify('parse free space comments') do
28
+ regexp = /
29
+ a ? # One letter
30
+ b {2,5} # Another one
31
+ [c-g] + # A set
32
+ (h|i|j) | # A group
33
+ klm *
34
+ nop +
35
+ /x
36
+
37
+ root = RP.parse(regexp)
38
+
39
+ alt = root.first
40
+ expect(alt).to be_instance_of(Alternation)
41
+
42
+ alt_1 = alt.alternatives.first
43
+ expect(alt_1).to be_instance_of(Alternative)
44
+ expect(alt_1.length).to eq 15
45
+
46
+ [0, 2, 4, 6, 8, 12, 14].each do |i|
47
+ expect(alt_1[i]).to be_instance_of(WhiteSpace)
48
+ end
49
+
50
+ [3, 7, 11].each { |i| expect(alt_1[i].class).to eq Comment }
51
+
52
+ alt_2 = alt.alternatives.last
53
+ expect(alt_2).to be_instance_of(Alternative)
54
+ expect(alt_2.length).to eq 7
55
+
56
+ [0, 2, 4, 6].each { |i| expect(alt_2[i].class).to eq WhiteSpace }
57
+
58
+ expect(alt_2[1]).to be_instance_of(Comment)
59
+ end
60
+
61
+ specify('parse free space nested comments') do
62
+ regexp = /
63
+ # Group one
64
+ (
65
+ abc # Comment one
66
+ \d? # Optional \d
67
+ )+
68
+
69
+ # Group two
70
+ (
71
+ def # Comment two
72
+ \s? # Optional \s
73
+ )?
74
+ /x
75
+
76
+ root = RP.parse(regexp)
77
+
78
+ top_comment_1 = root[1]
79
+ expect(top_comment_1).to be_instance_of(Comment)
80
+ expect(top_comment_1.text).to eq "# Group one\n"
81
+ expect(top_comment_1.starts_at).to eq 7
82
+
83
+ top_comment_2 = root[5]
84
+ expect(top_comment_2).to be_instance_of(Comment)
85
+ expect(top_comment_2.text).to eq "# Group two\n"
86
+ expect(top_comment_2.starts_at).to eq 95
87
+
88
+ [3, 7].each do |g,|
89
+ group = root[g]
90
+
91
+ [3, 7].each do |c|
92
+ comment = group[c]
93
+ expect(comment).to be_instance_of(Comment)
94
+ expect(comment.text.length).to eq 14
95
+ end
96
+ end
97
+ end
98
+
99
+ specify('parse free space quantifiers') do
100
+ regexp = /
101
+ a
102
+ # comment 1
103
+ ?
104
+ (
105
+ b # comment 2
106
+ # comment 3
107
+ +
108
+ )
109
+ # comment 4
110
+ *
111
+ /x
112
+
113
+ root = RP.parse(regexp)
114
+
115
+ literal_1 = root[1]
116
+ expect(literal_1).to be_instance_of(Literal)
117
+ expect(literal_1).to be_quantified
118
+ expect(literal_1.quantifier.token).to eq :zero_or_one
119
+
120
+ group = root[5]
121
+ expect(group).to be_instance_of(Group::Capture)
122
+ expect(group).to be_quantified
123
+ expect(group.quantifier.token).to eq :zero_or_more
124
+
125
+ literal_2 = group[1]
126
+ expect(literal_2).to be_instance_of(Literal)
127
+ expect(literal_2).to be_quantified
128
+ expect(literal_2.quantifier.token).to eq :one_or_more
129
+ end
130
+ end
@@ -0,0 +1,267 @@
1
+ require 'spec_helper'
2
+
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
247
+
248
+ 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
256
+ end
257
+
258
+ 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
266
+ end
267
+ end