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,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Keep lexing') do
4
+ specify('lex keep token') do
5
+ regexp = /ab\Kcd/
6
+ tokens = RL.lex(regexp)
7
+
8
+ expect(tokens[1].type).to eq :keep
9
+ expect(tokens[1].token).to eq :mark
10
+ end
11
+
12
+ specify('lex keep nested') do
13
+ regexp = /(a\Kb)|(c\\\Kd)ef/
14
+ tokens = RL.lex(regexp)
15
+
16
+ expect(tokens[2].type).to eq :keep
17
+ expect(tokens[2].token).to eq :mark
18
+
19
+ expect(tokens[9].type).to eq :keep
20
+ expect(tokens[9].token).to eq :mark
21
+ end
22
+ end
@@ -1,9 +1,6 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- require File.expand_path("../../helpers", __FILE__)
4
-
5
- class LexerLiterals < Test::Unit::TestCase
1
+ require 'spec_helper'
6
2
 
3
+ RSpec.describe('Literal lexing') do
7
4
  tests = {
8
5
  # ascii, single byte characters
9
6
  'a' => {
@@ -91,40 +88,39 @@ class LexerLiterals < Test::Unit::TestCase
91
88
  }
92
89
 
93
90
  tests.each_with_index do |(pattern, checks), count|
94
- define_method "test_lex_literal_runs_#{count}" do
91
+ specify("lex_literal_runs_#{count}") do
95
92
  tokens = RL.lex(pattern)
96
93
 
97
94
  checks.each do |index, (type, token, text, ts, te, level, set_level, conditional_level)|
98
95
  struct = tokens.at(index)
99
96
 
100
- assert_equal type, struct.type
101
- assert_equal token, struct.token
102
- assert_equal text, struct.text
103
- assert_equal ts, struct.ts
104
- assert_equal te, struct.te
105
- assert_equal level, struct.level
106
- assert_equal set_level, struct.set_level
107
- assert_equal conditional_level, struct.conditional_level
97
+ expect(struct.type).to eq type
98
+ expect(struct.token).to eq token
99
+ expect(struct.text).to eq text
100
+ expect(struct.ts).to eq ts
101
+ expect(struct.te).to eq te
102
+ expect(struct.level).to eq level
103
+ expect(struct.set_level).to eq set_level
104
+ expect(struct.conditional_level).to eq conditional_level
108
105
  end
109
106
  end
110
107
  end
111
108
 
112
- def test_lex_single_2_byte_char
113
- tokens = RL.lex('ا+')
109
+ specify('lex single 2 byte char') do
110
+ tokens = RL.lex("\u0627+")
114
111
 
115
- assert_equal 2, tokens.length
112
+ expect(tokens.length).to eq 2
116
113
  end
117
114
 
118
- def test_lex_single_3_byte_char
119
- tokens = RL.lex('れ+')
115
+ specify('lex single 3 byte char') do
116
+ tokens = RL.lex("\u308C+")
120
117
 
121
- assert_equal 2, tokens.length
118
+ expect(tokens.length).to eq 2
122
119
  end
123
120
 
124
- def test_lex_single_4_byte_char
125
- tokens = RL.lex('𝄞+')
121
+ specify('lex single 4 byte char') do
122
+ tokens = RL.lex("\u{1D11E}+")
126
123
 
127
- assert_equal 2, tokens.length
124
+ expect(tokens.length).to eq 2
128
125
  end
129
-
130
126
  end
@@ -1,7 +1,6 @@
1
- require File.expand_path("../../helpers", __FILE__)
2
-
3
- class LexerNesting < Test::Unit::TestCase
1
+ require 'spec_helper'
4
2
 
3
+ RSpec.describe('Nesting lexing') do
5
4
  tests = {
6
5
  '(((b)))' => {
7
6
  0 => [:group, :capture, '(', 0, 1, 0, 0, 0],
@@ -111,22 +110,21 @@ class LexerNesting < Test::Unit::TestCase
111
110
  }
112
111
 
113
112
  tests.each_with_index do |(pattern, checks), count|
114
- define_method "test_lex_nesting_in_'#{pattern}'_#{count}" do
113
+ specify("lex_nesting_in_'#{pattern}'_#{count}") do
115
114
  tokens = RL.lex(pattern, 'ruby/1.9')
116
115
 
117
116
  checks.each do |offset, (type, token, text, ts, te, level, set_level, conditional_level)|
118
117
  struct = tokens.at(offset)
119
118
 
120
- assert_equal type, struct.type
121
- assert_equal token, struct.token
122
- assert_equal text, struct.text
123
- assert_equal ts, struct.ts
124
- assert_equal te, struct.te
125
- assert_equal level, struct.level
126
- assert_equal set_level, struct.set_level
127
- assert_equal conditional_level, struct.conditional_level
119
+ expect(struct.type).to eq type
120
+ expect(struct.token).to eq token
121
+ expect(struct.text).to eq text
122
+ expect(struct.ts).to eq ts
123
+ expect(struct.te).to eq te
124
+ expect(struct.level).to eq level
125
+ expect(struct.set_level).to eq set_level
126
+ expect(struct.conditional_level).to eq conditional_level
128
127
  end
129
128
  end
130
129
  end
131
-
132
130
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('RefCall lexing') do
4
+ tests = {
5
+ # Traditional numerical group back-reference
6
+ '(abc)\1' => [3, :backref, :number, '\1', 5, 7, 0, 0, 0],
7
+
8
+ # Group back-references, named, numbered, and relative
9
+ '(?<X>abc)\k<X>' => [3, :backref, :name_ref, '\k<X>', 9, 14, 0, 0, 0],
10
+ "(?<X>abc)\\k'X'" => [3, :backref, :name_ref, "\\k'X'", 9, 14, 0, 0, 0],
11
+
12
+ '(abc)\k<1>' => [3, :backref, :number_ref, '\k<1>', 5, 10, 0, 0, 0],
13
+ "(abc)\\k'1'" => [3, :backref, :number_ref, "\\k'1'", 5, 10, 0, 0, 0],
14
+
15
+ '(abc)\k<-1>' => [3, :backref, :number_rel_ref, '\k<-1>', 5, 11, 0, 0, 0],
16
+ "(abc)\\k'-1'" => [3, :backref, :number_rel_ref, "\\k'-1'", 5, 11, 0, 0, 0],
17
+
18
+ # Sub-expression invocation, named, numbered, and relative
19
+ '(?<X>abc)\g<X>' => [3, :backref, :name_call, '\g<X>', 9, 14, 0, 0, 0],
20
+ "(?<X>abc)\\g'X'" => [3, :backref, :name_call, "\\g'X'", 9, 14, 0, 0, 0],
21
+
22
+ '(abc)\g<1>' => [3, :backref, :number_call, '\g<1>', 5, 10, 0, 0, 0],
23
+ "(abc)\\g'1'" => [3, :backref, :number_call, "\\g'1'", 5, 10, 0, 0, 0],
24
+
25
+ '(abc)\g<-1>' => [3, :backref, :number_rel_call, '\g<-1>', 5, 11, 0, 0, 0],
26
+ "(abc)\\g'-1'" => [3, :backref, :number_rel_call, "\\g'-1'", 5, 11, 0, 0, 0],
27
+
28
+ '(abc)\g<+1>' => [3, :backref, :number_rel_call, '\g<+1>', 5, 11, 0, 0, 0],
29
+ "(abc)\\g'+1'" => [3, :backref, :number_rel_call, "\\g'+1'", 5, 11, 0, 0, 0],
30
+
31
+ # Group back-references, with nesting level
32
+ '(?<X>abc)\k<X-0>' => [3, :backref, :name_recursion_ref, '\k<X-0>', 9, 16, 0, 0, 0],
33
+ "(?<X>abc)\\k'X-0'" => [3, :backref, :name_recursion_ref, "\\k'X-0'", 9, 16, 0, 0, 0],
34
+
35
+ '(abc)\k<1-0>' => [3, :backref, :number_recursion_ref, '\k<1-0>', 5, 12, 0, 0, 0],
36
+ "(abc)\\k'1-0'" => [3, :backref, :number_recursion_ref, "\\k'1-0'", 5, 12, 0, 0, 0],
37
+ }
38
+
39
+ tests.each_with_index do |(pattern, (index, type, token, text, ts, te, level, set_level, conditional_level)), count|
40
+ specify("lexer_#{type}_#{token}_#{count}") do
41
+ tokens = RL.lex(pattern, 'ruby/1.9')
42
+ struct = tokens.at(index)
43
+
44
+ expect(struct.type).to eq type
45
+ expect(struct.token).to eq token
46
+ expect(struct.text).to eq text
47
+ expect(struct.ts).to eq ts
48
+ expect(struct.te).to eq te
49
+ expect(struct.level).to eq level
50
+ expect(struct.set_level).to eq set_level
51
+ expect(struct.conditional_level).to eq conditional_level
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe(Regexp::Parser) do
4
+ specify('parse returns a root expression') do
5
+ expect(RP.parse('abc')).to be_instance_of(Regexp::Expression::Root)
6
+ end
7
+
8
+ specify('parse root contains expressions') do
9
+ root = RP.parse(/^a.c+[^one]{2,3}\b\d\\\C-C$/)
10
+ expect(root.expressions).to all(be_a Regexp::Expression::Base)
11
+ end
12
+
13
+ specify('parse node types') do
14
+ root = RP.parse('^(one){2,3}([^d\\]efm-qz\\,\\-]*)(ghi)+$')
15
+
16
+ expect(root[1][0]).to be_a(Literal)
17
+ expect(root[1]).to be_quantified
18
+ expect(root[2][0]).to be_a(CharacterSet)
19
+ expect(root[2]).not_to be_quantified
20
+ expect(root[3]).to be_a(Group::Capture)
21
+ expect(root[3]).to be_quantified
22
+ end
23
+
24
+ specify('parse no quantifier target raises error') do
25
+ expect { RP.parse('?abc') }.to raise_error(ArgumentError)
26
+ end
27
+
28
+ specify('parse sequence no quantifier target raises error') do
29
+ expect { RP.parse('abc|?def') }.to raise_error(ArgumentError)
30
+ end
31
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Alternation parsing') do
4
+ let(:root) { RP.parse('(ab??|cd*|ef+)*|(gh|ij|kl)?') }
5
+
6
+ specify('parse alternation root') do
7
+ e = root[0]
8
+ expect(e).to be_a(Alternation)
9
+ end
10
+
11
+ specify('parse alternation alts') do
12
+ alts = root[0].alternatives
13
+
14
+ expect(alts[0]).to be_a(Alternative)
15
+ expect(alts[1]).to be_a(Alternative)
16
+
17
+ expect(alts[0][0]).to be_a(Group::Capture)
18
+ expect(alts[1][0]).to be_a(Group::Capture)
19
+
20
+ expect(alts.length).to eq 2
21
+ end
22
+
23
+ specify('parse alternation nested') do
24
+ e = root[0].alternatives[0][0][0]
25
+
26
+ expect(e).to be_a(Alternation)
27
+ end
28
+
29
+ specify('parse alternation nested sequence') do
30
+ alts = root[0][0]
31
+ nested = alts[0][0][0]
32
+
33
+ expect(nested).to be_a(Alternative)
34
+
35
+ expect(nested[0]).to be_a(Literal)
36
+ expect(nested[1]).to be_a(Literal)
37
+ expect(nested.expressions.length).to eq 2
38
+ end
39
+
40
+ specify('parse alternation nested groups') do
41
+ root = RP.parse('(i|ey|([ougfd]+)|(ney))')
42
+
43
+ alts = root[0][0].alternatives
44
+ expect(alts.length).to eq 4
45
+ end
46
+
47
+ specify('parse alternation grouped alts') do
48
+ root = RP.parse('ca((n)|(t)|(ll)|(b))')
49
+
50
+ alts = root[1][0].alternatives
51
+
52
+ expect(alts.length).to eq 4
53
+
54
+ expect(alts[0]).to be_a(Alternative)
55
+ expect(alts[1]).to be_a(Alternative)
56
+ expect(alts[2]).to be_a(Alternative)
57
+ expect(alts[3]).to be_a(Alternative)
58
+ end
59
+
60
+ specify('parse alternation nested grouped alts') do
61
+ root = RP.parse('ca((n|t)|(ll|b))')
62
+
63
+ alts = root[1][0].alternatives
64
+
65
+ expect(alts.length).to eq 2
66
+
67
+ expect(alts[0]).to be_a(Alternative)
68
+ expect(alts[1]).to be_a(Alternative)
69
+
70
+ subalts = root[1][0][0][0][0].alternatives
71
+
72
+ expect(alts.length).to eq 2
73
+
74
+ expect(subalts[0]).to be_a(Alternative)
75
+ expect(subalts[1]).to be_a(Alternative)
76
+ end
77
+
78
+ specify('parse alternation continues after nesting') do
79
+ root = RP.parse(/a|(b)c/)
80
+
81
+ seq = root[0][1].expressions
82
+
83
+ expect(seq.length).to eq 2
84
+
85
+ expect(seq[0]).to be_a(Group::Capture)
86
+ expect(seq[1]).to be_a(Literal)
87
+ end
88
+ end
@@ -1,7 +1,6 @@
1
- require File.expand_path("../../helpers", __FILE__)
2
-
3
- class TestParserAnchors < Test::Unit::TestCase
1
+ require 'spec_helper'
4
2
 
3
+ RSpec.describe('Anchor parsing') do
5
4
  tests = {
6
5
  '^a' => [0, :anchor, :bol, Anchor::BOL],
7
6
  'a$' => [1, :anchor, :eol, Anchor::EOL],
@@ -19,16 +18,14 @@ class TestParserAnchors < Test::Unit::TestCase
19
18
  }
20
19
 
21
20
  tests.each_with_index do |(pattern, (index, type, token, klass)), count|
22
- define_method "test_parse_anchor_#{token}_#{count}" do
21
+ specify("parse_anchor_#{token}_#{count}") do
23
22
  root = RP.parse(pattern, 'ruby/1.9')
24
- exp = root.expressions.at(index)
23
+ exp = root.expressions.at(index)
25
24
 
26
- assert exp.is_a?(klass),
27
- "Expected #{klass}, but got #{exp.class.name}"
25
+ expect(exp).to be_a(klass)
28
26
 
29
- assert_equal type, exp.type
30
- assert_equal token, exp.token
27
+ expect(exp.type).to eq type
28
+ expect(exp.token).to eq token
31
29
  end
32
30
  end
33
-
34
31
  end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe('Conditional parsing') do
4
+ specify('parse conditional') do
5
+ regexp = Regexp.new('(?<A>a)(?(<A>)T|F)/')
6
+
7
+ root = RP.parse(regexp, 'ruby/2.0')
8
+ exp = root[1]
9
+
10
+ expect(exp).to be_a(Conditional::Expression)
11
+
12
+ expect(exp.type).to eq :conditional
13
+ expect(exp.token).to eq :open
14
+ expect(exp.to_s).to eq '(?(<A>)T|F)'
15
+ expect(exp.reference).to eq 'A'
16
+ end
17
+
18
+ specify('parse conditional condition') do
19
+ regexp = Regexp.new('(?<A>a)(?(<A>)T|F)/')
20
+
21
+ root = RP.parse(regexp, 'ruby/2.0')
22
+ exp = root[1].condition
23
+
24
+ expect(exp).to be_a(Conditional::Condition)
25
+
26
+ expect(exp.type).to eq :conditional
27
+ expect(exp.token).to eq :condition
28
+ expect(exp.to_s).to eq '(<A>)'
29
+ expect(exp.reference).to eq 'A'
30
+ expect(exp.referenced_expression.to_s).to eq '(?<A>a)'
31
+ end
32
+
33
+ specify('parse conditional condition with number ref') do
34
+ regexp = Regexp.new('(a)(?(1)T|F)/')
35
+
36
+ root = RP.parse(regexp, 'ruby/2.0')
37
+ exp = root[1].condition
38
+
39
+ expect(exp).to be_a(Conditional::Condition)
40
+
41
+ expect(exp.type).to eq :conditional
42
+ expect(exp.token).to eq :condition
43
+ expect(exp.to_s).to eq '(1)'
44
+ expect(exp.reference).to eq 1
45
+ expect(exp.referenced_expression.to_s).to eq '(a)'
46
+ end
47
+
48
+ specify('parse conditional nested groups') do
49
+ regexp = Regexp.new('((a)|(b)|((?(2)(c(d|e)+)?|(?(3)f|(?(4)(g|(h)(i)))))))/')
50
+
51
+ root = RP.parse(regexp, 'ruby/2.0')
52
+
53
+ expect(root.to_s).to eq regexp.source
54
+
55
+ group = root.first
56
+ expect(group).to be_instance_of(Group::Capture)
57
+
58
+ alt = group.first
59
+ expect(alt).to be_instance_of(Alternation)
60
+ expect(alt.length).to eq 3
61
+
62
+ expect(alt.map(&:first)).to all(be_a Group::Capture)
63
+
64
+ subgroup = alt[2].first
65
+ conditional = subgroup.first
66
+
67
+ expect(conditional).to be_instance_of(Conditional::Expression)
68
+ expect(conditional.length).to eq 3
69
+
70
+ expect(conditional[0]).to be_instance_of(Conditional::Condition)
71
+ expect(conditional[0].to_s).to eq '(2)'
72
+
73
+ condition = conditional.condition
74
+ expect(condition).to be_instance_of(Conditional::Condition)
75
+ expect(condition.to_s).to eq '(2)'
76
+
77
+ branches = conditional.branches
78
+ expect(branches.length).to eq 2
79
+ expect(branches).to be_instance_of(Array)
80
+ end
81
+
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)))/')
84
+
85
+ root = RP.parse(regexp, 'ruby/2.0')
86
+
87
+ expect(root.to_s).to eq regexp.source
88
+
89
+ {
90
+ 1 => [2, root[1]],
91
+ 2 => [2, root[1][1][0]],
92
+ 3 => [2, root[1][1][0][2][0]],
93
+ 4 => [1, root[1][2][0]],
94
+ 5 => [2, root[1][2][0][1][0]]
95
+ }.each do |index, example|
96
+ branch_count, exp = example
97
+
98
+ expect(exp).to be_instance_of(Conditional::Expression)
99
+ expect(exp.condition.to_s).to eq "(#{index})"
100
+ expect(exp.branches.length).to eq branch_count
101
+ end
102
+ end
103
+
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/')
106
+
107
+ root = RP.parse(regexp, 'ruby/2.0')
108
+
109
+ expect(root.to_s).to eq regexp.source
110
+
111
+ expect(root.first).to be_instance_of(Alternation)
112
+
113
+ [
114
+ [3, 'b|c|d', root[0][0][1][1][0][0]],
115
+ [3, 'e|f|g', root[0][0][1][2][0][0]],
116
+ [3, 'i|j|k', root[0][0][3][1][0][0]],
117
+ [3, 'l|m|n', root[0][0][3][2][0][0]]
118
+ ].each do |example|
119
+ alt_count, alt_text, exp = example
120
+
121
+ expect(exp).to be_instance_of(Alternation)
122
+ expect(exp.to_s).to eq alt_text
123
+ expect(exp.alternatives.length).to eq alt_count
124
+ end
125
+ end
126
+
127
+ specify('parse conditional extra separator') do
128
+ regexp = Regexp.new('(?<A>a)(?(<A>)T|)/')
129
+
130
+ root = RP.parse(regexp, 'ruby/2.0')
131
+ branches = root[1].branches
132
+
133
+ expect(branches.length).to eq 2
134
+
135
+ seq_1, seq_2 = branches
136
+
137
+ [seq_1, seq_2].each do |seq|
138
+ expect(seq).to be_a(Sequence)
139
+
140
+ expect(seq.type).to eq :expression
141
+ expect(seq.token).to eq :sequence
142
+ end
143
+
144
+ expect(seq_1.to_s).to eq 'T'
145
+ expect(seq_2.to_s).to eq ''
146
+ end
147
+
148
+ specify('parse conditional quantified') do
149
+ regexp = Regexp.new('(foo)(?(1)\d|(\w)){42}/')
150
+
151
+ root = RP.parse(regexp, 'ruby/2.0')
152
+ conditional = root[1]
153
+
154
+ expect(conditional).to be_quantified
155
+ expect(conditional.quantifier.to_s).to eq '{42}'
156
+ expect(conditional.to_s).to eq '(?(1)\\d|(\\w)){42}'
157
+ expect(conditional.branches.any?(&:quantified?)).to be false
158
+ end
159
+
160
+ specify('parse conditional branch content quantified') do
161
+ regexp = Regexp.new('(foo)(?(1)\d{23}|(\w){42})/')
162
+
163
+ root = RP.parse(regexp, 'ruby/2.0')
164
+ conditional = root[1]
165
+
166
+ expect(conditional).not_to be_quantified
167
+ expect(conditional.branches.any?(&:quantified?)).to be false
168
+ expect(conditional.branches[0][0]).to be_quantified
169
+ expect(conditional.branches[0][0].quantifier.to_s).to eq '{23}'
170
+ expect(conditional.branches[1][0]).to be_quantified
171
+ expect(conditional.branches[1][0].quantifier.to_s).to eq '{42}'
172
+ end
173
+
174
+ specify('parse conditional excessive branches') do
175
+ regexp = '(?<A>a)(?(<A>)T|F|X)'
176
+
177
+ expect { RP.parse(regexp, 'ruby/2.0') }.to raise_error(Conditional::TooManyBranches)
178
+ end
179
+ end