regexp_parser 0.1.6 → 0.2.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +57 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE +1 -1
  5. data/README.md +225 -206
  6. data/Rakefile +9 -3
  7. data/lib/regexp_parser.rb +7 -11
  8. data/lib/regexp_parser/expression.rb +72 -14
  9. data/lib/regexp_parser/expression/classes/alternation.rb +3 -16
  10. data/lib/regexp_parser/expression/classes/conditional.rb +57 -0
  11. data/lib/regexp_parser/expression/classes/free_space.rb +17 -0
  12. data/lib/regexp_parser/expression/classes/keep.rb +7 -0
  13. data/lib/regexp_parser/expression/classes/set.rb +28 -7
  14. data/lib/regexp_parser/expression/methods/strfregexp.rb +113 -0
  15. data/lib/regexp_parser/expression/methods/tests.rb +116 -0
  16. data/lib/regexp_parser/expression/methods/traverse.rb +63 -0
  17. data/lib/regexp_parser/expression/quantifier.rb +10 -0
  18. data/lib/regexp_parser/expression/sequence.rb +45 -0
  19. data/lib/regexp_parser/expression/subexpression.rb +29 -1
  20. data/lib/regexp_parser/lexer.rb +31 -8
  21. data/lib/regexp_parser/parser.rb +118 -45
  22. data/lib/regexp_parser/scanner.rb +1745 -1404
  23. data/lib/regexp_parser/scanner/property.rl +57 -3
  24. data/lib/regexp_parser/scanner/scanner.rl +161 -34
  25. data/lib/regexp_parser/syntax.rb +12 -2
  26. data/lib/regexp_parser/syntax/ruby/1.9.1.rb +3 -3
  27. data/lib/regexp_parser/syntax/ruby/1.9.3.rb +2 -7
  28. data/lib/regexp_parser/syntax/ruby/2.0.0.rb +4 -1
  29. data/lib/regexp_parser/syntax/ruby/2.1.4.rb +13 -0
  30. data/lib/regexp_parser/syntax/ruby/2.1.5.rb +13 -0
  31. data/lib/regexp_parser/syntax/ruby/2.1.rb +2 -2
  32. data/lib/regexp_parser/syntax/ruby/2.2.0.rb +16 -0
  33. data/lib/regexp_parser/syntax/ruby/2.2.rb +8 -0
  34. data/lib/regexp_parser/syntax/tokens.rb +19 -2
  35. data/lib/regexp_parser/syntax/tokens/conditional.rb +22 -0
  36. data/lib/regexp_parser/syntax/tokens/keep.rb +14 -0
  37. data/lib/regexp_parser/syntax/tokens/unicode_property.rb +45 -4
  38. data/lib/regexp_parser/token.rb +23 -8
  39. data/lib/regexp_parser/version.rb +5 -0
  40. data/regexp_parser.gemspec +35 -0
  41. data/test/expression/test_all.rb +6 -1
  42. data/test/expression/test_base.rb +19 -0
  43. data/test/expression/test_conditionals.rb +114 -0
  44. data/test/expression/test_free_space.rb +33 -0
  45. data/test/expression/test_set.rb +61 -0
  46. data/test/expression/test_strfregexp.rb +214 -0
  47. data/test/expression/test_subexpression.rb +24 -0
  48. data/test/expression/test_tests.rb +99 -0
  49. data/test/expression/test_to_h.rb +48 -0
  50. data/test/expression/test_to_s.rb +46 -0
  51. data/test/expression/test_traverse.rb +164 -0
  52. data/test/lexer/test_all.rb +16 -3
  53. data/test/lexer/test_conditionals.rb +101 -0
  54. data/test/lexer/test_keep.rb +24 -0
  55. data/test/lexer/test_literals.rb +51 -51
  56. data/test/lexer/test_nesting.rb +62 -62
  57. data/test/lexer/test_refcalls.rb +18 -20
  58. data/test/parser/test_all.rb +18 -3
  59. data/test/parser/test_alternation.rb +11 -14
  60. data/test/parser/test_conditionals.rb +148 -0
  61. data/test/parser/test_escapes.rb +29 -5
  62. data/test/parser/test_free_space.rb +139 -0
  63. data/test/parser/test_groups.rb +40 -0
  64. data/test/parser/test_keep.rb +21 -0
  65. data/test/scanner/test_all.rb +8 -2
  66. data/test/scanner/test_conditionals.rb +166 -0
  67. data/test/scanner/test_escapes.rb +8 -5
  68. data/test/scanner/test_free_space.rb +133 -0
  69. data/test/scanner/test_groups.rb +28 -0
  70. data/test/scanner/test_keep.rb +33 -0
  71. data/test/scanner/test_properties.rb +4 -0
  72. data/test/scanner/test_scripts.rb +71 -1
  73. data/test/syntax/ruby/test_1.9.3.rb +2 -2
  74. data/test/syntax/ruby/test_2.0.0.rb +38 -0
  75. data/test/syntax/ruby/test_2.2.0.rb +38 -0
  76. data/test/syntax/ruby/test_all.rb +1 -8
  77. data/test/syntax/ruby/test_files.rb +104 -0
  78. data/test/test_all.rb +2 -1
  79. data/test/token/test_all.rb +2 -0
  80. data/test/token/test_token.rb +109 -0
  81. metadata +75 -21
  82. data/VERSION.yml +0 -5
  83. data/lib/regexp_parser/ctype.rb +0 -48
  84. data/test/syntax/ruby/test_2.x.rb +0 -46
