rattler 0.3.0 → 0.4.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 (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