regexp_parser 1.3.0 → 1.6.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -1
  3. data/Gemfile +3 -3
  4. data/README.md +10 -14
  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/quantifier.rb +2 -2
  18. data/lib/regexp_parser/expression/sequence.rb +3 -6
  19. data/lib/regexp_parser/expression/sequence_operation.rb +2 -6
  20. data/lib/regexp_parser/expression/subexpression.rb +3 -5
  21. data/lib/regexp_parser/lexer.rb +30 -44
  22. data/lib/regexp_parser/parser.rb +47 -24
  23. data/lib/regexp_parser/scanner.rb +1159 -1329
  24. data/lib/regexp_parser/scanner/char_type.rl +0 -3
  25. data/lib/regexp_parser/scanner/properties/long.yml +34 -1
  26. data/lib/regexp_parser/scanner/properties/short.yml +12 -0
  27. data/lib/regexp_parser/scanner/scanner.rl +82 -190
  28. data/lib/regexp_parser/syntax/tokens.rb +2 -10
  29. data/lib/regexp_parser/syntax/tokens/unicode_property.rb +72 -21
  30. data/lib/regexp_parser/syntax/versions/2.6.0.rb +10 -0
  31. data/lib/regexp_parser/syntax/versions/2.6.2.rb +10 -0
  32. data/lib/regexp_parser/syntax/versions/2.6.3.rb +10 -0
  33. data/lib/regexp_parser/version.rb +1 -1
  34. data/regexp_parser.gemspec +3 -3
  35. data/spec/expression/base_spec.rb +94 -0
  36. data/spec/expression/clone_spec.rb +120 -0
  37. data/spec/expression/conditional_spec.rb +89 -0
  38. data/spec/expression/free_space_spec.rb +27 -0
  39. data/spec/expression/methods/match_length_spec.rb +154 -0
  40. data/spec/expression/methods/match_spec.rb +25 -0
  41. data/spec/expression/methods/strfregexp_spec.rb +224 -0
  42. data/spec/expression/methods/tests_spec.rb +99 -0
  43. data/spec/expression/methods/traverse_spec.rb +140 -0
  44. data/spec/expression/options_spec.rb +128 -0
  45. data/spec/expression/root_spec.rb +9 -0
  46. data/spec/expression/sequence_spec.rb +9 -0
  47. data/spec/expression/subexpression_spec.rb +50 -0
  48. data/spec/expression/to_h_spec.rb +26 -0
  49. data/spec/expression/to_s_spec.rb +100 -0
  50. data/spec/lexer/all_spec.rb +22 -0
  51. data/spec/lexer/conditionals_spec.rb +53 -0
  52. data/spec/lexer/escapes_spec.rb +14 -0
  53. data/spec/lexer/keep_spec.rb +10 -0
  54. data/spec/lexer/literals_spec.rb +89 -0
  55. data/spec/lexer/nesting_spec.rb +99 -0
  56. data/spec/lexer/refcalls_spec.rb +55 -0
  57. data/spec/parser/all_spec.rb +43 -0
  58. data/spec/parser/alternation_spec.rb +88 -0
  59. data/spec/parser/anchors_spec.rb +17 -0
  60. data/spec/parser/conditionals_spec.rb +179 -0
  61. data/spec/parser/errors_spec.rb +30 -0
  62. data/spec/parser/escapes_spec.rb +121 -0
  63. data/spec/parser/free_space_spec.rb +130 -0
  64. data/spec/parser/groups_spec.rb +108 -0
  65. data/spec/parser/keep_spec.rb +6 -0
  66. data/spec/parser/posix_classes_spec.rb +8 -0
  67. data/spec/parser/properties_spec.rb +115 -0
  68. data/spec/parser/quantifiers_spec.rb +51 -0
  69. data/spec/parser/refcalls_spec.rb +112 -0
  70. data/spec/parser/set/intersections_spec.rb +127 -0
  71. data/spec/parser/set/ranges_spec.rb +111 -0
  72. data/spec/parser/sets_spec.rb +178 -0
  73. data/spec/parser/types_spec.rb +18 -0
  74. data/spec/scanner/all_spec.rb +18 -0
  75. data/spec/scanner/anchors_spec.rb +21 -0
  76. data/spec/scanner/conditionals_spec.rb +128 -0
  77. data/spec/scanner/errors_spec.rb +68 -0
  78. data/spec/scanner/escapes_spec.rb +53 -0
  79. data/spec/scanner/free_space_spec.rb +133 -0
  80. data/spec/scanner/groups_spec.rb +52 -0
  81. data/spec/scanner/keep_spec.rb +10 -0
  82. data/spec/scanner/literals_spec.rb +49 -0
  83. data/spec/scanner/meta_spec.rb +18 -0
  84. data/spec/scanner/properties_spec.rb +64 -0
  85. data/spec/scanner/quantifiers_spec.rb +20 -0
  86. data/spec/scanner/refcalls_spec.rb +36 -0
  87. data/spec/scanner/sets_spec.rb +102 -0
  88. data/spec/scanner/types_spec.rb +14 -0
  89. data/spec/spec_helper.rb +15 -0
  90. data/{test → spec}/support/runner.rb +9 -8
  91. data/spec/support/shared_examples.rb +77 -0
  92. data/{test → spec}/support/warning_extractor.rb +5 -7
  93. data/spec/syntax/syntax_spec.rb +48 -0
  94. data/spec/syntax/syntax_token_map_spec.rb +23 -0
  95. data/spec/syntax/versions/1.8.6_spec.rb +17 -0
  96. data/spec/syntax/versions/1.9.1_spec.rb +10 -0
  97. data/spec/syntax/versions/1.9.3_spec.rb +9 -0
  98. data/spec/syntax/versions/2.0.0_spec.rb +13 -0
  99. data/spec/syntax/versions/2.2.0_spec.rb +9 -0
  100. data/spec/syntax/versions/aliases_spec.rb +37 -0
  101. data/spec/token/token_spec.rb +85 -0
  102. metadata +144 -143
  103. data/test/expression/test_all.rb +0 -12
  104. data/test/expression/test_base.rb +0 -90
  105. data/test/expression/test_clone.rb +0 -89
  106. data/test/expression/test_conditionals.rb +0 -113
  107. data/test/expression/test_free_space.rb +0 -35
  108. data/test/expression/test_set.rb +0 -84
  109. data/test/expression/test_strfregexp.rb +0 -230
  110. data/test/expression/test_subexpression.rb +0 -58
  111. data/test/expression/test_tests.rb +0 -99
  112. data/test/expression/test_to_h.rb +0 -59
  113. data/test/expression/test_to_s.rb +0 -104
  114. data/test/expression/test_traverse.rb +0 -161
  115. data/test/helpers.rb +0 -10
  116. data/test/lexer/test_all.rb +0 -41
  117. data/test/lexer/test_conditionals.rb +0 -127
  118. data/test/lexer/test_keep.rb +0 -24
  119. data/test/lexer/test_literals.rb +0 -130
  120. data/test/lexer/test_nesting.rb +0 -132
  121. data/test/lexer/test_refcalls.rb +0 -56
  122. data/test/parser/set/test_intersections.rb +0 -127
  123. data/test/parser/set/test_ranges.rb +0 -111
  124. data/test/parser/test_all.rb +0 -64
  125. data/test/parser/test_alternation.rb +0 -92
  126. data/test/parser/test_anchors.rb +0 -34
  127. data/test/parser/test_conditionals.rb +0 -187
  128. data/test/parser/test_errors.rb +0 -63
  129. data/test/parser/test_escapes.rb +0 -134
  130. data/test/parser/test_free_space.rb +0 -139
  131. data/test/parser/test_groups.rb +0 -289
  132. data/test/parser/test_keep.rb +0 -21
  133. data/test/parser/test_posix_classes.rb +0 -27
  134. data/test/parser/test_properties.rb +0 -133
  135. data/test/parser/test_quantifiers.rb +0 -301
  136. data/test/parser/test_refcalls.rb +0 -186
  137. data/test/parser/test_sets.rb +0 -179
  138. data/test/parser/test_types.rb +0 -50
  139. data/test/scanner/test_all.rb +0 -38
  140. data/test/scanner/test_anchors.rb +0 -38
  141. data/test/scanner/test_conditionals.rb +0 -184
  142. data/test/scanner/test_errors.rb +0 -91
  143. data/test/scanner/test_escapes.rb +0 -56
  144. data/test/scanner/test_free_space.rb +0 -200
  145. data/test/scanner/test_groups.rb +0 -79
  146. data/test/scanner/test_keep.rb +0 -35
  147. data/test/scanner/test_literals.rb +0 -89
  148. data/test/scanner/test_meta.rb +0 -40
  149. data/test/scanner/test_properties.rb +0 -312
  150. data/test/scanner/test_quantifiers.rb +0 -37
  151. data/test/scanner/test_refcalls.rb +0 -52
  152. data/test/scanner/test_scripts.rb +0 -53
  153. data/test/scanner/test_sets.rb +0 -119
  154. data/test/scanner/test_types.rb +0 -35
  155. data/test/scanner/test_unicode_blocks.rb +0 -30
  156. data/test/support/disable_autotest.rb +0 -8
  157. data/test/syntax/test_all.rb +0 -6
  158. data/test/syntax/test_syntax.rb +0 -61
  159. data/test/syntax/test_syntax_token_map.rb +0 -25
  160. data/test/syntax/versions/test_1.8.rb +0 -55
  161. data/test/syntax/versions/test_1.9.1.rb +0 -36
  162. data/test/syntax/versions/test_1.9.3.rb +0 -32
  163. data/test/syntax/versions/test_2.0.0.rb +0 -37
  164. data/test/syntax/versions/test_2.2.0.rb +0 -32
  165. data/test/syntax/versions/test_aliases.rb +0 -129
  166. data/test/syntax/versions/test_all.rb +0 -5
  167. data/test/test_all.rb +0 -5
  168. data/test/token/test_all.rb +0 -2
  169. data/test/token/test_token.rb +0 -107
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::Expression::FreeSpace) do
4
+ specify('white space quantify raises error') do
5
+ regexp = /
6
+ a # Comment
7
+ /x
8
+
9
+ root = RP.parse(regexp)
10
+ space = root[0]
11
+
12
+ expect(space).to be_instance_of(FreeSpace::WhiteSpace)
13
+ expect { space.quantify(:dummy, '#') }.to raise_error(RuntimeError)
14
+ end
15
+
16
+ specify('comment quantify raises error') do
17
+ regexp = /
18
+ a # Comment
19
+ /x
20
+
21
+ root = RP.parse(regexp)
22
+ comment = root[3]
23
+
24
+ expect(comment).to be_instance_of(FreeSpace::Comment)
25
+ expect { comment.quantify(:dummy, '#') }.to raise_error(RuntimeError)
26
+ end
27
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::MatchLength) do
4
+ ML = described_class
5
+
6
+ specify('literal') { expect(ML.of(/a/).minmax).to eq [1, 1] }
7
+ specify('literal sequence') { expect(ML.of(/abc/).minmax).to eq [3, 3] }
8
+ specify('dot') { expect(ML.of(/./).minmax).to eq [1, 1] }
9
+ specify('set') { expect(ML.of(/[abc]/).minmax).to eq [1, 1] }
10
+ specify('type') { expect(ML.of(/\d/).minmax).to eq [1, 1] }
11
+ specify('escape') { expect(ML.of(/\n/).minmax).to eq [1, 1] }
12
+ specify('property') { expect(ML.of(/\p{ascii}/).minmax).to eq [1, 1] }
13
+ specify('codepoint list') { expect(ML.of(/\u{61 62 63}/).minmax).to eq [3, 3] }
14
+ specify('multi-char literal') { expect(ML.of(/abc/).minmax).to eq [3, 3] }
15
+ specify('fixed quantified') { expect(ML.of(/a{5}/).minmax).to eq [5, 5] }
16
+ specify('range quantified') { expect(ML.of(/a{5,9}/).minmax).to eq [5, 9] }
17
+ specify('nested quantified') { expect(ML.of(/(a{2}){3,4}/).minmax).to eq [6, 8] }
18
+ specify('open-end quantified') { expect(ML.of(/a*/).minmax).to eq [0, Float::INFINITY] }
19
+ specify('empty subexpression') { expect(ML.of(//).minmax).to eq [0, 0] }
20
+ specify('anchor') { expect(ML.of(/^$/).minmax).to eq [0, 0] }
21
+ specify('lookaround') { expect(ML.of(/(?=abc)/).minmax).to eq [0, 0] }
22
+ specify('free space') { expect(ML.of(/ /x).minmax).to eq [0, 0] }
23
+ specify('comment') { expect(ML.of(/(?#comment)/x).minmax).to eq [0, 0] }
24
+ specify('backreference') { expect(ML.of(/(abc){2}\1/).minmax).to eq [9, 9] }
25
+ specify('subexp call') { expect(ML.of(/(abc){2}\g<-1>/).minmax).to eq [9, 9] }
26
+ specify('alternation') { expect(ML.of(/a|bcde/).minmax).to eq [1, 4] }
27
+ specify('nested alternation') { expect(ML.of(/a|bc(d|efg)/).minmax).to eq [1, 5] }
28
+ specify('quantified alternation') { expect(ML.of(/a|bcde?/).minmax).to eq [1, 4] }
29
+ if ruby_version_at_least('2.4.1')
30
+ specify('absence group') { expect(ML.of('(?~abc)').minmax).to eq [0, Float::INFINITY] }
31
+ end
32
+
33
+ specify('raises for missing references') do
34
+ exp = RP.parse(/(a)\1/).last
35
+ exp.referenced_expression = nil
36
+ expect { exp.match_length }.to raise_error(ArgumentError)
37
+ end
38
+
39
+ describe('::of') do
40
+ it('works with Regexps') { expect(ML.of(/foo/).minmax).to eq [3, 3] }
41
+ it('works with Strings') { expect(ML.of('foo').minmax).to eq [3, 3] }
42
+ it('works with Expressions') { expect(ML.of(RP.parse(/foo/)).minmax).to eq [3, 3] }
43
+ end
44
+
45
+ describe('Expression#match_length') do
46
+ it('returns the MatchLength') { expect(RP.parse(/abc/).match_length.minmax).to eq [3, 3] }
47
+ end
48
+
49
+ describe('Expression#inner_match_length') do
50
+ it 'returns the MatchLength of an expression that does not count towards parent match_length' do
51
+ exp = RP.parse(/(?=ab|cdef)/)[0]
52
+ expect(exp).to be_a Regexp::Expression::Assertion::Base
53
+ expect(exp.match_length.minmax).to eq [0, 0]
54
+ expect(exp.inner_match_length.minmax).to eq [2, 4]
55
+ end
56
+ end
57
+
58
+ describe('#include?') do
59
+ specify('unquantified') do
60
+ expect(ML.of(/a/)).to include 1
61
+ expect(ML.of(/a/)).not_to include 0
62
+ expect(ML.of(/a/)).not_to include 2
63
+ end
64
+
65
+ specify('fixed quantified') do
66
+ expect(ML.of(/a{5}/)).to include 5
67
+ expect(ML.of(/a{5}/)).not_to include 0
68
+ expect(ML.of(/a{5}/)).not_to include 4
69
+ expect(ML.of(/a{5}/)).not_to include 6
70
+ end
71
+
72
+ specify('variably quantified') do
73
+ expect(ML.of(/a?/)).to include 0
74
+ expect(ML.of(/a?/)).to include 1
75
+ expect(ML.of(/a?/)).not_to include 2
76
+ end
77
+
78
+ specify('nested quantified') do
79
+ expect(ML.of(/(a{2}){3,4}/)).to include 6
80
+ expect(ML.of(/(a{2}){3,4}/)).to include 8
81
+ expect(ML.of(/(a{2}){3,4}/)).not_to include 0
82
+ expect(ML.of(/(a{2}){3,4}/)).not_to include 5
83
+ expect(ML.of(/(a{2}){3,4}/)).not_to include 7
84
+ expect(ML.of(/(a{2}){3,4}/)).not_to include 9
85
+ end
86
+
87
+ specify('branches') do
88
+ expect(ML.of(/ab|cdef/)).to include 2
89
+ expect(ML.of(/ab|cdef/)).to include 4
90
+ expect(ML.of(/ab|cdef/)).not_to include 0
91
+ expect(ML.of(/ab|cdef/)).not_to include 3
92
+ expect(ML.of(/ab|cdef/)).not_to include 5
93
+ end
94
+
95
+ specify('called on leaf node') do
96
+ expect(ML.of(RP.parse(/a{2}/)[0])).to include 2
97
+ expect(ML.of(RP.parse(/a{2}/)[0])).not_to include 0
98
+ expect(ML.of(RP.parse(/a{2}/)[0])).not_to include 1
99
+ expect(ML.of(RP.parse(/a{2}/)[0])).not_to include 3
100
+ end
101
+ end
102
+
103
+ describe('#fixed?') do
104
+ specify('unquantified') { expect(ML.of(/a/)).to be_fixed }
105
+ specify('fixed quantified') { expect(ML.of(/a{5}/)).to be_fixed }
106
+ specify('variably quantified') { expect(ML.of(/a?/)).not_to be_fixed }
107
+ specify('equal branches') { expect(ML.of(/ab|cd/)).to be_fixed }
108
+ specify('unequal branches') { expect(ML.of(/ab|cdef/)).not_to be_fixed }
109
+ specify('equal quantified branches') { expect(ML.of(/a{2}|cd/)).to be_fixed }
110
+ specify('unequal quantified branches') { expect(ML.of(/a{3}|cd/)).not_to be_fixed }
111
+ specify('empty') { expect(ML.of(//)).to be_fixed }
112
+ end
113
+
114
+ describe('#each') do
115
+ it 'returns an Enumerator if called without a block' do
116
+ result = ML.of(/a?/).each
117
+ expect(result).to be_a(Enumerator)
118
+ expect(result.next).to eq 0
119
+ expect(result.next).to eq 1
120
+ expect { result.next }.to raise_error(StopIteration)
121
+ end
122
+
123
+ it 'is limited to 1000 iterations in case there are infinite match lengths' do
124
+ expect(ML.of(/a*/).first(3000).size).to eq 1000
125
+ end
126
+
127
+ it 'scaffolds the Enumerable interface' do
128
+ expect(ML.of(/abc|defg/).count).to eq 2
129
+ expect(ML.of(/(ab)*/).first(5)).to eq [0, 2, 4, 6, 8]
130
+ expect(ML.of(/a{,10}/).any? { |len| len > 20 }).to be false
131
+ end
132
+ end
133
+
134
+ describe('#endless_each') do
135
+ it 'returns an Enumerator if called without a block' do
136
+ result = ML.of(/a?/).endless_each
137
+ expect(result).to be_a(Enumerator)
138
+ expect(result.next).to eq 0
139
+ expect(result.next).to eq 1
140
+ expect { result.next }.to raise_error(StopIteration)
141
+ end
142
+
143
+ it 'never stops iterating for infinite match lengths' do
144
+ expect(ML.of(/a*/).endless_each.first(3000).size).to eq 3000
145
+ end
146
+ end
147
+
148
+ describe('#inspect') do
149
+ it 'is nice' do
150
+ result = RP.parse(/a{2,4}/)[0].match_length
151
+ expect(result.inspect).to eq '#<Regexp::MatchLength<Literal> min=2 max=4>'
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Expression#match') do
4
+ it 'returns the #match result of the respective Regexp' do
5
+ expect(RP.parse(/a/).match('a')[0]).to eq 'a'
6
+ end
7
+
8
+ it 'can be given an offset, just like Regexp#match' do
9
+ expect(RP.parse(/./).match('ab', 1)[0]).to eq 'b'
10
+ end
11
+
12
+ it 'works with the #=~ alias' do
13
+ expect(RP.parse(/a/) =~ 'a').to be_a MatchData
14
+ end
15
+ end
16
+
17
+ RSpec.describe('Expression#match?') do
18
+ it 'returns true if the Respective Regexp matches' do
19
+ expect(RP.parse(/a/).match?('a')).to be true
20
+ end
21
+
22
+ it 'returns false if the Respective Regexp does not match' do
23
+ expect(RP.parse(/a/).match?('b')).to be false
24
+ end
25
+ end
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Expression#strfregexp') do
4
+ specify('#strfre alias') do
5
+ expect(RP.parse(/a/)).to respond_to(:strfre)
6
+ end
7
+
8
+ specify('#strfregexp level') do
9
+ root = RP.parse(/a(b(c))/)
10
+
11
+ expect(root.strfregexp('%l')).to eq 'root'
12
+
13
+ a = root.first
14
+ expect(a.strfregexp('%%l')).to eq '%0'
15
+
16
+ b = root[1].first
17
+ expect(b.strfregexp('<%l>')).to eq '<1>'
18
+
19
+ c = root[1][1].first
20
+ expect(c.strfregexp('[at: %l]')).to eq '[at: 2]'
21
+ end
22
+
23
+ specify('#strfregexp start end') do
24
+ root = RP.parse(/a(b(c))/)
25
+
26
+ expect(root.strfregexp('%s')).to eq '0'
27
+ expect(root.strfregexp('%e')).to eq '7'
28
+
29
+ a = root.first
30
+ expect(a.strfregexp('%%s')).to eq '%0'
31
+ expect(a.strfregexp('%e')).to eq '1'
32
+
33
+ group_1 = root[1]
34
+ expect(group_1.strfregexp('GRP:%s')).to eq 'GRP:1'
35
+ expect(group_1.strfregexp('%e')).to eq '7'
36
+
37
+ b = group_1.first
38
+ expect(b.strfregexp('<@%s>')).to eq '<@2>'
39
+ expect(b.strfregexp('%e')).to eq '3'
40
+
41
+ c = group_1.last.first
42
+ expect(c.strfregexp('[at: %s]')).to eq '[at: 4]'
43
+ expect(c.strfregexp('%e')).to eq '5'
44
+ end
45
+
46
+ specify('#strfregexp length') do
47
+ root = RP.parse(/a[b]c/)
48
+
49
+ expect(root.strfregexp('%S')).to eq '5'
50
+
51
+ a = root.first
52
+ expect(a.strfregexp('%S')).to eq '1'
53
+
54
+ set = root[1]
55
+ expect(set.strfregexp('%S')).to eq '3'
56
+ end
57
+
58
+ specify('#strfregexp coded offset') do
59
+ root = RP.parse(/a[b]c/)
60
+
61
+ expect(root.strfregexp('%o')).to eq '@0+5'
62
+
63
+ a = root.first
64
+ expect(a.strfregexp('%o')).to eq '@0+1'
65
+
66
+ set = root[1]
67
+ expect(set.strfregexp('%o')).to eq '@1+3'
68
+ end
69
+
70
+ specify('#strfregexp type token') do
71
+ root = RP.parse(/a[b](c)/)
72
+
73
+ expect(root.strfregexp('%y')).to eq 'expression'
74
+ expect(root.strfregexp('%k')).to eq 'root'
75
+ expect(root.strfregexp('%i')).to eq 'expression:root'
76
+ expect(root.strfregexp('%c')).to eq 'Regexp::Expression::Root'
77
+
78
+ a = root.first
79
+ expect(a.strfregexp('%y')).to eq 'literal'
80
+ expect(a.strfregexp('%k')).to eq 'literal'
81
+ expect(a.strfregexp('%i')).to eq 'literal:literal'
82
+ expect(a.strfregexp('%c')).to eq 'Regexp::Expression::Literal'
83
+
84
+ set = root[1]
85
+ expect(set.strfregexp('%y')).to eq 'set'
86
+ expect(set.strfregexp('%k')).to eq 'character'
87
+ expect(set.strfregexp('%i')).to eq 'set:character'
88
+ expect(set.strfregexp('%c')).to eq 'Regexp::Expression::CharacterSet'
89
+
90
+ group = root.last
91
+ expect(group.strfregexp('%y')).to eq 'group'
92
+ expect(group.strfregexp('%k')).to eq 'capture'
93
+ expect(group.strfregexp('%i')).to eq 'group:capture'
94
+ expect(group.strfregexp('%c')).to eq 'Regexp::Expression::Group::Capture'
95
+ end
96
+
97
+ specify('#strfregexp quantifier') do
98
+ root = RP.parse(/a+[b](c)?d{3,4}/)
99
+
100
+ expect(root.strfregexp('%q')).to eq '{1}'
101
+ expect(root.strfregexp('%Q')).to eq ''
102
+ expect(root.strfregexp('%z, %Z')).to eq '1, 1'
103
+
104
+ a = root.first
105
+ expect(a.strfregexp('%q')).to eq '{1, or-more}'
106
+ expect(a.strfregexp('%Q')).to eq '+'
107
+ expect(a.strfregexp('%z, %Z')).to eq '1, -1'
108
+
109
+ set = root[1]
110
+ expect(set.strfregexp('%q')).to eq '{1}'
111
+ expect(set.strfregexp('%Q')).to eq ''
112
+ expect(set.strfregexp('%z, %Z')).to eq '1, 1'
113
+
114
+ group = root[2]
115
+ expect(group.strfregexp('%q')).to eq '{0, 1}'
116
+ expect(group.strfregexp('%Q')).to eq '?'
117
+ expect(group.strfregexp('%z, %Z')).to eq '0, 1'
118
+
119
+ d = root.last
120
+ expect(d.strfregexp('%q')).to eq '{3, 4}'
121
+ expect(d.strfregexp('%Q')).to eq '{3,4}'
122
+ expect(d.strfregexp('%z, %Z')).to eq '3, 4'
123
+ end
124
+
125
+ specify('#strfregexp text') do
126
+ root = RP.parse(/a(b(c))|[d-gk-p]+/)
127
+
128
+ expect(root.strfregexp('%t')).to eq 'a(b(c))|[d-gk-p]+'
129
+ expect(root.strfregexp('%~t')).to eq 'expression:root'
130
+
131
+ alt = root.first
132
+ expect(alt.strfregexp('%t')).to eq 'a(b(c))|[d-gk-p]+'
133
+ expect(alt.strfregexp('%T')).to eq 'a(b(c))|[d-gk-p]+'
134
+ expect(alt.strfregexp('%~t')).to eq 'meta:alternation'
135
+
136
+ seq_1 = alt.first
137
+ expect(seq_1.strfregexp('%t')).to eq 'a(b(c))'
138
+ expect(seq_1.strfregexp('%T')).to eq 'a(b(c))'
139
+ expect(seq_1.strfregexp('%~t')).to eq 'expression:sequence'
140
+
141
+ group = seq_1[1]
142
+ expect(group.strfregexp('%t')).to eq '(b(c))'
143
+ expect(group.strfregexp('%T')).to eq '(b(c))'
144
+ expect(group.strfregexp('%~t')).to eq 'group:capture'
145
+
146
+ seq_2 = alt.last
147
+ expect(seq_2.strfregexp('%t')).to eq '[d-gk-p]+'
148
+ expect(seq_2.strfregexp('%T')).to eq '[d-gk-p]+'
149
+
150
+ set = seq_2.first
151
+ expect(set.strfregexp('%t')).to eq '[d-gk-p]'
152
+ expect(set.strfregexp('%T')).to eq '[d-gk-p]+'
153
+ expect(set.strfregexp('%~t')).to eq 'set:character'
154
+ end
155
+
156
+ specify('#strfregexp combined') do
157
+ root = RP.parse(/a{5}|[b-d]+/)
158
+
159
+ expect(root.strfregexp('%b')).to eq '@0+11 expression:root'
160
+ expect(root.strfregexp('%b')).to eq root.strfregexp('%o %i')
161
+
162
+ expect(root.strfregexp('%m')).to eq '@0+11 expression:root {1}'
163
+ expect(root.strfregexp('%m')).to eq root.strfregexp('%b %q')
164
+
165
+ expect(root.strfregexp('%a')).to eq '@0+11 expression:root {1} a{5}|[b-d]+'
166
+ expect(root.strfregexp('%a')).to eq root.strfregexp('%m %t')
167
+ end
168
+
169
+ specify('#strfregexp conditional') do
170
+ root = RP.parse('(?<A>a)(?(<A>)b|c)', 'ruby/2.0')
171
+
172
+ expect { root.strfregexp }.not_to(raise_error)
173
+ end
174
+
175
+ specify('#strfregexp_tree') do
176
+ root = RP.parse(/a[b-d]*(e(f+))?/)
177
+
178
+ expect(root.strfregexp_tree('%>%o %~t')).to eq(
179
+ "@0+15 expression:root\n" +
180
+ " @0+1 a\n" +
181
+ " @1+6 set:character\n" +
182
+ " @2+3 set:range\n" +
183
+ " @2+1 b\n" +
184
+ " @4+1 d\n" +
185
+ " @7+8 group:capture\n" +
186
+ " @8+1 e\n" +
187
+ " @9+4 group:capture\n" +
188
+ " @10+2 f+"
189
+ )
190
+ end
191
+
192
+ specify('#strfregexp_tree separator') do
193
+ root = RP.parse(/a[b-d]*(e(f+))?/)
194
+
195
+ expect(root.strfregexp_tree('%>%o %~t', true, '-SEP-')).to eq(
196
+ "@0+15 expression:root-SEP-" +
197
+ " @0+1 a-SEP-" +
198
+ " @1+6 set:character-SEP-" +
199
+ " @2+3 set:range-SEP-" +
200
+ " @2+1 b-SEP-" +
201
+ " @4+1 d-SEP-" +
202
+ " @7+8 group:capture-SEP-" +
203
+ " @8+1 e-SEP-" +
204
+ " @9+4 group:capture-SEP-" +
205
+ " @10+2 f+"
206
+ )
207
+ end
208
+
209
+ specify('#strfregexp_tree excluding self') do
210
+ root = RP.parse(/a[b-d]*(e(f+))?/)
211
+
212
+ expect(root.strfregexp_tree('%>%o %~t', false)).to eq(
213
+ "@0+1 a\n" +
214
+ "@1+6 set:character\n" +
215
+ " @2+3 set:range\n" +
216
+ " @2+1 b\n" +
217
+ " @4+1 d\n" +
218
+ "@7+8 group:capture\n" +
219
+ " @8+1 e\n" +
220
+ " @9+4 group:capture\n" +
221
+ " @10+2 f+"
222
+ )
223
+ end
224
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('ExpressionTests') do
4
+ specify('#type?') do
5
+ root = RP.parse(/abcd|(ghij)|[klmn]/)
6
+
7
+ alt = root.first
8
+
9
+ expect(alt.type?(:meta)).to be true
10
+ expect(alt.type?(:escape)).to be false
11
+ expect(alt.type?(%i[meta escape])).to be true
12
+ expect(alt.type?(%i[literal escape])).to be false
13
+ expect(alt.type?(:*)).to be true
14
+ expect(alt.type?([:*])).to be true
15
+ expect(alt.type?(%i[literal escape *])).to be true
16
+
17
+ seq_1 = alt[0]
18
+ expect(seq_1.type?(:expression)).to be true
19
+ expect(seq_1.first.type?(:literal)).to be true
20
+
21
+ seq_2 = alt[1]
22
+ expect(seq_2.type?(:*)).to be true
23
+ expect(seq_2.first.type?(:group)).to be true
24
+
25
+ seq_3 = alt[2]
26
+ expect(seq_3.first.type?(:set)).to be true
27
+ end
28
+
29
+ specify('#is?') do
30
+ root = RP.parse(/.+|\.?/)
31
+
32
+ expect(root.is?(:*)).to be true
33
+
34
+ alt = root.first
35
+ expect(alt.is?(:*)).to be true
36
+ expect(alt.is?(:alternation)).to be true
37
+ expect(alt.is?(:alternation, :meta)).to be true
38
+
39
+ seq_1 = alt[0]
40
+ expect(seq_1.is?(:sequence)).to be true
41
+ expect(seq_1.is?(:sequence, :expression)).to be true
42
+
43
+ expect(seq_1.first.is?(:dot)).to be true
44
+ expect(seq_1.first.is?(:dot, :escape)).to be false
45
+ expect(seq_1.first.is?(:dot, :meta)).to be true
46
+ expect(seq_1.first.is?(:dot, %i[escape meta])).to be true
47
+
48
+ seq_2 = alt[1]
49
+ expect(seq_2.first.is?(:dot)).to be true
50
+ expect(seq_2.first.is?(:dot, :escape)).to be true
51
+ expect(seq_2.first.is?(:dot, :meta)).to be false
52
+ expect(seq_2.first.is?(:dot, %i[meta escape])).to be true
53
+ end
54
+
55
+ specify('#one_of?') do
56
+ root = RP.parse(/\Aab(c[\w])d|e.\z/)
57
+
58
+ expect(root.one_of?(:*)).to be true
59
+ expect(root.one_of?(:* => :*)).to be true
60
+ expect(root.one_of?(:* => [:*])).to be true
61
+
62
+ alt = root.first
63
+ expect(alt.one_of?(:*)).to be true
64
+ expect(alt.one_of?(:meta)).to be true
65
+ expect(alt.one_of?(:meta, :alternation)).to be true
66
+ expect(alt.one_of?(meta: %i[dot bogus])).to be false
67
+ expect(alt.one_of?(meta: %i[dot alternation])).to be true
68
+
69
+ seq_1 = alt[0]
70
+ expect(seq_1.one_of?(:expression)).to be true
71
+ expect(seq_1.one_of?(expression: :sequence)).to be true
72
+
73
+ expect(seq_1.first.one_of?(:anchor)).to be true
74
+ expect(seq_1.first.one_of?(anchor: :bos)).to be true
75
+ expect(seq_1.first.one_of?(anchor: :eos)).to be false
76
+ expect(seq_1.first.one_of?(anchor: %i[escape meta bos])).to be true
77
+ expect(seq_1.first.one_of?(anchor: %i[escape meta eos])).to be false
78
+
79
+ seq_2 = alt[1]
80
+ expect(seq_2.first.one_of?(:literal)).to be true
81
+
82
+ expect(seq_2[1].one_of?(:meta)).to be true
83
+ expect(seq_2[1].one_of?(meta: :dot)).to be true
84
+ expect(seq_2[1].one_of?(meta: :alternation)).to be false
85
+ expect(seq_2[1].one_of?(meta: [:dot])).to be true
86
+
87
+ expect(seq_2.last.one_of?(:group)).to be false
88
+ expect(seq_2.last.one_of?(group: [:*])).to be false
89
+ expect(seq_2.last.one_of?(group: [:*], meta: :*)).to be false
90
+
91
+ expect(seq_2.last.one_of?(:meta => [:*], :* => :*)).to be true
92
+ expect(seq_2.last.one_of?(meta: [:*], anchor: :*)).to be true
93
+ expect(seq_2.last.one_of?(meta: [:*], anchor: :eos)).to be true
94
+ expect(seq_2.last.one_of?(meta: [:*], anchor: [:bos])).to be false
95
+ expect(seq_2.last.one_of?(meta: [:*], anchor: %i[bos eos])).to be true
96
+
97
+ expect { root.one_of?(Object.new) }.to raise_error(ArgumentError)
98
+ end
99
+ end