regexp_parser 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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