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
@@ -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