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
@@ -1,22 +1,23 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
2
 
3
+ include Rattler::BackEnd::ParserGenerator
3
4
  include Rattler::Parsers
4
5
 
5
- describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
6
-
6
+ describe TokenGenerator do
7
+
7
8
  include ParserGeneratorSpecHelper
8
-
9
+
9
10
  let(:token) { Token[Match[/\w+/]] }
10
-
11
+
11
12
  describe '#gen_basic' do
12
-
13
+
13
14
  context 'when nested' do
14
15
  it 'generates nested token matching code' do
15
16
  nested_code {|g| g.gen_basic token }.
16
17
  should == '@scanner.scan(/\w+/)'
17
18
  end
18
19
  end
19
-
20
+
20
21
  context 'when top-level' do
21
22
  it 'generates top level token matching code' do
22
23
  top_level_code {|g| g.gen_basic token }.
@@ -24,16 +25,16 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
24
25
  end
25
26
  end
26
27
  end
27
-
28
+
28
29
  describe '#gen_assert' do
29
-
30
+
30
31
  context 'when nested' do
31
32
  it 'generates nested positive lookahead code' do
32
33
  nested_code {|g| g.gen_assert token }.
33
34
  should == '(@scanner.skip(/(?=\w+)/) && true)'
34
35
  end
35
36
  end
36
-
37
+
37
38
  context 'when top-level' do
38
39
  it 'generates top level positive lookahead code' do
39
40
  top_level_code {|g| g.gen_assert token }.
@@ -41,16 +42,16 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
41
42
  end
42
43
  end
43
44
  end
44
-
45
+
45
46
  describe '#gen_disallow' do
46
-
47
+
47
48
  context 'when nested' do
48
49
  it 'generates nested negative lookahead code' do
49
50
  nested_code {|g| g.gen_disallow token }.
50
51
  should == '(@scanner.skip(/(?!\w+)/) && true)'
51
52
  end
52
53
  end
53
-
54
+
54
55
  context 'when top-level' do
55
56
  it 'generates top level negative lookahead code' do
56
57
  top_level_code {|g| g.gen_disallow token }.
@@ -58,12 +59,14 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
58
59
  end
59
60
  end
60
61
  end
61
-
62
+
62
63
  describe '#gen_dispatch_action' do
63
-
64
+
65
+ let(:code) { NodeCode.new('Word', 'parsed') }
66
+
64
67
  context 'when nested' do
65
68
  it 'generates nested matching code with a dispatch action' do
66
- nested_code {|g| g.gen_dispatch_action token, 'Word', 'parsed' }.
69
+ nested_code {|g| g.gen_dispatch_action token, code }.
67
70
  should == (<<-CODE).strip
68
71
  begin
69
72
  (r = @scanner.scan(/\\w+/)) &&
@@ -72,10 +75,10 @@ end
72
75
  CODE
73
76
  end
74
77
  end
75
-
78
+
76
79
  context 'when top-level' do
77
80
  it 'generates top level matching code with a dispatch action' do
78
- top_level_code {|g| g.gen_dispatch_action token, 'Word', 'parsed' }.
81
+ top_level_code {|g| g.gen_dispatch_action token, code }.
79
82
  should == (<<-CODE).strip
80
83
  (r = @scanner.scan(/\\w+/)) &&
81
84
  Word.parsed([r])
@@ -83,12 +86,14 @@ Word.parsed([r])
83
86
  end
84
87
  end
85
88
  end
86
-
89
+
87
90
  describe '#gen_direct_action' do
88
-
91
+
92
+ let(:code) { ActionCode.new('|_| _.to_sym') }
93
+
89
94
  context 'when nested' do
90
95
  it 'generates nested matching code with a direct action' do
91
- nested_code {|g| g.gen_direct_action token, ActionCode.new('|_| _.to_sym') }.
96
+ nested_code {|g| g.gen_direct_action token, code }.
92
97
  should == (<<-CODE).strip
93
98
  begin
94
99
  (r = @scanner.scan(/\\w+/)) &&
@@ -97,10 +102,10 @@ end
97
102
  CODE
98
103
  end
99
104
  end
100
-
105
+
101
106
  context 'when top-level' do
102
107
  it 'generates top level matching code with a direct action' do
103
- top_level_code {|g| g.gen_direct_action token, ActionCode.new('|_| _.to_sym') }.
108
+ top_level_code {|g| g.gen_direct_action token, code }.
104
109
  should == (<<-CODE).strip
105
110
  (r = @scanner.scan(/\\w+/)) &&
106
111
  (r.to_sym)
@@ -108,16 +113,16 @@ end
108
113
  end
109
114
  end
110
115
  end
111
-
116
+
112
117
  describe '#gen_token' do
113
-
118
+
114
119
  context 'when nested' do
115
120
  it 'generates nested token matching code' do
116
121
  nested_code {|g| g.gen_token token }.
117
122
  should == '@scanner.scan(/\w+/)'
118
123
  end
119
124
  end
120
-
125
+
121
126
  context 'when top-level' do
122
127
  it 'generates top level token matching code' do
123
128
  top_level_code {|g| g.gen_token token }.
@@ -125,16 +130,16 @@ end
125
130
  end
126
131
  end
127
132
  end
128
-
133
+
129
134
  describe '#gen_skip' do
130
-
135
+
131
136
  context 'when nested' do
132
137
  it 'generates nested skipping code' do
133
138
  nested_code {|g| g.gen_skip token }.
134
139
  should == '(@scanner.skip(/\w+/) && true)'
135
140
  end
136
141
  end
137
-
142
+
138
143
  context 'when top-level' do
139
144
  it 'generates top level skipping code' do
140
145
  top_level_code {|g| g.gen_skip token }.
@@ -142,33 +147,33 @@ end
142
147
  end
143
148
  end
144
149
  end
145
-
150
+
146
151
  describe '#gen_intermediate' do
147
152
  it 'generates nested token matching code' do
148
153
  nested_code {|g| g.gen_intermediate token }.
149
154
  should == '@scanner.scan(/\w+/)'