@@ -2,10 +2,6 @@ require File.expand_path("../../helpers", __FILE__)
2
2
 
3
3
  class TestParserEscapes < Test::Unit::TestCase
4
4
 
5
- def test_parse_control_sequence_short
6
- #root = RP.parse(/\b\d\\\c2\C-C\M-\C-2/)
7
- end
8
-
9
5
  tests = {
10
6
  /a\ac/ => [1, :escape, :bell, EscapeSequence::Bell],
11
7
  /a\ec/ => [1, :escape, :escape, EscapeSequence::AsciiEscape],
@@ -33,7 +29,7 @@ class TestParserEscapes < Test::Unit::TestCase
33
29
 
34
30
  count = 0
35
31
  tests.each do |pattern, test|
36
- define_method "test_parse_anchor_#{test[2]}_#{count+=1}" do
32
+ define_method "test_parse_escape_#{test[2]}_#{count+=1}" do
37
33
  root = RP.parse(pattern, 'ruby/1.9')
38
34
  exp = root.expressions[test[0]]
39
35
 
@@ -45,4 +41,32 @@ class TestParserEscapes < Test::Unit::TestCase
45
41
  end
46
42
  end
47
43
 
44
+ def test_parse_escape_control_sequence_lower
45
+ root = RP.parse(/a\\\c2b/)
46
+
47
+ assert_equal( EscapeSequence::Control, root[2].class )
48
+ assert_equal( '\\c2', root[2].text )
49
+ end
50
+
51
+ def test_parse_escape_control_sequence_upper
52
+ root = RP.parse(/\d\\\C-C\w/)
53
+
54
+ assert_equal( EscapeSequence::Control, root[2].class )
55
+ assert_equal( '\\C-C', root[2].text )
56
+ end
57
+
58
+ def test_parse_escape_meta_sequence
59
+ root = RP.parse(/\Z\\\M-Z/n)
60
+
61
+ assert_equal( EscapeSequence::Meta, root[2].class )
62
+ assert_equal( '\\M-Z', root[2].text )
63
+ end
64
+
65
+ def test_parse_escape_meta_control_sequence
66
+ root = RP.parse(/\A\\\M-\C-X/n)
67
+
68
+ assert_equal( EscapeSequence::MetaControl, root[2].class )
69
+ assert_equal( '\\M-\\C-X', root[2].text )
70
+ end
71
+
48
72
  end
@@ -0,0 +1,139 @@
1
+ require File.expand_path("../../helpers", __FILE__)
2
+
3
+ class ParserFreeSpace < Test::Unit::TestCase
4
+
5
+ def test_parse_free_space_spaces
6
+ regexp = /a ? b * c + d{2,4}/x
7
+ root = RP.parse(regexp)
8
+
9
+ 0.upto(6) do |i|
10
+ if i%2 == 1
11
+ # Consecutive spaces get merged by the parser, thus the two spaces.
12
+ assert_equal( WhiteSpace, root[i].class )
13
+ assert_equal( ' ', root[i].text )
14
+ else
15
+ assert_equal( Literal, root[i].class )
16
+ assert_equal( true, root[i].quantified? )
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_parse_non_free_space_literals
22
+ regexp = /a b c d/
23
+ root = RP.parse(regexp)
24
+
25
+ assert_equal( Literal, root.first.class )
26
+ assert_equal( 'a b c d', root.first.text )
27
+ end
28
+
29
+ def test_parse_free_space_comments
30
+ regexp = %r{
31
+ a ? # One letter
32
+ b {2,5} # Another one
33
+ [c-g] + # A set
34
+ (h|i|j) | # A group
35
+ klm *
36
+ nop +
37
+ }x
38
+
39
+ root = RP.parse(regexp)
40
+
41
+ alt = root.first
42
+ assert_equal( Alternation, alt.class )
43
+
44
+ alt_1 = alt.alternatives.first
45
+ assert_equal( Alternative, alt_1.class )
46
+ assert_equal( 15, alt_1.length )
47
+
48
+ [0, 2, 4, 6, 8, 12, 14].each do |i|
49
+ assert_equal( WhiteSpace, alt_1[i].class )
50
+ end
51
+
52
+ [3, 7, 11].each do |i|
53
+ assert_equal( Comment, alt_1[i].class )
54
+ end
55
+
56
+ alt_2 = alt.alternatives.last
57
+ assert_equal( Alternative, alt_2.class )
58
+ assert_equal( 7, alt_2.length )
59
+
60
+ [0, 2, 4, 6].each do |i|
61
+ assert_equal( WhiteSpace, alt_2[i].class )
62
+ end
63
+
64
+ assert_equal( Comment, alt_2[1].class )
65
+ end
66
+
67
+ def test_parse_free_space_nested_comments
68
+ # Tests depend on spacing and indentation, obviously.
69
+ regexp = %r{
70
+ # Group one
71
+ (
72
+ abc # Comment one
73
+ \d? # Optional \d
74
+ )+
75
+
76
+ # Group two
77
+ (
78
+ def # Comment two
79
+ \s? # Optional \s
80
+ )?
81
+ }x
82
+
83
+ root = RP.parse(regexp)
84
+
85
+ top_comment_1 = root[1]
86
+ assert_equal( Comment, top_comment_1.class )
87
+ assert_equal( "# Group one\n", top_comment_1.text )
88
+ assert_equal( 7, top_comment_1.starts_at )
89
+
90
+ top_comment_2 = root[5]
91
+ assert_equal( Comment, top_comment_2.class )
92
+ assert_equal( "# Group two\n", top_comment_2.text )
93
+ assert_equal( 95, top_comment_2.starts_at )
94
+
95
+ # Nested comments
96
+ [3, 7].each_with_index do |g, i|
97
+ group = root[g]
98
+
99
+ [3, 7].each do |c|
100
+ comment = group[c]
101
+ assert_equal( Comment, comment.class )
102
+ assert_equal( 14, comment.text.length )
103
+ end
104
+ end
105
+ end
106
+
107
+ def test_parse_free_space_quantifiers
108
+ regexp = %r{
109
+ a
110
+ # comment 1
111
+ ?
112
+ (
113
+ b # comment 2
114
+ # comment 3
115
+ +
116
+ )
117
+ # comment 4
118
+ *
119
+ }x
120
+
121
+ root = RP.parse(regexp)
122
+
123
+ literal_1 = root[1]
124
+ assert_equal( Literal, literal_1.class )
125
+ assert_equal( true, literal_1.quantified? )
126
+ assert_equal( :zero_or_one, literal_1.quantifier.token )
127
+
128
+ group = root[5]
129
+ assert_equal( Group::Capture, group.class )
130
+ assert_equal( true, group.quantified? )
131
+ assert_equal( :zero_or_more, group.quantifier.token )
132
+
133
+ literal_2 = group[1]
134
+ assert_equal( Literal, literal_2.class )
135
+ assert_equal( true, literal_2.quantified? )
136
+ assert_equal( :one_or_more, literal_2.quantifier.token )
137
+ end
138
+
139
+ end
@@ -34,6 +34,46 @@ class TestParserGroups < Test::Unit::TestCase
34
34
  assert_equal( false, t.expressions[0].expressions[1].x? )
35
35
  end
36
36
 
37
+ if RUBY_VERSION >= '2.0'
38
+ def test_parse_options_dau
39
+ t = RP.parse('(?dua:abc)')
40
+
41
+ assert_equal( true, t.expressions[0].d? )
42
+ assert_equal( true, t.expressions[0].a? )
43
+ assert_equal( true, t.expressions[0].u? )
44
+ end
45
+
46
+ def test_parse_nested_options_dau
47
+ t = RP.parse('(?u:a(?d:b))')
48
+
49
+ assert_equal( true, t.expressions[0].u? )
50
+ assert_equal( false, t.expressions[0].d? )
51
+ assert_equal( false, t.expressions[0].a? )
52
+
53
+ assert_equal( true, t.expressions[0].expressions[1].d? )
54
+ assert_equal( false, t.expressions[0].expressions[1].a? )
55
+ assert_equal( false, t.expressions[0].expressions[1].u? )
56
+ end
57
+
58
+ def test_parse_nested_options_da
59
+ t = RP.parse('(?di-xm:a(?da-x:b))')
60
+
61
+ assert_equal( true, t.expressions[0].d? )
62
+ assert_equal( true, t.expressions[0].i? )
63
+ assert_equal( false, t.expressions[0].m? )
64
+ assert_equal( false, t.expressions[0].x? )
65
+ assert_equal( false, t.expressions[0].a? )
66
+ assert_equal( false, t.expressions[0].u? )
67
+
68
+ assert_equal( true, t.expressions[0].expressions[1].d? )
69
+ assert_equal( true, t.expressions[0].expressions[1].a? )
70
+ assert_equal( false, t.expressions[0].expressions[1].u? )
71
+ assert_equal( false, t.expressions[0].expressions[1].x? )
72
+ assert_equal( false, t.expressions[0].expressions[1].m? )
73
+ assert_equal( false, t.expressions[0].expressions[1].i? )
74
+ end
75
+ end
76
+
37
77
  def test_parse_lookahead
38
78
  t = RP.parse('(?=abc)(?!def)', 'ruby/1.8')
39
79
 
@@ -0,0 +1,21 @@
1
+ require File.expand_path("../../helpers", __FILE__)
2
+
3
+ class ParserKeep < Test::Unit::TestCase
4
+
5
+ def test_parse_keep
6
+ regexp = /ab\Kcd/
7
+ root = RP.parse(regexp)
8
+
9
+ assert_equal( Keep::Mark, root[1].class )
10
+ assert_equal( '\\K', root[1].text )
11
+ end
12
+
13
+ def test_parse_keep_nested
14
+ regexp = /(a\\\Kb)/
15
+ root = RP.parse(regexp)
16
+
17
+ assert_equal( Keep::Mark, root[0][2].class )
18
+ assert_equal( '\\K', root[0][2].text )
19
+ end
20
+
21
+ end
@@ -1,12 +1,18 @@
1
1
  require File.expand_path("../../helpers", __FILE__)
2
2
 
3
3
  %w{
4
- anchors errors escapes groups literals meta properties
5
- quantifiers scripts sets types
4
+ anchors errors escapes free_space groups literals
5
+ meta properties quantifiers scripts sets types
6
6
  }.each do|tc|
7
7
  require File.expand_path("../test_#{tc}", __FILE__)
8
8
  end
9
9
 
10
+ if RUBY_VERSION >= '2.0.0'
11
+ %w{conditionals keep}.each do|tc|
12
+ require File.expand_path("../test_#{tc}", __FILE__)
13
+ end
14
+ end
15
+
10
16
  class TestRegexpScanner < Test::Unit::TestCase
11
17
 
12
18
  def test_scanner_returns_an_array
@@ -0,0 +1,166 @@
1
+ require File.expand_path("../../helpers", __FILE__)
2
+
3
+ class ScannerConditionals < Test::Unit::TestCase
4
+
5
+ # Basic conditional scan token tests
6
+ tests = {
7
+ /(?(1)T|F)/ => [0, :conditional, :open, '(?', 0, 2],
8
+ /(?(2)T|F)/ => [1, :conditional, :condition_open, '(', 2, 3],
9
+ /(?(3)T|F)/ => [2, :conditional, :condition, '3', 3, 4],
10
+ /(?(4)T|F)/ => [3, :conditional, :condition_close, ')', 4, 5],
11
+ /(?(5)T|F)/ => [4, :literal, :literal, 'T', 5, 6],
12
+ /(?(6)T|F)/ => [5, :conditional, :separator, '|', 6, 7],
13
+ /(?(7)T|F)/ => [6, :literal, :literal, 'F', 7, 8],
14
+ /(?(8)T|F)/ => [7, :conditional, :close, ')', 8, 9],
15
+
16
+ /(?(1)TRUE)/ => [5, :conditional, :close, ')', 9, 10],
17
+
18
+ /(?(1)TRUE|)/ => [5, :conditional, :separator, '|', 9, 10],
19
+ /(?(2)TRUE|)/ => [6, :conditional, :close, ')', 10, 11],
20
+
21
+ /(?<N>A)(?(<N>)T|F)/ => [5, :conditional, :condition, '<N>', 10, 13],
22
+ /(?'N'A)(?('N')T|F)/ => [5, :conditional, :condition, "'N'", 10, 13],
23
+ }
24
+
25
+ count = 0
26
+ tests.each do |pattern, test|
27
+ define_method "test_scan_#{test[1]}_#{test[2]}_#{count+=1}" do
28
+
29
+ tokens = RS.scan(pattern)
30
+ token = tokens[test[0]]
31
+ assert_equal( test[1,5], token )
32
+
33
+ end
34
+ end
35
+
36
+ def test_scan_conditional_nested
37
+ regexp = /(a(b(c)))(?(1)(?(2)d|(?(3)e|f))|(?(2)(?(1)g|h)))/
38
+ tokens = RS.scan(regexp)
39
+
40
+ [ [ 0, :group, :capture, '(', 0, 1],
41
+ [ 1, :literal, :literal, 'a', 1, 2],
42
+ [ 2, :group, :capture, '(', 2, 3],
43
+ [ 3, :literal, :literal, 'b', 3, 4],
44
+ [ 4, :group, :capture, '(', 4, 5],
45
+ [ 5, :literal, :literal, 'c', 5, 6],
46
+ [ 6, :group, :close, ')', 6, 7],
47
+ [ 7, :group, :close, ')', 7, 8],
48
+ [ 8, :group, :close, ')', 8, 9],
49
+ [ 9, :conditional, :open, '(?', 9, 11],
50
+ [10, :conditional, :condition_open, '(', 11, 12],
51
+ [11, :conditional, :condition, '1', 12, 13],
52
+ [12, :conditional, :condition_close, ')', 13, 14],
53
+ [13, :conditional, :open, '(?', 14, 16],
54
+ [14, :conditional, :condition_open, '(', 16, 17],
55
+ [15, :conditional, :condition, '2', 17, 18],
56
+ [16, :conditional, :condition_close, ')', 18, 19],
57
+ [17, :literal, :literal, 'd', 19, 20],
58
+ [18, :conditional, :separator, '|', 20, 21],
59
+ [19, :conditional, :open, '(?', 21, 23],
60
+ [20, :conditional, :condition_open, '(', 23, 24],
61
+ [21, :conditional, :condition, '3', 24, 25],
62
+ [22, :conditional, :condition_close, ')', 25, 26],
63
+ [23, :literal, :literal, 'e', 26, 27],
64
+ [24, :conditional, :separator, '|', 27, 28],
65
+ [25, :literal, :literal, 'f', 28, 29],
66
+ [26, :conditional, :close, ')', 29, 30],
67
+ [27, :conditional, :close, ')', 30, 31],
68
+ [28, :conditional, :separator, '|', 31, 32],
69
+ [29, :conditional, :open, '(?', 32, 34],
70
+ [30, :conditional, :condition_open, '(', 34, 35],
71
+ [31, :conditional, :condition, '2', 35, 36],
72
+ [32, :conditional, :condition_close, ')', 36, 37],
73
+ [33, :conditional, :open, '(?', 37, 39],
74
+ [34, :conditional, :condition_open, '(', 39, 40],
75
+ [35, :conditional, :condition, '1', 40, 41],
76
+ [36, :conditional, :condition_close, ')', 41, 42],
77
+ [37, :literal, :literal, 'g', 42, 43],
78
+ [38, :conditional, :separator, '|', 43, 44],
79
+ [39, :literal, :literal, 'h', 44, 45],
80
+ [40, :conditional, :close, ')', 45, 46],
81
+ [41, :conditional, :close, ')', 46, 47],
82
+ [42, :conditional, :close, ')', 47, 48]
83
+ ].each do |test|
84
+ assert_equal( test[1,5], tokens[test[0]] )
85
+ end
86
+ end
87
+
88
+ def test_scan_conditional_nested_groups
89
+ regexp = /((a)|(b)|((?(2)(c(d|e)+)?|(?(3)f|(?(4)(g|(h)(i)))))))/
90
+ tokens = RS.scan(regexp)
91
+
92
+ [ [ 0, :group, :capture, '(', 0, 1],
93
+ [ 1, :group, :capture, '(', 1, 2],
94
+ [ 2, :literal, :literal, 'a', 2, 3],
95
+ [ 3, :group, :close, ')', 3, 4],
96
+ [ 4, :meta, :alternation, '|', 4, 5],
97
+ [ 5, :group, :capture, '(', 5, 6],
98
+ [ 6, :literal, :literal, 'b', 6, 7],
99
+ [ 7, :group, :close, ')', 7, 8],
100
+ [ 8, :meta, :alternation, '|', 8, 9],
101
+ [ 9, :group, :capture, '(', 9, 10],
102
+ [10, :conditional, :open, '(?', 10, 12],
103
+ [11, :conditional, :condition_open, '(', 12, 13],
104
+ [12, :conditional, :condition, '2', 13, 14],
105
+ [13, :conditional, :condition_close, ')', 14, 15],
106
+ [14, :group, :capture, '(', 15, 16],
107
+ [15, :literal, :literal, 'c', 16, 17],
108
+ [16, :group, :capture, '(', 17, 18],
109
+ [17, :literal, :literal, 'd', 18, 19],
110
+ [18, :meta, :alternation, '|', 19, 20],
111
+ [19, :literal, :literal, 'e', 20, 21],
112
+ [20, :group, :close, ')', 21, 22],
113
+ [21, :quantifier, :one_or_more, '+', 22, 23],
114
+ [22, :group, :close, ')', 23, 24],
115
+ [23, :quantifier, :zero_or_one, '?', 24, 25],
116
+ [24, :conditional, :separator, '|', 25, 26],
117
+ [25, :conditional, :open, '(?', 26, 28],
118
+ [26, :conditional, :condition_open, '(', 28, 29],
119
+ [27, :conditional, :condition, '3', 29, 30],
120
+ [28, :conditional, :condition_close, ')', 30, 31],
121
+ [29, :literal, :literal, 'f', 31, 32],
122
+ [30, :conditional, :separator, '|', 32, 33],
123
+ [31, :conditional, :open, '(?', 33, 35],
124
+ [32, :conditional, :condition_open, '(', 35, 36],
125
+ [33, :conditional, :condition, '4', 36, 37],
126
+ [34, :conditional, :condition_close, ')', 37, 38],
127
+ [35, :group, :capture, '(', 38, 39],
128
+ [36, :literal, :literal, 'g', 39, 40],
129
+ [37, :meta, :alternation, '|', 40, 41],
130
+ [38, :group, :capture, '(', 41, 42],
131
+ [39, :literal, :literal, 'h', 42, 43],
132
+ [40, :group, :close, ')', 43, 44],
133
+ [41, :group, :capture, '(', 44, 45],
134
+ [42, :literal, :literal, 'i', 45, 46],
135
+ [43, :group, :close, ')', 46, 47],
136
+ [44, :group, :close, ')', 47, 48],
137
+ [45, :conditional, :close, ')', 48, 49],
138
+ [46, :conditional, :close, ')', 49, 50],
139
+ [47, :conditional, :close, ')', 50, 51],
140
+ [48, :group, :close, ')', 51, 52],
141
+ [49, :group, :close, ')', 52, 53]
142
+ ].each do |test|
143
+ assert_equal( test[1,5], tokens[test[0]] )
144
+ end
145
+ end
146
+
147
+ def test_scan_conditional_nested_alternation
148
+ regexp = /(a)(?(1)(b|c|d)|(e|f|g))(h)(?(2)(i|j|k)|(l|m|n))|o|p/
149
+ tokens = RS.scan(regexp)
150
+
151
+ [9, 11, 17, 19, 32, 34, 40, 42, 46, 48].each do |token|
152
+ assert_equal(:meta, tokens[token][0])
153
+ assert_equal(:alternation, tokens[token][1])
154
+ assert_equal('|', tokens[token][2])
155
+ assert_equal(1, tokens[token][4] - tokens[token][3])
156
+ end
157
+
158
+ [14, 37].each do |token|
159
+ assert_equal(:conditional, tokens[token][0])
160
+ assert_equal(:separator, tokens[token][1])
161
+ assert_equal('|', tokens[token][2])
162
+ assert_equal(1, tokens[token][4] - tokens[token][3])
163
+ end
164
+ end
165
+
166
+ end