regexp_parser 1.3.0 → 1.7.1

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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -1
  3. data/Gemfile +3 -3
  4. data/README.md +12 -19
  5. data/Rakefile +3 -4
  6. data/lib/regexp_parser/expression.rb +28 -53
  7. data/lib/regexp_parser/expression/classes/backref.rb +18 -10
  8. data/lib/regexp_parser/expression/classes/conditional.rb +7 -2
  9. data/lib/regexp_parser/expression/classes/escape.rb +0 -4
  10. data/lib/regexp_parser/expression/classes/group.rb +4 -2
  11. data/lib/regexp_parser/expression/classes/keep.rb +1 -3
  12. data/lib/regexp_parser/expression/methods/match.rb +13 -0
  13. data/lib/regexp_parser/expression/methods/match_length.rb +172 -0
  14. data/lib/regexp_parser/expression/methods/options.rb +35 -0
  15. data/lib/regexp_parser/expression/methods/strfregexp.rb +0 -1
  16. data/lib/regexp_parser/expression/methods/tests.rb +6 -15
  17. data/lib/regexp_parser/expression/methods/traverse.rb +3 -1
  18. data/lib/regexp_parser/expression/quantifier.rb +2 -2
  19. data/lib/regexp_parser/expression/sequence.rb +3 -6
  20. data/lib/regexp_parser/expression/sequence_operation.rb +2 -6
  21. data/lib/regexp_parser/expression/subexpression.rb +3 -5
  22. data/lib/regexp_parser/lexer.rb +30 -44
  23. data/lib/regexp_parser/parser.rb +47 -24
  24. data/lib/regexp_parser/scanner.rb +1228 -1367
  25. data/lib/regexp_parser/scanner/char_type.rl +0 -3
  26. data/lib/regexp_parser/scanner/properties/long.yml +34 -1
  27. data/lib/regexp_parser/scanner/properties/short.yml +12 -0
  28. data/lib/regexp_parser/scanner/scanner.rl +101 -194
  29. data/lib/regexp_parser/syntax/tokens.rb +2 -10
  30. data/lib/regexp_parser/syntax/tokens/unicode_property.rb +72 -21
  31. data/lib/regexp_parser/syntax/versions/2.6.0.rb +10 -0
  32. data/lib/regexp_parser/syntax/versions/2.6.2.rb +10 -0
  33. data/lib/regexp_parser/syntax/versions/2.6.3.rb +10 -0
  34. data/lib/regexp_parser/version.rb +1 -1
  35. data/regexp_parser.gemspec +3 -3
  36. data/spec/expression/base_spec.rb +94 -0
  37. data/spec/expression/clone_spec.rb +120 -0
  38. data/spec/expression/conditional_spec.rb +89 -0
  39. data/spec/expression/free_space_spec.rb +27 -0
  40. data/spec/expression/methods/match_length_spec.rb +161 -0
  41. data/spec/expression/methods/match_spec.rb +25 -0
  42. data/spec/expression/methods/strfregexp_spec.rb +224 -0
  43. data/spec/expression/methods/tests_spec.rb +99 -0
  44. data/spec/expression/methods/traverse_spec.rb +161 -0
  45. data/spec/expression/options_spec.rb +128 -0
  46. data/spec/expression/root_spec.rb +9 -0
  47. data/spec/expression/sequence_spec.rb +9 -0
  48. data/spec/expression/subexpression_spec.rb +50 -0
  49. data/spec/expression/to_h_spec.rb +26 -0
  50. data/spec/expression/to_s_spec.rb +100 -0
  51. data/spec/lexer/all_spec.rb +22 -0
  52. data/spec/lexer/conditionals_spec.rb +53 -0
  53. data/spec/lexer/delimiters_spec.rb +68 -0
  54. data/spec/lexer/escapes_spec.rb +14 -0
  55. data/spec/lexer/keep_spec.rb +10 -0
  56. data/spec/lexer/literals_spec.rb +89 -0
  57. data/spec/lexer/nesting_spec.rb +99 -0
  58. data/spec/lexer/refcalls_spec.rb +55 -0
  59. data/spec/parser/all_spec.rb +43 -0
  60. data/spec/parser/alternation_spec.rb +88 -0
  61. data/spec/parser/anchors_spec.rb +17 -0
  62. data/spec/parser/conditionals_spec.rb +179 -0
  63. data/spec/parser/errors_spec.rb +30 -0
  64. data/spec/parser/escapes_spec.rb +121 -0
  65. data/spec/parser/free_space_spec.rb +130 -0
  66. data/spec/parser/groups_spec.rb +108 -0
  67. data/spec/parser/keep_spec.rb +6 -0
  68. data/spec/parser/posix_classes_spec.rb +8 -0
  69. data/spec/parser/properties_spec.rb +115 -0
  70. data/spec/parser/quantifiers_spec.rb +52 -0
  71. data/spec/parser/refcalls_spec.rb +112 -0
  72. data/spec/parser/set/intersections_spec.rb +127 -0
  73. data/spec/parser/set/ranges_spec.rb +111 -0
  74. data/spec/parser/sets_spec.rb +178 -0
  75. data/spec/parser/types_spec.rb +18 -0
  76. data/spec/scanner/all_spec.rb +18 -0
  77. data/spec/scanner/anchors_spec.rb +21 -0
  78. data/spec/scanner/conditionals_spec.rb +128 -0
  79. data/spec/scanner/delimiters_spec.rb +52 -0
  80. data/spec/scanner/errors_spec.rb +67 -0
  81. data/spec/scanner/escapes_spec.rb +53 -0
  82. data/spec/scanner/free_space_spec.rb +133 -0
  83. data/spec/scanner/groups_spec.rb +52 -0
  84. data/spec/scanner/keep_spec.rb +10 -0
  85. data/spec/scanner/literals_spec.rb +49 -0
  86. data/spec/scanner/meta_spec.rb +18 -0
  87. data/spec/scanner/properties_spec.rb +64 -0
  88. data/spec/scanner/quantifiers_spec.rb +20 -0
  89. data/spec/scanner/refcalls_spec.rb +36 -0
  90. data/spec/scanner/sets_spec.rb +102 -0
  91. data/spec/scanner/types_spec.rb +14 -0
  92. data/spec/spec_helper.rb +15 -0
  93. data/{test → spec}/support/runner.rb +9 -8
  94. data/spec/support/shared_examples.rb +77 -0
  95. data/{test → spec}/support/warning_extractor.rb +5 -7
  96. data/spec/syntax/syntax_spec.rb +48 -0
  97. data/spec/syntax/syntax_token_map_spec.rb +23 -0
  98. data/spec/syntax/versions/1.8.6_spec.rb +17 -0
  99. data/spec/syntax/versions/1.9.1_spec.rb +10 -0
  100. data/spec/syntax/versions/1.9.3_spec.rb +9 -0
  101. data/spec/syntax/versions/2.0.0_spec.rb +13 -0
  102. data/spec/syntax/versions/2.2.0_spec.rb +9 -0
  103. data/spec/syntax/versions/aliases_spec.rb +37 -0
  104. data/spec/token/token_spec.rb +85 -0
  105. metadata +151 -146
  106. data/test/expression/test_all.rb +0 -12
  107. data/test/expression/test_base.rb +0 -90
  108. data/test/expression/test_clone.rb +0 -89
  109. data/test/expression/test_conditionals.rb +0 -113
  110. data/test/expression/test_free_space.rb +0 -35
  111. data/test/expression/test_set.rb +0 -84
  112. data/test/expression/test_strfregexp.rb +0 -230
  113. data/test/expression/test_subexpression.rb +0 -58
  114. data/test/expression/test_tests.rb +0 -99
  115. data/test/expression/test_to_h.rb +0 -59
  116. data/test/expression/test_to_s.rb +0 -104
  117. data/test/expression/test_traverse.rb +0 -161
  118. data/test/helpers.rb +0 -10
  119. data/test/lexer/test_all.rb +0 -41
  120. data/test/lexer/test_conditionals.rb +0 -127
  121. data/test/lexer/test_keep.rb +0 -24
  122. data/test/lexer/test_literals.rb +0 -130
  123. data/test/lexer/test_nesting.rb +0 -132
  124. data/test/lexer/test_refcalls.rb +0 -56
  125. data/test/parser/set/test_intersections.rb +0 -127
  126. data/test/parser/set/test_ranges.rb +0 -111
  127. data/test/parser/test_all.rb +0 -64
  128. data/test/parser/test_alternation.rb +0 -92
  129. data/test/parser/test_anchors.rb +0 -34
  130. data/test/parser/test_conditionals.rb +0 -187
  131. data/test/parser/test_errors.rb +0 -63
  132. data/test/parser/test_escapes.rb +0 -134
  133. data/test/parser/test_free_space.rb +0 -139
  134. data/test/parser/test_groups.rb +0 -289
  135. data/test/parser/test_keep.rb +0 -21
  136. data/test/parser/test_posix_classes.rb +0 -27
  137. data/test/parser/test_properties.rb +0 -133
  138. data/test/parser/test_quantifiers.rb +0 -301
  139. data/test/parser/test_refcalls.rb +0 -186
  140. data/test/parser/test_sets.rb +0 -179
  141. data/test/parser/test_types.rb +0 -50
  142. data/test/scanner/test_all.rb +0 -38
  143. data/test/scanner/test_anchors.rb +0 -38
  144. data/test/scanner/test_conditionals.rb +0 -184
  145. data/test/scanner/test_errors.rb +0 -91
  146. data/test/scanner/test_escapes.rb +0 -56
  147. data/test/scanner/test_free_space.rb +0 -200
  148. data/test/scanner/test_groups.rb +0 -79
  149. data/test/scanner/test_keep.rb +0 -35
  150. data/test/scanner/test_literals.rb +0 -89
  151. data/test/scanner/test_meta.rb +0 -40
  152. data/test/scanner/test_properties.rb +0 -312
  153. data/test/scanner/test_quantifiers.rb +0 -37
  154. data/test/scanner/test_refcalls.rb +0 -52
  155. data/test/scanner/test_scripts.rb +0 -53
  156. data/test/scanner/test_sets.rb +0 -119
  157. data/test/scanner/test_types.rb +0 -35
  158. data/test/scanner/test_unicode_blocks.rb +0 -30
  159. data/test/support/disable_autotest.rb +0 -8
  160. data/test/syntax/test_all.rb +0 -6
  161. data/test/syntax/test_syntax.rb +0 -61
  162. data/test/syntax/test_syntax_token_map.rb +0 -25
  163. data/test/syntax/versions/test_1.8.rb +0 -55
  164. data/test/syntax/versions/test_1.9.1.rb +0 -36
  165. data/test/syntax/versions/test_1.9.3.rb +0 -32
  166. data/test/syntax/versions/test_2.0.0.rb +0 -37
  167. data/test/syntax/versions/test_2.2.0.rb +0 -32
  168. data/test/syntax/versions/test_aliases.rb +0 -129
  169. data/test/syntax/versions/test_all.rb +0 -5
  170. data/test/test_all.rb +0 -5
  171. data/test/token/test_all.rb +0 -2
  172. data/test/token/test_token.rb +0 -107
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Subexpression#traverse') do
4
+ specify('Subexpression#traverse') do
5
+ root = RP.parse(/a(b(c(d)))|g[h-i]j|klmn/)
6
+
7
+ enters = 0
8
+ visits = 0
9
+ exits = 0
10
+
11
+ root.traverse do |event, _exp, _index|
12
+ enters = (enters + 1) if event == :enter
13
+ visits = (visits + 1) if event == :visit
14
+ exits = (exits + 1) if event == :exit
15
+ end
16
+
17
+ expect(enters).to eq 9
18
+ expect(enters).to eq exits
19
+
20
+ expect(visits).to eq 9
21
+ end
22
+
23
+ specify('Subexpression#traverse including self') do
24
+ root = RP.parse(/a(b(c(d)))|g[h-i]j|klmn/)
25
+
26
+ enters = 0
27
+ visits = 0
28
+ exits = 0
29
+
30
+ root.traverse(true) do |event, _exp, _index|
31
+ enters = (enters + 1) if event == :enter
32
+ visits = (visits + 1) if event == :visit
33
+ exits = (exits + 1) if event == :exit
34
+ end
35
+
36
+ expect(enters).to eq 10
37
+ expect(enters).to eq exits
38
+
39
+ expect(visits).to eq 9
40
+ end
41
+
42
+ specify('Subexpression#traverse without a block') do
43
+ root = RP.parse(/abc/)
44
+ enum = root.traverse
45
+
46
+ expect(enum).to be_a(Enumerator)
47
+ event, expr, idx = enum.next
48
+ expect(event).to eq(:visit)
49
+ expect(expr).to be_a(Regexp::Expression::Literal)
50
+ expect(idx).to eq(0)
51
+ end
52
+
53
+ specify('Subexpression#walk alias') do
54
+ root = RP.parse(/abc/)
55
+
56
+ expect(root).to respond_to(:walk)
57
+ end
58
+
59
+ specify('Subexpression#each_expression') do
60
+ root = RP.parse(/a(?x:b(c))|g[h-k]/)
61
+
62
+ count = 0
63
+ root.each_expression { count += 1 }
64
+
65
+ expect(count).to eq 13
66
+ end
67
+
68
+ specify('Subexpression#each_expression including self') do
69
+ root = RP.parse(/a(?x:b(c))|g[h-k]/)
70
+
71
+ count = 0
72
+ root.each_expression(true) { count += 1 }
73
+
74
+ expect(count).to eq 14
75
+ end
76
+
77
+ specify('Subexpression#each_expression indices') do
78
+ root = RP.parse(/a(b)c/)
79
+
80
+ indices = []
81
+ root.each_expression { |_exp, index| (indices << index) }
82
+
83
+ expect(indices).to eq [0, 1, 0, 2]
84
+ end
85
+
86
+ specify('Subexpression#each_expression indices including self') do
87
+ root = RP.parse(/a(b)c/)
88
+
89
+ indices = []
90
+ root.each_expression(true) { |_exp, index| (indices << index) }
91
+
92
+ expect(indices).to eq [0, 0, 1, 0, 2]
93
+ end
94
+
95
+ specify('Subexpression#each_expression without a block') do
96
+ root = RP.parse(/abc/)
97
+ enum = root.each_expression
98
+
99
+ expect(enum).to be_a(Enumerator)
100
+ expr, idx = enum.next
101
+ expect(expr).to be_a(Regexp::Expression::Literal)
102
+ expect(idx).to eq(0)
103
+ end
104
+
105
+ specify('Subexpression#flat_map without block') do
106
+ root = RP.parse(/a(b([c-e]+))?/)
107
+
108
+ array = root.flat_map
109
+
110
+ expect(array).to be_instance_of(Array)
111
+ expect(array.length).to eq 8
112
+
113
+ array.each do |item|
114
+ expect(item).to be_instance_of(Array)
115
+ expect(item.length).to eq 2
116
+ expect(item.first).to be_a(Regexp::Expression::Base)
117
+ expect(item.last).to be_a(Integer)
118
+ end
119
+ end
120
+
121
+ specify('Subexpression#flat_map without block including self') do
122
+ root = RP.parse(/a(b([c-e]+))?/)
123
+
124
+ array = root.flat_map(true)
125
+
126
+ expect(array).to be_instance_of(Array)
127
+ expect(array.length).to eq 9
128
+ end
129
+
130
+ specify('Subexpression#flat_map indices') do
131
+ root = RP.parse(/a(b([c-e]+))?f*g/)
132
+
133
+ indices = root.flat_map { |_exp, index| index }
134
+
135
+ expect(indices).to eq [0, 1, 0, 1, 0, 0, 0, 1, 2, 3]
136
+ end
137
+
138
+ specify('Subexpression#flat_map indices including self') do
139
+ root = RP.parse(/a(b([c-e]+))?f*g/)
140
+
141
+ indices = root.flat_map(true) { |_exp, index| index }
142
+
143
+ expect(indices).to eq [0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 3]
144
+ end
145
+
146
+ specify('Subexpression#flat_map expressions') do
147
+ root = RP.parse(/a(b(c(d)))/)
148
+
149
+ levels = root.flat_map { |exp, _index| [exp.level, exp.text] if exp.terminal? }.compact
150
+
151
+ expect(levels).to eq [[0, 'a'], [1, 'b'], [2, 'c'], [3, 'd']]
152
+ end
153
+
154
+ specify('Subexpression#flat_map expressions including self') do
155
+ root = RP.parse(/a(b(c(d)))/)
156
+
157
+ levels = root.flat_map(true) { |exp, _index| [exp.level, exp.to_s] }.compact
158
+
159
+ expect(levels).to eq [[nil, 'a(b(c(d)))'], [0, 'a'], [0, '(b(c(d)))'], [1, 'b'], [1, '(c(d))'], [2, 'c'], [2, '(d)'], [3, 'd']]
160
+ end
161
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Expression#options') do
4
+ it 'returns a hash of options/flags that affect the expression' do
5
+ exp = RP.parse(/a/ix)[0]
6
+ expect(exp).to be_a Literal
7
+ expect(exp.options).to eq(i: true, x: true)
8
+ end
9
+
10
+ it 'includes options that are locally enabled via special groups' do
11
+ exp = RP.parse(/(?x)(?m:a)/i)[1][0]
12
+ expect(exp).to be_a Literal
13
+ expect(exp.options).to eq(i: true, m: true, x: true)
14
+ end
15
+
16
+ it 'excludes locally disabled options' do
17
+ exp = RP.parse(/(?x)(?-im:a)/i)[1][0]
18
+ expect(exp).to be_a Literal
19
+ expect(exp.options).to eq(x: true)
20
+ end
21
+
22
+ it 'gives correct precedence to negative options' do
23
+ # Negative options have precedence. E.g. /(?i-i)a/ is case-sensitive.
24
+ regexp = /(?i-i:a)/
25
+ expect(regexp).to match 'a'
26
+ expect(regexp).not_to match 'A'
27
+
28
+ exp = RP.parse(regexp)[0][0]
29
+ expect(exp).to be_a Literal
30
+ expect(exp.options).to eq({})
31
+ end
32
+
33
+ it 'correctly handles multiple negative option parts' do
34
+ regexp = /(?--m--mx--) . /mx
35
+ expect(regexp).to match ' . '
36
+ expect(regexp).not_to match '.'
37
+ expect(regexp).not_to match "\n"
38
+
39
+ exp = RP.parse(regexp)[2]
40
+ expect(exp.options).to eq({})
41
+ end
42
+
43
+ it 'gives correct precedence when encountering multiple encoding flags' do
44
+ # Any encoding flag overrides all previous encoding flags. If there are
45
+ # multiple encoding flags in an options string, the last one wins.
46
+ # E.g. /(?dau)\w/ matches UTF8 chars but /(?dua)\w/ only ASCII chars.
47
+ regexp1 = /(?dau)\w/
48
+ regexp2 = /(?dua)\w/
49
+ expect(regexp1).to match 'ü'
50
+ expect(regexp2).not_to match 'ü'
51
+
52
+ exp1 = RP.parse(regexp1)[1]
53
+ exp2 = RP.parse(regexp2)[1]
54
+ expect(exp1.options).to eq(u: true)
55
+ expect(exp2.options).to eq(a: true)
56
+ end
57
+
58
+ it 'is accessible via shortcuts' do
59
+ exp = Root.build
60
+
61
+ expect { exp.options[:i] = true }
62
+ .to change { exp.i? }.from(false).to(true)
63
+ .and change { exp.ignore_case? }.from(false).to(true)
64
+ .and change { exp.case_insensitive? }.from(false).to(true)
65
+
66
+ expect { exp.options[:m] = true }
67
+ .to change { exp.m? }.from(false).to(true)
68
+ .and change { exp.multiline? }.from(false).to(true)
69
+
70
+ expect { exp.options[:x] = true }
71
+ .to change { exp.x? }.from(false).to(true)
72
+ .and change { exp.extended? }.from(false).to(true)
73
+ .and change { exp.free_spacing? }.from(false).to(true)
74
+
75
+ expect { exp.options[:a] = true }
76
+ .to change { exp.a? }.from(false).to(true)
77
+ .and change { exp.ascii_classes? }.from(false).to(true)
78
+
79
+ expect { exp.options[:d] = true }
80
+ .to change { exp.d? }.from(false).to(true)
81
+ .and change { exp.default_classes? }.from(false).to(true)
82
+
83
+ expect { exp.options[:u] = true }
84
+ .to change { exp.u? }.from(false).to(true)
85
+ .and change { exp.unicode_classes? }.from(false).to(true)
86
+ end
87
+
88
+ RSpec.shared_examples '#options' do |regexp, path, klass|
89
+ it "works for expression class #{klass}" do
90
+ exp = RP.parse(/#{regexp.source}/i).dig(*path)
91
+ expect(exp).to be_a(klass)
92
+ expect(exp).to be_i
93
+ expect(exp).not_to be_x
94
+ end
95
+ end
96
+
97
+ include_examples '#options', //, [], Root
98
+ include_examples '#options', /a/, [0], Literal
99
+ include_examples '#options', /\A/, [0], Anchor::Base
100
+ include_examples '#options', /\d/, [0], CharacterType::Base
101
+ include_examples '#options', /\n/, [0], EscapeSequence::Base
102
+ include_examples '#options', /\K/, [0], Keep::Mark
103
+ include_examples '#options', /./, [0], CharacterType::Any
104
+ include_examples '#options', /(a)/, [0], Group::Base
105
+ include_examples '#options', /(a)/, [0, 0], Literal
106
+ include_examples '#options', /(?=a)/, [0], Assertion::Base
107
+ include_examples '#options', /(?=a)/, [0, 0], Literal
108
+ include_examples '#options', /(a|b)/, [0], Group::Base
109
+ include_examples '#options', /(a|b)/, [0, 0], Alternation
110
+ include_examples '#options', /(a|b)/, [0, 0, 0], Alternative
111
+ include_examples '#options', /(a|b)/, [0, 0, 0, 0], Literal
112
+ include_examples '#options', /(a)\1/, [1], Backreference::Base
113
+ include_examples '#options', /(a)\k<1>/, [1], Backreference::Number
114
+ include_examples '#options', /(a)\g<1>/, [1], Backreference::NumberCall
115
+ include_examples '#options', /[a]/, [0], CharacterSet
116
+ include_examples '#options', /[a]/, [0, 0], Literal
117
+ include_examples '#options', /[a-z]/, [0, 0], CharacterSet::Range
118
+ include_examples '#options', /[a-z]/, [0, 0, 0], Literal
119
+ include_examples '#options', /[a&&z]/, [0, 0], CharacterSet::Intersection
120
+ include_examples '#options', /[a&&z]/, [0, 0, 0], CharacterSet::IntersectedSequence
121
+ include_examples '#options', /[a&&z]/, [0, 0, 0, 0], Literal
122
+ include_examples '#options', /[[:ascii:]]/, [0, 0], PosixClass
123
+ include_examples '#options', /\p{word}/, [0], UnicodeProperty::Base
124
+ include_examples '#options', /(a)(?(1)b|c)/, [1], Conditional::Expression
125
+ include_examples '#options', /(a)(?(1)b|c)/, [1, 0], Conditional::Condition
126
+ include_examples '#options', /(a)(?(1)b|c)/, [1, 1], Conditional::Branch
127
+ include_examples '#options', /(a)(?(1)b|c)/, [1, 1, 0], Literal
128
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::Expression::Root) do
4
+ describe('#initialize') do
5
+ it 'supports the old, nonstandard arity for backwards compatibility' do
6
+ expect { Root.new }.to output.to_stderr
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::Expression::Sequence) do
4
+ describe('#initialize') do
5
+ it 'supports the old, nonstandard arity for backwards compatibility' do
6
+ expect { Sequence.new(0, 0, 0) }.to output.to_stderr
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::Expression::Subexpression) do
4
+ specify('#ts, #te') do
5
+ regx = /abcd|ghij|klmn|pqur/
6
+ root = RP.parse(regx)
7
+
8
+ alt = root.first
9
+
10
+ { 0 => [0, 4], 1 => [5, 9], 2 => [10, 14], 3 => [15, 19] }.each do |index, span|
11
+ sequence = alt[index]
12
+
13
+ expect(sequence.ts).to eq span[0]
14
+ expect(sequence.te).to eq span[1]
15
+ end
16
+ end
17
+
18
+ specify('#nesting_level') do
19
+ root = RP.parse(/a(b(\d|[ef-g[h]]))/)
20
+
21
+ tests = {
22
+ 'a' => 1,
23
+ 'b' => 2,
24
+ '\d|[ef-g[h]]' => 3, # alternation
25
+ '\d' => 4, # first alternative
26
+ '[ef-g[h]]' => 4, # second alternative
27
+ 'e' => 5,
28
+ 'f-g' => 5,
29
+ 'f' => 6,
30
+ 'g' => 6,
31
+ 'h' => 6,
32
+ }
33
+
34
+ root.each_expression do |exp|
35
+ next unless expected_nesting_level = tests.delete(exp.to_s)
36
+ expect(expected_nesting_level).to eq exp.nesting_level
37
+ end
38
+
39
+ expect(tests).to be_empty
40
+ end
41
+
42
+ specify('#dig') do
43
+ root = RP.parse(/(((a)))/)
44
+
45
+ expect(root.dig(0).to_s).to eq '(((a)))'
46
+ expect(root.dig(0, 0, 0, 0).to_s).to eq 'a'
47
+ expect(root.dig(0, 0, 0, 0, 0)).to be_nil
48
+ expect(root.dig(3, 7)).to be_nil
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Expression#to_h') do
4
+ specify('Root#to_h') do
5
+ root = RP.parse('abc')
6
+
7
+ hash = root.to_h
8
+
9
+ expect(token: :root, type: :expression, text: 'abc', starts_at: 0, length: 3, quantifier: nil, options: {}, level: nil, set_level: nil, conditional_level: nil, expressions: [{ token: :literal, type: :literal, text: 'abc', starts_at: 0, length: 3, quantifier: nil, options: {}, level: 0, set_level: 0, conditional_level: 0 }]).to eq hash
10
+ end
11
+
12
+ specify('Quantifier#to_h') do
13
+ root = RP.parse('a{2,4}')
14
+ exp = root.expressions.at(0)
15
+
16
+ hash = exp.quantifier.to_h
17
+
18
+ expect(max: 4, min: 2, mode: :greedy, text: '{2,4}', token: :interval).to eq hash
19
+ end
20
+
21
+ specify('Conditional#to_h') do
22
+ root = RP.parse('(?<A>a)(?(<A>)b|c)', 'ruby/2.0')
23
+
24
+ expect { root.to_h }.not_to(raise_error)
25
+ end
26
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Expression#to_s') do
4
+ specify('literal alternation') do
5
+ pattern = 'abcd|ghij|klmn|pqur'
6
+
7
+ expect(RP.parse(pattern).to_s).to eq pattern
8
+ end
9
+
10
+ specify('quantified alternations') do
11
+ pattern = '(?:a?[b]+(c){2}|d+[e]*(f)?)|(?:g+[h]?(i){2,3}|j*[k]{3,5}(l)?)'
12
+
13
+ expect(RP.parse(pattern).to_s).to eq pattern
14
+ end
15
+
16
+ specify('quantified sets') do
17
+ pattern = '[abc]+|[^def]{3,6}'
18
+
19
+ expect(RP.parse(pattern).to_s).to eq pattern
20
+ end
21
+
22
+ specify('property sets') do
23
+ pattern = '[\\a\\b\\p{Lu}\\P{Z}\\c\\d]+'
24
+
25
+ expect(RP.parse(pattern, 'ruby/1.9').to_s).to eq pattern
26
+ end
27
+
28
+ specify('groups') do
29
+ pattern = "(a(?>b(?:c(?<n>d(?'N'e)??f)+g)*+h)*i)++"
30
+
31
+ expect(RP.parse(pattern, 'ruby/1.9').to_s).to eq pattern
32
+ end
33
+
34
+ specify('assertions') do
35
+ pattern = '(a+(?=b+(?!c+(?<=d+(?<!e+)?f+)?g+)?h+)?i+)?'
36
+
37
+ expect(RP.parse(pattern, 'ruby/1.9').to_s).to eq pattern
38
+ end
39
+
40
+ specify('comments') do
41
+ pattern = '(?#start)a(?#middle)b(?#end)'
42
+
43
+ expect(RP.parse(pattern).to_s).to eq pattern
44
+ end
45
+
46
+ specify('options') do
47
+ pattern = '(?mix:start)a(?-mix:middle)b(?i-mx:end)'
48
+
49
+ expect(RP.parse(pattern).to_s).to eq pattern
50
+ end
51
+
52
+ specify('url') do
53
+ pattern = ('(^$)|(^(http|https):\\/\\/[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*' + '\\.[a-z]{2,5}(([0-9]{1,5})?\\/.*)?$)')
54
+
55
+ expect(RP.parse(pattern).to_s).to eq pattern
56
+ end
57
+
58
+ specify('multiline source') do
59
+ multiline = /
60
+ \A
61
+ a? # One letter
62
+ b{2,5} # Another one
63
+ [c-g]+ # A set
64
+ \z
65
+ /x
66
+
67
+ expect(RP.parse(multiline).to_s).to eq multiline.source
68
+ end
69
+
70
+ specify('multiline #to_s') do
71
+ multiline = /
72
+ \A
73
+ a? # One letter
74
+ b{2,5} # Another one
75
+ [c-g]+ # A set
76
+ \z
77
+ /x
78
+
79
+ expect(RP.parse(multiline.to_s).to_s).to eq multiline.to_s
80
+ end
81
+
82
+ # Free spacing expressions that use spaces between quantifiers and their
83
+ # targets do not produce identical results due to the way quantifiers are
84
+ # applied to expressions (members, not nodes) and the merging of consecutive
85
+ # space nodes. This tests that they produce equivalent results.
86
+ specify('multiline equivalence') do
87
+ multiline = /
88
+ \A
89
+ a ? # One letter
90
+ b {2,5} # Another one
91
+ [c-g] + # A set
92
+ \z
93
+ /x
94
+
95
+ str = 'bbbcged'
96
+ root = RP.parse(multiline)
97
+
98
+ expect(Regexp.new(root.to_s, Regexp::EXTENDED).match(str)[0]).to eq multiline.match(str)[0]
99
+ end
100
+ end