150
155
  end
151
156
  end
152
-
157
+
153
158
  describe '#gen_intermediate_assert' do
154
159
  it 'generates intermediate positive lookahead code' do
155
160
  nested_code {|g| g.gen_intermediate_assert token }.
156
161
  should == '@scanner.skip(/(?=\w+)/)'
157
162
  end
158
163
  end
159
-
164
+
160
165
  describe '#gen_intermediate_disallow' do
161
166
  it 'generates intermediate negative lookahead code' do
162
167
  nested_code {|g| g.gen_intermediate_disallow token }.
163
168
  should == '@scanner.skip(/(?!\w+)/)'
164
169
  end
165
170
  end
166
-
171
+
167
172
  describe '#gen_intermediate_skip' do
168
173
  it 'generates intermediate skipping code' do
169
174
  nested_code {|g| g.gen_intermediate_skip token }.
170
175
  should == '@scanner.skip(/\w+/)'
171
176
  end
172
177
  end
173
-
178
+
174
179
  end
@@ -1,19 +1,20 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
2
 
3
+ include Rattler::BackEnd::ParserGenerator
3
4
  include Rattler::Parsers
4
5
 
5
- describe Rattler::BackEnd::ParserGenerator::ZeroOrMoreGenerator do
6
-
6
+ describe ZeroOrMoreGenerator do
7
+
7
8
  include ParserGeneratorSpecHelper
8
-
9
+
9
10
  let(:zero_or_more) { ZeroOrMore[Match[/w+/]] }
10
-
11
+
11
12
  describe '#gen_basic' do
12
-
13
+
13
14
  let :zero_or_more do
14
15
  ZeroOrMore[Choice[Match[/[[:alpha:]]/], Match[/[[:digit:]]/]]]
15
16
  end
16
-
17
+
17
18
  context 'when nested' do
18
19
  it 'generates nested zero-or-more matching code' do
19
20
  nested_code {|g| g.gen_basic zero_or_more }.
@@ -31,7 +32,7 @@ end
31
32
  CODE
32
33
  end
33
34
  end
34
-
35
+
35
36
  context 'when top-level' do
36
37
  it 'generates top level zero-or-more matching code' do
37
38
  top_level_code {|g| g.gen_basic zero_or_more }.
@@ -48,42 +49,44 @@ a
48
49
  end
49
50
  end
50
51
  end
51
-
52
+
52
53
  describe '#gen_assert' do
53
-
54
+
54
55
  context 'when nested' do
55
56
  it 'generates "true"' do
56
57
  nested_code {|g| g.gen_assert zero_or_more }.should == 'true'
57
58
  end
58
59
  end
59
-
60
+
60
61
  context 'when top-level' do
61
62
  it 'generates "true"' do
62
63
  top_level_code {|g| g.gen_assert zero_or_more }.should == 'true'
63
64
  end
64
65
  end
65
66
  end
66
-
67
+
67
68
  describe '#gen_disallow' do
68
-
69
+
69
70
  context 'when nested' do
70
71
  it 'generates "false"' do
71
72
  nested_code {|g| g.gen_disallow zero_or_more }.should == 'false'
72
73
  end
73
74
  end
74
-
75
+
75
76
  context 'when top-level' do
76
77
  it 'generates "false"' do
77
78
  top_level_code {|g| g.gen_disallow zero_or_more }.should == 'false'
78
79
  end
79
80
  end
80
81
  end
81
-
82
+
82
83
  describe '#gen_dispatch_action' do
83
-
84
+
85
+ let(:code) { NodeCode.new('Word', 'parsed') }
86
+
84
87
  context 'when nested' do
85
88
  it 'generates nested zero-or-more matching code with a dispatch action' do
86
- nested_code {|g| g.gen_dispatch_action zero_or_more, 'Word', 'parsed' }.
89
+ nested_code {|g| g.gen_dispatch_action zero_or_more, code }.
87
90
  should == (<<-CODE).strip
88
91
  begin
89
92
  a = []
@@ -95,10 +98,10 @@ end
95
98
  CODE
96
99
  end
97
100
  end
98
-
101
+
99
102
  context 'when top-level' do
100
103
  it 'generates top level zero-or-more matching code with a dispatch action' do
101
- top_level_code {|g| g.gen_dispatch_action zero_or_more, 'Word', 'parsed' }.
104
+ top_level_code {|g| g.gen_dispatch_action zero_or_more, code }.
102
105
  should == (<<-CODE).strip
103
106
  a = []
104
107
  while r = @scanner.scan(/w+/)
@@ -109,12 +112,14 @@ Word.parsed(select_captures(a))
109
112
  end
110
113
  end
111
114
  end
112
-
115
+
113
116
  describe '#gen_direct_action' do
114
-
117
+
118
+ let(:code) { ActionCode.new('|_| _.size') }
119
+
115
120
  context 'when nested' do
116
121
  it 'generates nested zero-or-more matching code with a direct action' do
117
- nested_code {|g| g.gen_direct_action zero_or_more, ActionCode.new('|_| _.size') }.
122
+ nested_code {|g| g.gen_direct_action zero_or_more, code }.
118
123
  should == (<<-CODE).strip
119
124
  begin
120
125
  a = []
@@ -126,10 +131,10 @@ end
126
131
  CODE
127
132
  end
128
133
  end
129
-
134
+
130
135
  context 'when top-level' do
131
136
  it 'generates top level zero-or-more matching code with a direct action' do
132
- top_level_code {|g| g.gen_direct_action zero_or_more, ActionCode.new('|_| _.size') }.
137
+ top_level_code {|g| g.gen_direct_action zero_or_more, code }.
133
138
  should == (<<-CODE).strip
134
139
  a = []
135
140
  while r = @scanner.scan(/w+/)
@@ -140,9 +145,9 @@ end
140
145
  end
141
146
  end
142
147
  end
143
-
148
+
144
149
  describe '#gen_token' do
145
-
150
+
146
151
  context 'when nested' do
147
152
  it 'generates nested zero-or-more matching code' do
