rattler 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. data/README.rdoc +57 -37
  2. data/features/command_line/dest_option.feature +8 -21
  3. data/features/command_line/lib_option.feature +37 -0
  4. data/features/command_line/parser_generator.feature +7 -4
  5. data/features/grammar/back_reference.feature +37 -0
  6. data/features/grammar/fail.feature +3 -3
  7. data/features/grammar/labels.feature +11 -3
  8. data/features/grammar/list_matching.feature +14 -5
  9. data/features/grammar/literal.feature +30 -4
  10. data/features/grammar/nonterminal.feature +1 -1
  11. data/features/grammar/ordered_choice.feature +2 -2
  12. data/features/grammar/skip_operator.feature +1 -1
  13. data/features/grammar/symantic_action.feature +7 -7
  14. data/features/grammar/whitespace.feature +2 -2
  15. data/features/step_definitions/grammar_steps.rb +2 -2
  16. data/lib/rattler/back_end.rb +1 -0
  17. data/lib/rattler/back_end/compiler.rb +19 -20
  18. data/lib/rattler/back_end/optimizer.rb +100 -0
  19. data/lib/rattler/back_end/optimizer/composite_reducing.rb +18 -0
  20. data/lib/rattler/back_end/optimizer/flatten_choice.rb +31 -0
  21. data/lib/rattler/back_end/optimizer/flatten_sequence.rb +59 -0
  22. data/lib/rattler/back_end/optimizer/flattening.rb +17 -0
  23. data/lib/rattler/back_end/optimizer/inline_regular_rules.rb +46 -0
  24. data/lib/rattler/back_end/optimizer/join_match_capturing_sequence.rb +71 -0
  25. data/lib/rattler/back_end/optimizer/join_match_choice.rb +37 -0
  26. data/lib/rattler/back_end/optimizer/join_match_matching_sequence.rb +38 -0
  27. data/lib/rattler/back_end/optimizer/join_match_sequence.rb +17 -0
  28. data/lib/rattler/back_end/optimizer/join_predicate_bare_match.rb +68 -0
  29. data/lib/rattler/back_end/optimizer/join_predicate_match.rb +17 -0
  30. data/lib/rattler/back_end/optimizer/join_predicate_nested_match.rb +37 -0
  31. data/lib/rattler/back_end/optimizer/join_predicate_or_bare_match.rb +68 -0
  32. data/lib/rattler/back_end/optimizer/join_predicate_or_match.rb +17 -0
  33. data/lib/rattler/back_end/optimizer/join_predicate_or_nested_match.rb +36 -0
  34. data/lib/rattler/back_end/optimizer/match_joining.rb +60 -0
  35. data/lib/rattler/back_end/optimizer/optimization.rb +94 -0
  36. data/lib/rattler/back_end/optimizer/optimization_context.rb +72 -0
  37. data/lib/rattler/back_end/optimizer/optimization_sequence.rb +37 -0
  38. data/lib/rattler/back_end/optimizer/optimize_children.rb +46 -0
  39. data/lib/rattler/back_end/optimizer/reduce_repeat_match.rb +44 -0
  40. data/lib/rattler/back_end/optimizer/remove_meaningless_wrapper.rb +32 -0
  41. data/lib/rattler/back_end/optimizer/simplify_redundant_repeat.rb +43 -0
  42. data/lib/rattler/back_end/optimizer/simplify_token_match.rb +38 -0
  43. data/lib/rattler/back_end/parser_generator.rb +21 -14
  44. data/lib/rattler/back_end/parser_generator/apply_generator.rb +35 -35
  45. data/lib/rattler/back_end/parser_generator/assert_generator.rb +29 -30
  46. data/lib/rattler/back_end/parser_generator/back_reference_generator.rb +93 -0
  47. data/lib/rattler/back_end/parser_generator/choice_generator.rb +33 -49
  48. data/lib/rattler/back_end/parser_generator/direct_action_generator.rb +14 -14
  49. data/lib/rattler/back_end/parser_generator/disallow_generator.rb +29 -30
  50. data/lib/rattler/back_end/parser_generator/dispatch_action_generator.rb +11 -13
  51. data/lib/rattler/back_end/parser_generator/expr_generator.rb +36 -56
  52. data/lib/rattler/back_end/parser_generator/fail_generator.rb +18 -18
  53. data/lib/rattler/back_end/parser_generator/group_match.rb +18 -0
  54. data/lib/rattler/back_end/parser_generator/group_match_generator.rb +76 -0
  55. data/lib/rattler/back_end/parser_generator/label_generator.rb +25 -6
  56. data/lib/rattler/back_end/parser_generator/list1_generator.rb +7 -7
  57. data/lib/rattler/back_end/parser_generator/list_generating.rb +19 -20
  58. data/lib/rattler/back_end/parser_generator/list_generator.rb +5 -5
  59. data/lib/rattler/back_end/parser_generator/match_generator.rb +52 -52
  60. data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +6 -6
  61. data/lib/rattler/back_end/parser_generator/optional_generator.rb +30 -29
  62. data/lib/rattler/back_end/parser_generator/predicate_propogating.rb +8 -8
  63. data/lib/rattler/back_end/parser_generator/repeat_generating.rb +23 -25
  64. data/lib/rattler/back_end/parser_generator/rule_generator.rb +27 -79
  65. data/lib/rattler/back_end/parser_generator/rule_set_generator.rb +102 -0
  66. data/lib/rattler/back_end/parser_generator/sequence_generator.rb +49 -41
  67. data/lib/rattler/back_end/parser_generator/skip_generator.rb +14 -20
  68. data/lib/rattler/back_end/parser_generator/skip_propogating.rb +4 -4
  69. data/lib/rattler/back_end/parser_generator/sub_generating.rb +6 -0
  70. data/lib/rattler/back_end/parser_generator/token_generator.rb +12 -12
  71. data/lib/rattler/back_end/parser_generator/token_propogating.rb +2 -2
  72. data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +4 -4
  73. data/lib/rattler/grammar.rb +4 -3
  74. data/lib/rattler/grammar/analysis.rb +91 -0
  75. data/lib/rattler/grammar/grammar.rb +37 -25
  76. data/lib/rattler/grammar/grammar_parser.rb +19 -11
  77. data/lib/rattler/grammar/metagrammar.rb +569 -800
  78. data/lib/rattler/grammar/rattler.rtlr +162 -144
  79. data/lib/rattler/parsers.rb +5 -1
  80. data/lib/rattler/parsers/action_code.rb +29 -15
  81. data/lib/rattler/parsers/apply.rb +5 -5
  82. data/lib/rattler/parsers/assert.rb +4 -18
  83. data/lib/rattler/parsers/back_reference.rb +46 -0
  84. data/lib/rattler/parsers/choice.rb +6 -39
  85. data/lib/rattler/parsers/combinator_parser.rb +32 -0
  86. data/lib/rattler/parsers/combining.rb +3 -29
  87. data/lib/rattler/parsers/direct_action.rb +27 -30
  88. data/lib/rattler/parsers/disallow.rb +4 -18
  89. data/lib/rattler/parsers/dispatch_action.rb +30 -25
  90. data/lib/rattler/parsers/label.rb +9 -18
  91. data/lib/rattler/parsers/list.rb +3 -34
  92. data/lib/rattler/parsers/list1.rb +4 -36
  93. data/lib/rattler/parsers/list_parser.rb +64 -0
  94. data/lib/rattler/parsers/match.rb +7 -42
  95. data/lib/rattler/parsers/node_code.rb +44 -0
  96. data/lib/rattler/parsers/one_or_more.rb +7 -27
  97. data/lib/rattler/parsers/optional.rb +5 -25
  98. data/lib/rattler/parsers/parser.rb +16 -44
  99. data/lib/rattler/parsers/parser_dsl.rb +13 -3
  100. data/lib/rattler/parsers/predicate.rb +4 -12
  101. data/lib/rattler/parsers/rule.rb +18 -19
  102. data/lib/rattler/parsers/rule_set.rb +63 -0
  103. data/lib/rattler/parsers/sequence.rb +12 -46
  104. data/lib/rattler/parsers/skip.rb +12 -26
  105. data/lib/rattler/parsers/token.rb +6 -21
  106. data/lib/rattler/parsers/zero_or_more.rb +6 -26
  107. data/lib/rattler/runner.rb +66 -28
  108. data/lib/rattler/runtime/extended_packrat_parser.rb +26 -20
  109. data/lib/rattler/runtime/packrat_parser.rb +17 -21
  110. data/lib/rattler/runtime/parser.rb +12 -2
  111. data/lib/rattler/runtime/recursive_descent_parser.rb +3 -11
  112. data/lib/rattler/util.rb +2 -1
  113. data/lib/rattler/util/graphviz.rb +29 -0
  114. data/lib/rattler/util/graphviz/digraph_builder.rb +71 -0
  115. data/lib/rattler/util/graphviz/node_builder.rb +84 -0
  116. data/lib/rattler/util/node.rb +37 -19
  117. data/lib/rattler/util/parser_spec_helper.rb +61 -35
  118. data/spec/rattler/back_end/compiler_spec.rb +6 -860
  119. data/spec/rattler/back_end/optimizer/flatten_choice_spec.rb +70 -0
  120. data/spec/rattler/back_end/optimizer/flatten_sequence_spec.rb +130 -0
  121. data/spec/rattler/back_end/optimizer/inline_regular_rules_spec.rb +80 -0
  122. data/spec/rattler/back_end/optimizer/join_match_capturing_sequence_spec.rb +241 -0
  123. data/spec/rattler/back_end/optimizer/join_match_choice_spec.rb +100 -0
  124. data/spec/rattler/back_end/optimizer/join_match_matching_sequence_spec.rb +112 -0
  125. data/spec/rattler/back_end/optimizer/join_predicate_bare_match_spec.rb +194 -0
  126. data/spec/rattler/back_end/optimizer/join_predicate_nested_match_spec.rb +180 -0
  127. data/spec/rattler/back_end/optimizer/join_predicate_or_bare_match_spec.rb +153 -0
  128. data/spec/rattler/back_end/optimizer/join_predicate_or_nested_match_spec.rb +153 -0
  129. data/spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb +98 -0
  130. data/spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb +226 -0
  131. data/spec/rattler/back_end/optimizer/simplify_token_match_spec.rb +85 -0
  132. data/spec/rattler/back_end/parser_generator/apply_generator_spec.rb +38 -33
  133. data/spec/rattler/back_end/parser_generator/assert_generator_spec.rb +38 -33
  134. data/spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb +181 -0
  135. data/spec/rattler/back_end/parser_generator/choice_generator_spec.rb +38 -33
  136. data/spec/rattler/back_end/parser_generator/direct_action_generator_spec.rb +38 -33
  137. data/spec/rattler/back_end/parser_generator/disallow_generator_spec.rb +38 -33
  138. data/spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb +38 -33
  139. data/spec/rattler/back_end/parser_generator/group_match_generator_spec.rb +185 -0
  140. data/spec/rattler/back_end/parser_generator/label_generator_spec.rb +38 -33
  141. data/spec/rattler/back_end/parser_generator/list1_generator_spec.rb +10 -5
  142. data/spec/rattler/back_end/parser_generator/list_generator_spec.rb +10 -5
  143. data/spec/rattler/back_end/parser_generator/match_generator_spec.rb +38 -33
  144. data/spec/rattler/back_end/parser_generator/one_or_more_generator_spec.rb +38 -33
  145. data/spec/rattler/back_end/parser_generator/optional_generator_spec.rb +38 -33
  146. data/spec/rattler/back_end/parser_generator/rule_generator_spec.rb +13 -46
  147. data/spec/rattler/back_end/parser_generator/rule_set_generator_spec.rb +97 -0
  148. data/spec/rattler/back_end/parser_generator/sequence_generator_spec.rb +38 -33
  149. data/spec/rattler/back_end/parser_generator/skip_generator_spec.rb +38 -33
  150. data/spec/rattler/back_end/parser_generator/token_generator_spec.rb +38 -33
  151. data/spec/rattler/back_end/parser_generator/zero_or_more_generator_spec.rb +39 -34
  152. data/spec/rattler/back_end/shared_compiler_examples.rb +885 -0
  153. data/spec/rattler/grammar/analysis_spec.rb +167 -0
  154. data/spec/rattler/grammar/grammar_parser_spec.rb +169 -179
  155. data/spec/rattler/grammar/grammar_spec.rb +24 -21
  156. data/spec/rattler/parsers/action_code_spec.rb +64 -19
  157. data/spec/rattler/parsers/apply_spec.rb +9 -9
  158. data/spec/rattler/parsers/back_reference_spec.rb +38 -0
  159. data/spec/rattler/parsers/combinator_parser_spec.rb +14 -0
  160. data/spec/rattler/parsers/direct_action_spec.rb +16 -2
  161. data/spec/rattler/parsers/dispatch_action_spec.rb +15 -32
  162. data/spec/rattler/parsers/fail_spec.rb +6 -4
  163. data/spec/rattler/parsers/label_spec.rb +10 -28
  164. data/spec/rattler/parsers/node_code_spec.rb +48 -0
  165. data/spec/rattler/parsers/parser_dsl_spec.rb +1 -1
  166. data/spec/rattler/parsers/rule_set_spec.rb +35 -0
  167. data/spec/rattler/parsers/sequence_spec.rb +15 -24
  168. data/spec/rattler/runtime/extended_packrat_parser_spec.rb +22 -17
  169. data/spec/rattler/runtime/packrat_parser_spec.rb +1 -1
  170. data/spec/rattler/runtime/parse_node_spec.rb +15 -19
  171. data/spec/rattler/runtime/recursive_descent_parser_spec.rb +1 -1
  172. data/spec/rattler/runtime/shared_parser_examples.rb +61 -28
  173. data/spec/rattler/util/graphviz/node_builder_spec.rb +84 -0
  174. data/spec/rattler/util/node_spec.rb +92 -65
  175. data/spec/rattler_spec.rb +16 -16
  176. data/spec/support/combinator_parser_spec_helper.rb +19 -18
  177. data/spec/support/compiler_spec_helper.rb +56 -87
  178. data/spec/support/runtime_parser_spec_helper.rb +6 -14
  179. metadata +117 -22
  180. data/features/grammar/regex.feature +0 -24
  181. data/lib/rattler/parsers/match_joining.rb +0 -67
  182. data/lib/rattler/parsers/rules.rb +0 -43
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Rattler::Parsers
4
+
5
+ describe Rattler::BackEnd::Optimizer::FlattenChoice do
6
+
7
+ describe '#apply' do
8
+
9
+ context 'given a choice with choice terms' do
10
+
11
+ let(:choice) { Choice[
12
+ Match[/a/],
13
+ Choice[Match[/b/], Match[/c/]],
14
+ Sequence[Match[/d/], Match[/e/]]
15
+ ] }
16
+
17
+ it 'flattens the choice' do
18
+ subject.apply(choice, :any).should == Choice[
19
+ Match[/a/],
20
+ Match[/b/],
21
+ Match[/c/],
22
+ Sequence[Match[/d/], Match[/e/]]
23
+ ]
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#applies_to?' do
29
+
30
+ context 'given a choice with choice terms' do
31
+
32
+ let(:choice) { Choice[
33
+ Match[/a/],
34
+ Choice[Match[/b/], Match[/c/]],
35
+ Sequence[Match[/d/], Match[/e/]]
36
+ ] }
37
+
38
+ it 'returns true' do
39
+ subject.applies_to?(choice, :any).should be_true
40
+ end
41
+ end
42
+
43
+ context 'given a choice without choice terms' do
44
+
45
+ let(:choice) { Choice[
46
+ Match[/a/],
47
+ Match[/b/],
48
+ Sequence[Match[/c/], Match[/d/]]
49
+ ] }
50
+
51
+ it 'returns false' do
52
+ subject.applies_to?(choice, :any).should be_false
53
+ end
54
+ end
55
+
56
+ context 'given something other than a choie' do
57
+
58
+ let(:choice) { Sequence[
59
+ Match[/a/],
60
+ Choice[Match[/b/], Match[/c/]],
61
+ Sequence[Match[/d/], Match[/e/]]
62
+ ] }
63
+
64
+ it 'returns false' do
65
+ subject.applies_to?(choice, :any).should be_false
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,130 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Rattler::BackEnd::Optimizer
4
+ include Rattler::Parsers
5
+
6
+ describe FlattenSequence do
7
+
8
+ let(:matching) { OptimizationContext[:type => :matching] }
9
+ let(:capturing) { OptimizationContext[:type => :capturing] }
10
+
11
+ describe '#apply' do
12
+
13
+ context 'in the matching context' do
14
+
15
+ context 'given a sequence with sequence terms' do
16
+
17
+ let(:sequence) { Sequence[
18
+ Match[/a/],
19
+ Sequence[Match[/b/], Match[/c/]],
20
+ Choice[Match[/c/], Match[/d/]]
21
+ ] }
22
+
23
+ it 'flattens the sequence' do
24
+ subject.apply(sequence, matching).should == Sequence[
25
+ Match[/a/],
26
+ Match[/b/],
27
+ Match[/c/],
28
+ Choice[Match[/c/], Match[/d/]],
29
+ ]
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'in the capturing context' do
35
+
36
+ context 'given a sequence with single-capturing sequence terms' do
37
+
38
+ let(:sequence) { Sequence[
39
+ Match[/a/],
40
+ Sequence[Skip[Match[/b/]], Skip[Match[/c/]]],
41
+ Sequence[Match[/b/], Skip[Match[/c/]]]
42
+ ] }
43
+
44
+ it 'flattens the sequences' do
45
+ subject.apply(sequence, capturing).should == Sequence[
46
+ Match[/a/],
47
+ Skip[Match[/b/]],
48
+ Skip[Match[/c/]],
49
+ Match[/b/],
50
+ Skip[Match[/c/]]
51
+ ]
52
+ end
53
+ end
54
+
55
+ context 'given a sequence with multi-capturing sequence terms' do
56
+
57
+ let(:sequence) { Sequence[
58
+ Match[/a/],
59
+ Sequence[Skip[Match[/b/]], Skip[Match[/c/]]],
60
+ Sequence[Match[/b/], Match[/c/]]
61
+ ] }
62
+
63
+ it 'avoids flattening the capturing sequences' do
64
+ subject.apply(sequence, capturing).should == Sequence[
65
+ Match[/a/],
66
+ Skip[Match[/b/]],
67
+ Skip[Match[/c/]],
68
+ Sequence[Match[/b/], Match[/c/]]
69
+ ]
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ describe '#applies_to?' do
77
+
78
+ context 'given a sequence with only capturing sequence terms' do
79
+
80
+ let(:sequence) { Sequence[
81
+ Match[/a/],
82
+ Sequence[Match[/b/], Match[/c/]],
83
+ Choice[Match[/c/], Match[/d/]]
84
+ ] }
85
+
86
+ context 'in the matching context' do
87
+ it 'returns true' do
88
+ subject.applies_to?(sequence, matching).should be_true
89
+ end
90
+ end
91
+
92
+ context 'in the capturing context' do
93
+ it 'returns false' do
94
+ subject.applies_to?(sequence, capturing).should be_false
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'given a sequence with non-capturing sequence terms' do
100
+
101
+ let(:sequence) { Sequence[
102
+ Match[/a/],
103
+ Sequence[Skip[Match[/b/]], Skip[Match[/c/]]],
104
+ Choice[Match[/c/], Match[/d/]]
105
+ ] }
106
+
107
+ context 'in the capturing context' do
108
+ it 'returns true' do
109
+ subject.applies_to?(sequence, capturing).should be_true
110
+ end
111
+ end
112
+ end
113
+
114
+ context 'given a sequence with single-capturing sequence terms' do
115
+
116
+ let(:sequence) { Sequence[
117
+ Match[/a/],
118
+ Sequence[Skip[Match[/b/]], Skip[Match[/c/]]],
119
+ Sequence[Match[/b/], Skip[Match[/c/]]]
120
+ ] }
121
+
122
+ context 'in the capturing context' do
123
+ it 'returns true' do
124
+ subject.applies_to?(sequence, capturing).should be_true
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,80 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Rattler::BackEnd::Optimizer
4
+ include Rattler::Parsers
5
+
6
+ describe InlineRegularRules do
7
+
8
+ let(:standalone) { OptimizationContext[
9
+ :type => :capturing,
10
+ :rules => rule_set,
11
+ :standalone => true
12
+ ] }
13
+
14
+ let(:modular) { OptimizationContext[
15
+ :type => :capturing,
16
+ :rules => rule_set
17
+ ] }
18
+
19
+ let(:rule_set) { RuleSet[rule_a, {:start_rule => :a}] }
20
+
21
+ describe '#apply' do
22
+
23
+ context 'given a reference to a regular rule' do
24
+
25
+ let(:rule_a) { Rule[:a, Match[/a/]] }
26
+
27
+ it 'inlines the regular rule' do
28
+ subject.apply(Apply[:a], standalone).should == Match[/a/]
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#_applies_to?' do
34
+
35
+ context 'with the :standalone option set' do
36
+
37
+ context 'given a reference to a regular rule' do
38
+
39
+ let(:rule_a) { Rule[:a, Match[/a/]] }
40
+
41
+ it 'returns true' do
42
+ subject.applies_to?(Apply[:a], standalone).should be_true
43
+ end
44
+ end
45
+
46
+ context 'given a reference to a recursive rule' do
47
+
48
+ let(:rule_a) { Rule[:a, Sequence[Match[/a/], Apply[:a]]] }
49
+
50
+ it 'returns false' do
51
+ subject.applies_to?(Apply[:a], standalone).should be_false
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'without the :standalone option set' do
57
+
58
+ context 'given a reference to a regular rule' do
59
+
60
+ context 'tagged as :inline' do
61
+
62
+ let(:rule_a) { Rule[:a, Match[/a/], {:inline => true}] }
63
+
64
+ it 'returns true' do
65
+ subject.applies_to?(Apply[:a], modular).should be_true
66
+ end
67
+ end
68
+
69
+ context 'not tagged as :inline' do
70
+
71
+ let(:rule_a) { Rule[:a, Match[/a/]] }
72
+
73
+ it 'returns false' do
74
+ subject.applies_to?(Apply[:a], modular).should be_false
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,241 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Rattler::BackEnd::Optimizer
4
+ include Rattler::Parsers
5
+ include Rattler::BackEnd::ParserGenerator
6
+
7
+ describe JoinMatchCapturingSequence do
8
+
9
+ let(:capturing) { OptimizationContext[:type => :capturing] }
10
+ let(:matching) { OptimizationContext[:type => :matching] }
11
+
12
+ describe '#apply' do
13
+
14
+ context 'given a sequence of all skipping matches' do
15
+
16
+ let(:sequence) { Sequence[
17
+ Skip[Match[/a/]],
18
+ Skip[Match[/b/]],
19
+ Skip[Match[/c/]]
20
+ ] }
21
+
22
+ it 'joins them into a single skipping match' do
23
+ subject.apply(sequence, capturing).
24
+ should == Skip[Match[/(?>a)(?>b)(?>c)/]]
25
+ end
26
+ end
27
+
28
+ context 'given a sequence of skipping matches followed by something else' do
29
+
30
+ let(:sequence) { Sequence[
31
+ Skip[Match[/a/]],
32
+ Skip[Match[/b/]],
33
+ Apply[:c]
34
+ ] }
35
+
36
+ it 'joins the consecutive skipping matches into a single match' do
37
+ subject.apply(sequence, capturing).should == Sequence[
38
+ Skip[Match[/(?>a)(?>b)/]],
39
+ Apply[:c]
40
+ ]
41
+ end
42
+ end
43
+
44
+ context 'given a sequence of skipping matches following something else' do
45
+
46
+ let(:sequence) { Sequence[
47
+ Apply[:a],
48
+ Skip[Match[/b/]],
49
+ Skip[Match[/c/]]
50
+ ] }
51
+
52
+ it 'joins the consecutive skipping matches into a single match' do
53
+ subject.apply(sequence, capturing).should == Sequence[
54
+ Apply[:a],
55
+ Skip[Match[/(?>b)(?>c)/]]
56
+ ]
57
+ end
58
+ end
59
+
60
+ context 'given a sequence of matches' do
61
+
62
+ let(:sequence) { Sequence[
63
+ Match[/a/],
64
+ Match[/b/],
65
+ Match[/c/]
66
+ ] }
67
+
68
+ it 'joins them into a single group match' do
69
+ subject.apply(sequence, capturing).
70
+ should == GroupMatch[Match[/(a)(b)(c)/], {:num_groups => 3}]
71
+ end
72
+ end
73
+
74
+ context 'given a sequence of single-group matches' do
75
+
76
+ let(:sequence) { Sequence[
77
+ GroupMatch[Match[/\s*(a)/], {:num_groups => 1}],
78
+ GroupMatch[Match[/\s*(b)/], {:num_groups => 1}],
79
+ GroupMatch[Match[/\s*(c)/], {:num_groups => 1}]
80
+ ] }
81
+
82
+ it 'joins them into a single group match' do
83
+ subject.apply(sequence, capturing).
84
+ should == GroupMatch[Match[/(?>\s*(a))(?>\s*(b))(?>\s*(c))/], {:num_groups => 3}]
85
+ end
86
+ end
87
+
88
+ context 'give a mix of bare and single-group matches' do
89
+
90
+ let(:sequence) { Sequence[
91
+ GroupMatch[Match[/\s*(a)/], {:num_groups => 1}],
92
+ Match[/[+\-*\/]/],
93
+ GroupMatch[Match[/\s*(b)/], {:num_groups => 1}],
94
+ ] }
95
+
96
+ it 'joins them into a single group match' do
97
+ subject.apply(sequence, capturing).
98
+ should == GroupMatch[Match[/(?>\s*(a))([+\-*\/])(?>\s*(b))/], {:num_groups => 3}]
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#applies_to?' do
104
+
105
+ context 'given a sequence of matches followed by something else' do
106
+
107
+ let(:sequence) { Sequence[
108
+ Match[/a/],
109
+ Match[/b/],
110
+ Apply[:c]
111
+ ] }
112
+
113
+ it 'returns true' do
114
+ subject.applies_to?(sequence, capturing).should be_true
115
+ end
116
+ end
117
+
118
+ context 'given a sequence of matches following something else' do
119
+
120
+ let(:sequence) { Sequence[
121
+ Apply[:a],
122
+ Match[/b/],
123
+ Match[/c/]
124
+ ] }
125
+
126
+ it 'returns true' do
127
+ subject.applies_to?(sequence, capturing).should be_true
128
+ end
129
+ end
130
+
131
+ context 'given a sequence of skipping matches followed by something else' do
132
+
133
+ let(:sequence) { Sequence[
134
+ Skip[Match[/a/]],
135
+ Skip[Match[/b/]],
136
+ Apply[:c]
137
+ ] }
138
+
139
+ it 'returns true' do
140
+ subject.applies_to?(sequence, capturing).should be_true
141
+ end
142
+ end
143
+
144
+ context 'given a sequence of skipping matches following something else' do
145
+
146
+ let(:sequence) { Sequence[
147
+ Apply[:a],
148
+ Skip[Match[/b/]],
149
+ Skip[Match[/c/]]
150
+ ] }
151
+
152
+ it 'returns true' do
153
+ subject.applies_to?(sequence, capturing).should be_true
154
+ end
155
+ end
156
+
157
+ context 'given a sequence of single-group matches followed by something else' do
158
+
159
+ let(:sequence) { Sequence[
160
+ GroupMatch[Match[/a/], {:num_groups => 1}],
161
+ GroupMatch[Match[/b/], {:num_groups => 1}],
162
+ Apply[:c]
163
+ ] }
164
+
165
+ it 'returns true' do
166
+ subject.applies_to?(sequence, capturing).should be_true
167
+ end
168
+ end
169
+
170
+ context 'given a sequence of single-group matches following something else' do
171
+
172
+ let(:sequence) { Sequence[
173
+ Apply[:a],
174
+ GroupMatch[Match[/b/], {:num_groups => 1}],
175
+ GroupMatch[Match[/c/], {:num_groups => 1}]
176
+ ] }
177
+
178
+ it 'returns true' do
179
+ subject.applies_to?(sequence, capturing).should be_true
180
+ end
181
+ end
182
+
183
+ context 'given a sequence of multi-group matches followed by something else' do
184
+
185
+ let(:sequence) { Sequence[
186
+ GroupMatch[Match[/a/], {:num_groups => 2}],
187
+ GroupMatch[Match[/b/], {:num_groups => 2}],
188
+ Apply[:c]
189
+ ] }
190
+
191
+ it 'returns false' do
192
+ subject.applies_to?(sequence, capturing).should be_false
193
+ end
194
+ end
195
+
196
+ context 'given a sequence of multi-group matches following something else' do
197
+
198
+ let(:sequence) { Sequence[
199
+ Apply[:a],
200
+ GroupMatch[Match[/b/], {:num_groups => 2}],
201
+ GroupMatch[Match[/c/], {:num_groups => 2}]
202
+ ] }
203
+
204
+ it 'returns false' do
205
+ subject.applies_to?(sequence, capturing).should be_false
206
+ end
207
+ end
208
+
209
+ context 'given a sequence with only one match' do
210
+
211
+ let(:sequence) { Sequence[Apply[:a], Skip[Match[/b/]], Apply[:c]] }
212
+
213
+ it 'returns false' do
214
+ subject.applies_to?(sequence, capturing).should be_false
215
+ end
216
+ end
217
+
218
+ context 'given a sequence of non-consecutive matches' do
219
+
220
+ let(:sequence) { Sequence[Skip[Match[/a/]], Apply[:b], Skip[Match[/c/]]] }
221
+
222
+ it 'returns false' do
223
+ subject.applies_to?(sequence, capturing).should be_false
224
+ end
225
+ end
226
+
227
+ context 'given a non-sequence' do
228
+
229
+ let(:parser) { Choice[
230
+ Skip[Match[/a/]],
231
+ Skip[Match[/b/]],
232
+ Skip[Match[/c/]]
233
+ ] }
234
+
235
+ it 'returns false' do
236
+ subject.applies_to?(parser, capturing).should be_false
237
+ end
238
+ end
239
+ end
240
+
241
+ end