148
153
  nested_code {|g| g.gen_token zero_or_more }.
@@ -157,7 +162,7 @@ end
157
162
  CODE
158
163
  end
159
164
  end
160
-
165
+
161
166
  context 'when top-level' do
162
167
  it 'generates top level zero-or-more matching code' do
163
168
  top_level_code {|g| g.gen_token zero_or_more }.
@@ -171,9 +176,9 @@ a.join
171
176
  end
172
177
  end
173
178
  end
174
-
179
+
175
180
  describe '#gen_skip' do
176
-
181
+
177
182
  context 'when nested' do
178
183
  it 'generates nested zero-or-more skipping code' do
179
184
  nested_code {|g| g.gen_skip zero_or_more }.
@@ -185,7 +190,7 @@ end
185
190
  CODE
186
191
  end
187
192
  end
188
-
193
+
189
194
  context 'when top-level' do
190
195
  it 'generates top level zero-or-more skipping code' do
191
196
  top_level_code {|g| g.gen_skip zero_or_more }.
@@ -196,7 +201,7 @@ true
196
201
  end
197
202
  end
198
203
  end
199
-
204
+
200
205
  describe '#gen_intermediate' do
201
206
  it 'generates nested zero-or-more matching code' do
202
207
  nested_code {|g| g.gen_intermediate zero_or_more }.
@@ -211,19 +216,19 @@ end
211
216
  CODE
212
217
  end
213
218
  end
214
-
219
+
215
220
  describe '#gen_intermediate_assert' do
216
221
  it 'generates "true"' do
217
222
  nested_code {|g| g.gen_intermediate_assert zero_or_more }.should == 'true'
218
223
  end
219
224
  end
220
-
225
+
221
226
  describe '#gen_intermediate_disallow' do
222
227
  it 'generates "false"' do
223
228
  nested_code {|g| g.gen_intermediate_disallow zero_or_more }.should == 'false'
224
229
  end
225
230
  end
226
-
231
+
227
232
  describe '#gen_intermediate_skip' do
228
233
  it 'generates nested zero-or-more skipping code' do
229
234
  nested_code {|g| g.gen_intermediate_skip zero_or_more }.
@@ -235,5 +240,5 @@ end
235
240
  CODE
236
241
  end
237
242
  end
238
-
243
+
239
244
  end
@@ -0,0 +1,885 @@
1
+ shared_examples_for 'a compiled parser' do
2
+ include CompilerSpecHelper
3
+
4
+ subject do
5
+ described_class.compile_parser(compiled_parser_base, grammar)
6
+ end
7
+
8
+ let :reference_parser do
9
+ Rattler::Parsers::CombinatorParser.as_class(grammar.rules.first, grammar.rules)
10
+ end
11
+
12
+ let(:compiled_parser_base) { Rattler::Runtime::RecursiveDescentParser }
13
+
14
+ ########## match ##########
15
+ context 'with a match rule' do
16
+ let(:grammar) { define_grammar do
17
+ rule(:digit) { match /\d/ }
18
+ end }
19
+ it { should parse('451').succeeding.twice.like reference_parser }
20
+ it { should parse(' 4').failing.like reference_parser }
21
+ it { should parse('foo').failing.like reference_parser }
22
+ end
23
+
24
+ ########## choice ##########
25
+ context 'with a choice rule' do
26
+ let(:grammar) { define_grammar do
27
+ rule(:atom) { match(/[[:alpha:]]+/) | match(/[[:digit:]]+/) }
28
+ end }
29
+ it { should parse('abc123').succeeding.twice.like reference_parser }
30
+ it { should parse('==').failing.like reference_parser }
31
+
32
+ context 'with nested choices' do
33
+ let(:grammar) { define_grammar do
34
+ rule(:foo) do
35
+ ( (match('a') | match('b')) \
36
+ | (match('c') | match('d')) )
37
+ end
38
+ end }
39
+ it { should parse('abcd').succeeding(4).times.like reference_parser }
40
+ it { should parse('123').failing.like reference_parser }
41
+ end
42
+
43
+ context 'with nested sequences' do
44
+ let(:grammar) { define_grammar do
45
+ rule(:foo) do
46
+ ( match('a') & match('b') \
47
+ | match('c') & match('d') )
48
+ end
49
+ end }
50
+ it { should parse('abcd').succeeding.twice.like reference_parser }
51
+ it { should parse('123').failing.like reference_parser }
52
+ end
53
+
54
+ context 'with nested optional parsers' do
55
+ let(:grammar) { define_grammar do
56
+ rule(:foo) do
57
+ optional('a') | optional('b')
58
+ end
59
+ end }
60
+ it { should parse('abcd').succeeding(3).times.like reference_parser }
61
+ it { should parse('123').succeeding.like reference_parser }
62
+ end
63
+ end
64
+
65
+ ########## sequence ##########
66
+ context 'with a sequence rule' do
67
+ let(:grammar) { define_grammar do
68
+ rule :assignment do
69
+ match(/[[:alpha:]]+/) & match('=') & match(/[[:digit:]]+/)
70
+ end
71
+ end }
72
+ it { should parse('val=42 ').succeeding.like reference_parser }
73
+ it { should parse('val=x ').failing.like reference_parser }
74
+
75
+ context 'with non-capturing parsers' do
76
+ let(:grammar) { define_grammar do
77
+ rule :foo do
78
+ match(/[[:alpha:]]+/) & skip(/\s+/) & match(/[[:digit:]]+/)
79
+ end
80
+ end }
81
+ it { should parse('foo 42').succeeding.like reference_parser }
82
+ end
83
+
84
+ context 'with only one capturing parser' do
85
+ let(:grammar) { define_grammar do
86
+ rule :foo do
87
+ skip(/\s+/) & match(/\w+/)
88
+ end
89
+ end }
90
+ it { should parse(' abc123').succeeding.like reference_parser }
91
+ end
92
+
93
+ context 'with no capturing parsers' do
94
+ let(:grammar) { define_grammar do
95
+ rule :foo do
96
+ skip(/\s*/) & skip(/#[^\n]+/)
97
+ end
98
+ end }
99
+ it { should parse(' # foo').succeeding.like reference_parser }
100
+ end
101
+
102
+ context 'with an apply referencing a non-capturing rule' do
103
+ let(:grammar) { define_grammar do
104
+ rule :foo do
105
+ match(/[[:alpha:]]+/) & match(:ws) & match(/[[:digit:]]+/)
106
+ end
107
+ rule :ws do
108
+ skip(/\s+/)
109
+ end
110
+ end }
111
+ it { should parse('foo 42').succeeding.like reference_parser }
112
+ end
113
+ end
114
+
115
+ ########## optional ##########
116
+ context 'with an optional rule' do
117
+ let(:grammar) { define_grammar do
118
+ rule :foo do
119
+ optional(/\w+/)
120
+ end
121
+ end }
122
+ it { should parse('foo ').succeeding.like reference_parser }
123
+ it { should parse(' ').succeeding.like reference_parser }
124
+
125
+ context 'with a non-capturing parser' do
126
+ let(:grammar) { define_grammar do
127
+ rule :foo do
128
+ optional(skip(/\w+/))
129
+ end
130
+ end }
131
+ it { should parse('foo ').succeeding.like reference_parser }
132
+ it { should parse(' ').succeeding.like reference_parser }
133
+ end
134
+ end
135
+
136
+ ########## zero-or-more ##########
137
+ context 'with an zero-or-more rule' do
138
+ let(:grammar) { define_grammar do
139
+ rule :foo do
140
+ zero_or_more(/\w/)
141
+ end
142
+ end }
143
+ it { should parse('foo ').succeeding.like reference_parser }
144
+ it { should parse(' ').succeeding.like reference_parser }
145
+
146
+ context 'with a non-capturing parser' do
147
+ let(:grammar) { define_grammar do
148
+ rule :foo do
149
+ zero_or_more(skip(/\w/))
150
+ end
151
+ end }
152
+ it { should parse('foo ').succeeding.like reference_parser }
153
+ it { should parse(' ').succeeding.like reference_parser }
154
+ end
155
+ end
156
+
157
+ ########## one-or-more ##########
158
+ context 'with an one-or-more rule' do
159
+ let(:grammar) { define_grammar do
160
+ rule :foo do
161
+ one_or_more(/\w/)
162
+ end
163
+ end }
164
+ it { should parse('foo ').succeeding.like reference_parser }
165
+ it { should parse(' ').failing.like reference_parser }
166
+
167
+ context 'with a non-capturing parser' do
168
+ let(:grammar) { define_grammar do
169
+ rule :foo do
170
+ one_or_more(skip(/\w/))
171
+ end
172
+ end }
173
+ it { should parse('foo ').succeeding.like reference_parser }
174
+ it { should parse(' ').failing.like reference_parser }
175
+ end
176
+ end
177
+
178
+ ########## list ##########
179
+ context 'given a list rule' do
180
+ let(:grammar) { define_grammar do
181
+ rule :foo do
182
+ list(/\w+/, /[,;]/)
183
+ end
184
+ end }
185
+ it { should parse(' ').succeeding.like reference_parser }
186
+ it { should parse('foo ').succeeding.like reference_parser }
187
+ it { should parse('foo,bar;baz ').succeeding.like reference_parser }
188
+ it { should parse('foo,bar, ').succeeding.like reference_parser }
189
+
190
+ context 'with a non-capturing parser' do
191
+ let(:grammar) { define_grammar do
192
+ rule :foo do
193
+ list(skip(/\w+/), /[,;]/)
194
+ end
195
+ end }
196
+ it { should parse(' ').succeeding.like reference_parser }
197
+ it { should parse('foo ').succeeding.like reference_parser }
198
+ it { should parse('foo,bar;baz ').succeeding.like reference_parser }
199
+ it { should parse('foo,bar, ').succeeding.like reference_parser }
200
+ end
201
+ end
202
+
203
+ ########## list1 ##########
204
+ context 'given a list1 rule' do
205
+ let(:grammar) { define_grammar do
206
+ rule :foo do
207
+ list1(/\w+/, /[,;]/)
208
+ end
209
+ end }
210
+ it { should parse(' ').failing.like reference_parser }
211
+ it { should parse('foo ').succeeding.like reference_parser }
212
+ it { should parse('foo,bar;baz ').succeeding.like reference_parser }
213
+ it { should parse('foo,bar, ').succeeding.like reference_parser }
214
+
215
+ context 'with a non-capturing parser' do
216
+ let(:grammar) { define_grammar do
217
+ rule :foo do
218
+ list1(skip(/\w+/), /[,;]/)
219
+ end
220
+ end }
221
+ it { should parse(' ').failing.like reference_parser }
222
+ it { should parse('foo ').succeeding.like reference_parser }
223
+ it { should parse('foo,bar;baz ').succeeding.like reference_parser }
224
+ it { should parse('foo,bar, ').succeeding.like reference_parser }
225
+ end
226
+ end
227
+
228
+ ########## apply ##########
229
+ context 'given an apply rule' do
230
+ let(:grammar) { define_grammar do
231
+ rule(:digit) { match /\d/ }
232
+ rule(:foo) { match :digit }
233
+ end }
234
+ it { should parse('451 ').twice.succeeding.like reference_parser }
235
+ it { should parse('hi').failing.like reference_parser }
236
+ end
237
+
238
+ ########## assert ##########
239
+ context 'given an assert rule' do
240
+
241
+ context 'with a nested match rule' do
242
+ let(:grammar) { define_grammar do
243
+ rule(:word) { assert /\w+/ }
244
+ end }
245
+ it { should parse('abc123 ').succeeding.like reference_parser }
246
+ it { should parse(' ').failing.like reference_parser }
247
+ end
248
+
249
+ context 'with a nested choice rule' do
250
+ let(:grammar) { define_grammar do
251
+ rule(:word) { assert(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/)) }
252
+ end }
253
+ it { should parse('abc123 ').succeeding.like reference_parser }
254
+ it { should parse(' ').failing.like reference_parser }
255
+ end
256
+
257
+ context 'with a nested sequence rule' do
258
+ let(:grammar) { define_grammar do
259
+ rule(:word) { assert(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/)) }
260
+ end }
261
+ it { should parse('abc123 ').succeeding.like reference_parser }
262
+ it { should parse(' ').failing.like reference_parser }
263
+ end
264
+
265
+ context 'with a nested optional rule' do
266
+ let(:grammar) { define_grammar do
267
+ rule(:word) { assert(optional(/\w+/)) }
268
+ end }
269
+ it { should parse('abc123 ').succeeding.like reference_parser }
270
+ it { should parse(' ').succeeding.like reference_parser }
271
+ end
272
+
273
+ context 'with a nested zero_or_more rule' do
274
+ let(:grammar) { define_grammar do
275
+ rule(:word) { assert(zero_or_more(/\w/)) }
276
+ end }
277
+ it { should parse('abc123 ').succeeding.like reference_parser }
278
+ it { should parse(' ').succeeding.like reference_parser }
279
+ end
280
+
281
+ context 'with a nested one_or_more rule' do
282
+ let(:grammar) { define_grammar do
283
+ rule(:word) { assert(one_or_more(/\w/)) }
284
+ end }
285
+ it { should parse('abc123 ').succeeding.like reference_parser }
286
+ it { should parse(' ').failing.like reference_parser }
287
+ end
288
+
289
+ context 'with a nested list rule' do
290
+ let(:grammar) { define_grammar do
291
+ rule(:word) { assert(list(/\w+/, /,/)) }
292
+ end }
293
+ it { should parse('abc,123 ').succeeding.like reference_parser }
294
+ it { should parse(' ').succeeding.like reference_parser }
295
+ end
296
+
297
+ context 'with a nested list1 rule' do
298
+ let(:grammar) { define_grammar do
299
+ rule(:word) { assert(list1(/\w+/, /,/)) }
300
+ end }
301
+ it { should parse('abc,123 ').succeeding.like reference_parser }
302
+ it { should parse(' ').failing.like reference_parser }
303
+ end
304
+
305
+ context 'with a nested apply rule' do
306
+ let(:grammar) { define_grammar do
307
+ rule(:foo) { match :word }
308
+ rule(:word) { assert /\w+/ }
309
+ end }
310
+ it { should parse('abc123 ').succeeding.like reference_parser }
311
+ it { should parse(' ').failing.like reference_parser }
312
+ end
313
+
314
+ context 'with a nested dispatch-action rule' do
315
+ let(:grammar) { define_grammar do
316
+ rule(:word) { assert(dispatch_action(/\w+/)) }
317
+ end }
318
+ it { should parse('abc123 ').succeeding.like reference_parser }
319
+ it { should parse(' ').failing.like reference_parser }
320
+ end
321
+
322
+ context 'with a nested token rule' do
323
+ let(:grammar) { define_grammar do
324
+ rule(:word) { assert(token(match(/\w+/))) }
325
+ end }
326
+ it { should parse('abc123 ').succeeding.like reference_parser }
327
+ it { should parse(' ').failing.like reference_parser }
328
+ end
329
+
330
+ context 'with a nested skip rule' do
331
+ let(:grammar) { define_grammar do
332
+ rule(:word) { assert(skip(/\w+/)) }
333
+ end }
334
+ it { should parse('abc123 ').succeeding.like reference_parser }
335
+ it { should parse(' ').failing.like reference_parser }
336
+ end
337
+ end
338
+
339
+ ########## disallow ##########
340
+ context 'given a disallow rule' do
341
+
342
+ context 'with a nested match rule' do
343
+ let(:grammar) { define_grammar do
344
+ rule(:word) { disallow /\w+/ }
345
+ end }
346
+ it { should parse(' ').succeeding.like reference_parser }
347
+ it { should parse('abc123 ').failing.like reference_parser }
348
+ end
349
+
350
+ context 'with a nested choice rule' do
351
+ let(:grammar) { define_grammar do
352
+ rule(:word) { disallow(match(/[[:alpha:]]/) | match(/[[:digit:]]/)) }
353
+ end }
354
+ it { should parse(' ').succeeding.like reference_parser }
355
+ it { should parse('abc123 ').failing.like reference_parser }
356
+ end
357
+
358
+ context 'with a nested sequence rule' do
359
+ let(:grammar) { define_grammar do
360
+ rule(:word) { disallow(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/)) }
361
+ end }
362
+ it { should parse(' ').succeeding.like reference_parser }
363
+ it { should parse('abc123 ').failing.like reference_parser }
364
+ end
365
+
366
+ context 'with a nested optional rule' do
367
+ let(:grammar) { define_grammar do
368
+ rule(:word) { disallow(optional(/\w+/)) }
369
+ end }
370
+ it { should parse(' ').failing.like reference_parser }
371
+ it { should parse('abc123 ').failing.like reference_parser }
372
+ end
373
+
374
+ context 'with a nested zero_or_more rule' do
375
+ let(:grammar) { define_grammar do
376
+ rule(:word) { disallow(zero_or_more(/\w/)) }
377
+ end }
378
+ it { should parse(' ').failing.like reference_parser }
379
+ it { should parse('abc123 ').failing.like reference_parser }
380
+ end
381
+
382
+ context 'with a nested one_or_more rule' do
383
+ let(:grammar) { define_grammar do
384
+ rule(:word) { disallow(one_or_more(/\w/)) }
385
+ end }
386
+ it { should parse(' ').succeeding.like reference_parser }
387
+ it { should parse('abc123 ').failing.like reference_parser }
388
+ end
389
+
390
+ context 'with a nested list rule' do
391
+ let(:grammar) { define_grammar do
392
+ rule(:word) { disallow(list(/\w+/, /,/)) }
393
+ end }
394
+ it { should parse(' ').failing.like reference_parser }
395
+ it { should parse('abc,123 ').failing.like reference_parser }
396
+ end
397
+
398
+ context 'with a nested list1 rule' do
399
+ let(:grammar) { define_grammar do
400
+ rule(:word) { disallow(list1(/\w+/, /,/)) }
401
+ end }
402
+ it { should parse(' ').succeeding.like reference_parser }
403
+ it { should parse('abc,123 ').failing.like reference_parser }
404
+ end
405
+
406
+ context 'with a nested apply rule' do
407
+ let(:grammar) { define_grammar do
408
+ rule(:foo) { match :word }
409
+ rule(:word) { disallow /\w+/ }
410
+ end }
411
+ it { should parse(' ').succeeding.like reference_parser }
412
+ it { should parse('abc123 ').failing.like reference_parser }
413
+ end
414
+
415
+ context 'with a nested dispatch-action rule' do
416
+ let(:grammar) { define_grammar do
417
+ rule(:word) { disallow(dispatch_action(/\w+/)) }
418
+ end }
419
+ it { should parse(' ').succeeding.like reference_parser }
420
+ it { should parse('abc123 ').failing.like reference_parser }
421
+ end
422
+
423
+ context 'with a nested token rule' do
424
+ let(:grammar) { define_grammar do
425
+ rule(:word) { disallow(token(match(/\w+/))) }
426
+ end }
427
+ it { should parse(' ').succeeding.like reference_parser }
428
+ it { should parse('abc123 ').failing.like reference_parser }
429
+ end
430
+
431
+ context 'with a nested skip rule' do
432
+ let(:grammar) { define_grammar do
433
+ rule(:word) { disallow(skip(/\w+/)) }
434
+ end }
435
+ it { should parse(' ').succeeding.like reference_parser }
436
+ it { should parse('abc123 ').failing.like reference_parser }
437
+ end
438
+ end
439
+
440
+ ########## dispatch_action ##########
441
+ context 'given a dispatch-action rule' do
442
+
443
+ context 'with a nested match rule' do
444
+ let(:grammar) { define_grammar do
445
+ rule(:digits) { dispatch_action(/\d+/) }
446
+ end }
447
+ it { should parse('451a').succeeding.like reference_parser }
448
+ it { should parse(' ').failing.like reference_parser }
449
+ end
450
+
451
+ context 'with a nested choice rule' do
452
+ let(:grammar) { define_grammar do
453
+ rule :atom do
454
+ dispatch_action(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
455
+ end
456
+ end }
457
+
458
+ it { should parse('451a').succeeding.like reference_parser }
459
+ it { should parse(' ').failing.like reference_parser }
460
+ end
461
+
462
+ context 'with a nested sequence rule' do
463
+ let(:grammar) { define_grammar do
464
+ rule :assignment do
465
+ dispatch_action(
466
+ match(/[[:alpha:]]+/) &
467
+ match('=') &
468
+ match(/[[:digit:]]+/)
469
+ )
470
+ end
471
+ end }
472
+
473
+ it { should parse('val=42 ').succeeding.like reference_parser }
474
+ it { should parse('val=x').failing.like reference_parser }
475
+
476
+ context 'with labels' do
477
+ let(:grammar) { define_grammar do
478
+ rule :assignment do
479
+ dispatch_action(
480
+ label(:name, /[[:alpha:]]+/) &
481
+ match('=') &
482
+ label(:value, /[[:digit:]]+/)
483
+ )
484
+ end
485
+ end }
486
+ it { should parse('val=42 ').succeeding.like reference_parser }
487
+ end
488
+ end
489
+
490
+ context 'with a nested optional rule' do
491
+ let(:grammar) { define_grammar do
492
+ rule :foo do
493
+ dispatch_action(optional(/\w+/))
494
+ end
495
+ end }
496
+ it { should parse('foo ').succeeding.like reference_parser }
497
+ it { should parse(' ').succeeding.like reference_parser }
498
+ end
499
+
500
+ context 'with a nested zero-or-more rule' do
501
+ let(:grammar) { define_grammar do
502
+ rule :foo do
503
+ dispatch_action(zero_or_more(/\w/))
504
+ end
505
+ end }
506
+ it { should parse('foo ').succeeding.like reference_parser }
507
+ it { should parse(' ').succeeding.like reference_parser }
508
+ end
509
+
510
+ context 'with a nested one-or-more rule' do
511
+ let(:grammar) { define_grammar do
512
+ rule :foo do
513
+ dispatch_action(one_or_more(/\w/))
514
+ end
515
+ end }
516
+ it { should parse('foo ').succeeding.like reference_parser }
517
+ it { should parse(' ').failing.like reference_parser }
518
+ end
519
+
520
+ context 'with a nested apply rule' do
521
+ let(:grammar) { define_grammar do
522
+ rule(:digit) { match /\d/ }
523
+ rule(:foo) { dispatch_action :digit }
524
+ end }
525
+ it { should parse('451a').succeeding.twice.like reference_parser }
526
+ it { should parse(' ').failing.like reference_parser }
527
+ end
528
+
529
+ context 'with a nested token rule' do
530
+ let(:grammar) { define_grammar do
531
+ rule :foo do
532
+ dispatch_action(token(match(/\w+/)))
533
+ end
534
+ end }
535
+ it { should parse('abc123').succeeding.like reference_parser }
536
+ it { should parse(' ').failing.like reference_parser }
537
+ end
538
+
539
+ context 'with a nested skip rule' do
540
+ let(:grammar) { define_grammar do
541
+ rule :foo do
542
+ dispatch_action(skip(/\w+/))
543
+ end
544
+ end }
545
+ it { should parse('abc123').succeeding.like reference_parser }
546
+ it { should parse(' ').failing.like reference_parser }
547
+ end
548
+ end
549
+
550
+ ########## direct_action ##########
551
+ context 'given a direct-action rule' do
552
+
553
+ context 'with a nested match rule' do
554
+ let(:grammar) { define_grammar do
555
+ rule(:num) { direct_action(/\d+/, '|_| _.to_i') }
556
+ end }
557
+ it { should parse('451a').succeeding.like reference_parser }
558
+ it { should parse(' ').failing.like reference_parser }
559
+ end
560
+
561
+ context 'with a nested choice rule' do
562
+ let(:grammar) { define_grammar do
563
+ rule :foo do
564
+ direct_action(
565
+ match(/[[:alpha:]]+/) | match(/[[:digit:]]+/),
566
+ '|_| _.size'
567
+ )
568
+ end
569
+ end }
570
+
571
+ it { should parse('abc123').succeeding.like reference_parser }
572
+ it { should parse('451a').succeeding.like reference_parser }
573
+ it { should parse(' ').failing.like reference_parser }
574
+ end
575
+
576
+ context 'with a nested sequence rule' do
577
+ let(:grammar) { define_grammar do
578
+ rule :assignment do
579
+ direct_action(
580
+ match(/[[:alpha:]]+/) & match('=') & match(/[[:digit:]]+/),
581
+ '|l,_,r| "#{r} -> #{l}"'
582
+ )
583
+ end
584
+ end }
585
+
586
+ it { should parse('val=42 ').succeeding.like reference_parser }
587
+ it { should parse('val=x').failing.like reference_parser }
588
+
589
+ context 'with labels' do
590
+ let(:grammar) { define_grammar do
591
+ rule :assignment do
592
+ direct_action(
593
+ label(:name, /[[:alpha:]]+/) & match('=') & label(:value, /[[:digit:]]+/),
594
+ '"#{value} -> #{name}"'
595
+ )
596
+ end
597
+ end }
598
+ it { should parse('val=42 ').succeeding.like reference_parser }
599
+ end
600
+ end
601
+
602
+ context 'with a nested optional rule' do
603
+ let(:grammar) { define_grammar do
604
+ rule :foo do
605
+ direct_action(optional(/\w+/), '|_| _.size')
606
+ end
607
+ end }
608
+ it { should parse('foo ').succeeding.like reference_parser }
609
+ it { should parse(' ').succeeding.like reference_parser }
610
+ end
611
+
612
+ context 'with a nested zero-or-more rule' do
613
+ let(:grammar) { define_grammar do
614
+ rule :foo do
615
+ direct_action(zero_or_more(/\w/), '|_| _.size')
616
+ end
617
+ end }
618
+ it { should parse('foo ').succeeding.like reference_parser }
619
+ it { should parse(' ').succeeding.like reference_parser }
620
+ end
621
+
622
+ context 'with a nested one-or-more rule' do
623
+ let(:grammar) { define_grammar do
624
+ rule :foo do
625
+ direct_action(one_or_more(/\w/), '|_| _.size')
626
+ end
627
+ end }
628
+ it { should parse('foo ').succeeding.like reference_parser }
629
+ it { should parse(' ').failing.like reference_parser }
630
+ end
631
+
632
+ context 'with a nested apply rule' do
633
+ let(:grammar) { define_grammar do
634
+ rule(:digit) { match /\d/ }
635
+ rule(:foo) { direct_action :digit, '|_| _.to_i' }
636
+ end }
637
+ it { should parse('451a').succeeding.twice.like reference_parser }
638
+ it { should parse(' ').failing.like reference_parser }
639
+ end
640
+
641
+ context 'with a nested token rule' do
642
+ let(:grammar) { define_grammar do
643
+ rule :foo do
644
+ direct_action(token(/\w+/), '|_| _.size')
645
+ end
646
+ end }
647
+ it { should parse('abc123').succeeding.like reference_parser }
648
+ it { should parse(' ').failing.like reference_parser }
649
+ end
650
+
651
+ context 'with a nested skip rule' do
652
+ let(:grammar) { define_grammar do
653
+ rule :foo do
654
+ direct_action(skip(/\w+/), '42' )
655
+ end
656
+ end }
657
+ it { should parse('abc123').succeeding.like reference_parser }
658
+ it { should parse(' ').failing.like reference_parser }
659
+ end
660
+ end
661
+
662
+ ########## token ##########
663
+ context 'given a token rule' do
664
+
665
+ context 'with a nested match rule' do
666
+ let(:grammar) { define_grammar do
667
+ rule(:digits) { token(match(/\d+/)) }
668
+ end }
669
+ it { should parse('451a').succeeding.like reference_parser }
670
+ it { should parse('hi').failing.like reference_parser }
671
+ end
672
+
673
+ context 'with a nested choice rule' do
674
+ let(:grammar) { define_grammar do
675
+ rule(:atom) do
676
+ token(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
677
+ end
678
+ end }
679
+
680
+ it { should parse('abc123 ').succeeding.like reference_parser }
681
+ it { should parse('==').failing.like reference_parser }
682
+
683
+ context 'with non-capturing choices' do
684
+ let(:grammar) { define_grammar do
685
+ rule(:atom) do
686
+ token(skip(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
687
+ end
688
+ end }
689
+ it { should parse('abc123 ').succeeding.like reference_parser }
690
+ it { should parse('==').failing.like reference_parser }
691
+ end
692
+ end
693
+
694
+ context 'with a nested sequence rule' do
695
+ let(:grammar) { define_grammar do
696
+ rule(:atom) do
697
+ token(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/))
698
+ end
699
+ end }
700
+
701
+ it { should parse('foo42!').succeeding.like reference_parser }
702
+ it { should parse('val=x').failing.like reference_parser }
703
+
704
+ context 'with non-capturing parsers' do
705
+ let(:grammar) { define_grammar do
706
+ rule :foo do
707
+ token(match(/[[:alpha:]]+/) & skip(/\s+/) & match(/[[:digit:]]+/))
708
+ end
709
+ end }
710
+ it { should parse('foo 42').succeeding.like reference_parser }
711
+ it { should parse('foo bar').failing.like reference_parser }
712
+ end
713
+ end
714
+
715
+ context 'with a nested optional rule' do
716
+ let(:grammar) { define_grammar do
717
+ rule :foo do
718
+ token(optional(/\w+/))
719
+ end
720
+ end }
721
+
722
+ it { should parse('foo ').succeeding.like reference_parser }
723
+ it { should parse(' ').succeeding.like reference_parser }
724
+
725
+ context 'with a non-capturing rule' do
726
+ let(:grammar) { define_grammar do
727
+ rule :foo do
728
+ token(optional(skip(/\w+/)))
729
+ end
730
+ end }
731
+ it { should parse('foo ').succeeding.like reference_parser }
732
+ it { should parse(' ').succeeding.like reference_parser }
733
+ end
734
+ end
735
+
736
+ context 'with a nested zero-or-more rule' do
737
+ let(:grammar) { define_grammar do
738
+ rule :foo do
739
+ token(zero_or_more(/\w/))
740
+ end
741
+ end }
742
+
743
+ it { should parse('foo ').succeeding.like reference_parser }
744
+ it { should parse(' ').succeeding.like reference_parser }
745
+
746
+ context 'with a non-capturing rule' do
747
+ let(:grammar) { define_grammar do
748
+ rule :foo do
749
+ token(zero_or_more(skip(/\w/)))
750
+ end
751
+ end }
752
+ it { should parse('foo ').succeeding.like reference_parser }
753
+ it { should parse(' ').succeeding.like reference_parser }
754
+ end
755
+ end
756
+
757
+ context 'with a nested one-or-more rule' do
758
+ let(:grammar) { define_grammar do
759
+ rule :foo do
760
+ token(one_or_more(/\w/))
761
+ end
762
+ end }
763
+
764
+ it { should parse('foo ').succeeding.like reference_parser }
765
+ it { should parse(' ').failing.like reference_parser }
766
+
767
+ context 'with a non-capturing rule' do
768
+ let(:grammar) { define_grammar do
769
+ rule :foo do
770
+ token(one_or_more(skip(/\w/)))
771
+ end
772
+ end }
773
+ it { should parse('foo ').succeeding.like reference_parser }
774
+ it { should parse(' ').failing.like reference_parser }
775
+ end
776
+ end
777
+
778
+ context 'with a nested apply rule' do
779
+ let(:grammar) { define_grammar do
780
+ rule(:foo) { token(match(:digits)) }
781
+ rule(:digits) { match(/\d+/) }
782
+ end }
783
+
784
+ it { should parse('451a').succeeding.like reference_parser }
785
+ it { should parse('hi').failing.like reference_parser }
786
+
787
+ context 'applying a non-capturing rule' do
788
+ let(:grammar) { define_grammar do
789
+ rule(:foo) { token(match(:digits)) }
790
+ rule(:digits) { skip(/\d+/) }
791
+ end }
792
+ it { should parse('451a').succeeding.like reference_parser }
793
+ it { should parse('hi').failing.like reference_parser }
794
+ end
795
+ end
796
+
797
+ context 'with a nested dispatch-action rule' do
798
+ let(:grammar) { define_grammar do
799
+ rule(:foo) { token(dispatch_action(/\w+/)) }
800
+ end }
801
+ it { should parse('abc123').succeeding.like reference_parser }
802
+ it { should parse(' ').failing.like reference_parser }
803
+ end
804
+
805
+ context 'with a nested skip rule' do
806
+ let(:grammar) { define_grammar do
807
+ rule(:foo) { token(skip(/\w+/)) }
808
+ end }
809
+ it { should parse('abc123').succeeding.like reference_parser }
810
+ it { should parse(' ').failing.like reference_parser }
811
+ end
812
+ end
813
+
814
+ ########## skip ##########
815
+ context 'given a skip rule' do
816
+
817
+ context 'with a nested match rule' do
818
+ let(:grammar) { define_grammar do
819
+ rule(:ws) { skip(/\s+/) }
820
+ end }
821
+ it { should parse(' foo').succeeding.like reference_parser }
822
+ it { should parse('hi').failing.like reference_parser }
823
+ end
824
+
825
+ context 'with a nested choice rule' do
826
+ let(:grammar) { define_grammar do
827
+ rule(:ws) do
828
+ skip(match(/\s+/) | match(/\#[^\n]*/))
829
+ end
830
+ end }
831
+ it { should parse(' # hi there ').succeeding.twice.like reference_parser }
832
+ it { should parse('hi').failing.like reference_parser }
833
+ end
834
+
835
+ context 'with a nested sequence rule' do
836
+ let(:grammar) { define_grammar do
837
+ rule :foo do
838
+ skip(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/))
839
+ end
840
+ end }
841
+ it { should parse('foo42!').succeeding.like reference_parser }
842
+ it { should parse('val=x').failing.like reference_parser }
843
+ end
844
+
845
+ context 'with a nested optional rule' do
846
+ let(:grammar) { define_grammar do
847
+ rule :foo do
848
+ skip(optional(/\w+/))
849
+ end
850
+ end }
851
+ it { should parse('foo ').succeeding.like reference_parser }
852
+ it { should parse(' ').succeeding.like reference_parser }
853
+ end
854
+
855
+ context 'with a nested zero-or-more rule' do
856
+ let(:grammar) { define_grammar do
857
+ rule :foo do
858
+ skip(zero_or_more(/\w/))
859
+ end
860
+ end }
861
+ it { should parse('foo ').succeeding.like reference_parser }
862
+ it { should parse(' ').succeeding.like reference_parser }
863
+ end
864
+
865
+ context 'with a nested one-or-more rule' do
866
+ let(:grammar) { define_grammar do
867
+ rule :foo do
868
+ skip(one_or_more(/\w/))
869
+ end
870
+ end }
871
+ it { should parse('foo ').succeeding.like reference_parser }
872
+ it { should parse(' ').failing.like reference_parser }
873
+ end
874
+
875
+ context 'with a nested apply rule' do
876
+ let(:grammar) { define_grammar do
877
+ rule(:digits) { match(/\d+/) }
878
+ rule(:foo) { skip(:digits) }
879
+ end }
880
+ it { should parse('451a').succeeding.like reference_parser }
881
+ it { should parse('hi').failing.like reference_parser }
882
+ end
883
+ end
884
+
885
+ end