rattler 0.2.2 → 0.3.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 (56) hide show
  1. data/README.rdoc +83 -64
  2. data/features/grammar/comments.feature +24 -0
  3. data/features/grammar/list_matching.feature +41 -0
  4. data/features/grammar/symantic_action.feature +30 -12
  5. data/lib/rattler/back_end/parser_generator/assert_generator.rb +27 -27
  6. data/lib/rattler/back_end/parser_generator/choice_generator.rb +29 -29
  7. data/lib/rattler/back_end/parser_generator/direct_action_generator.rb +17 -17
  8. data/lib/rattler/back_end/parser_generator/disallow_generator.rb +27 -27
  9. data/lib/rattler/back_end/parser_generator/dispatch_action_generator.rb +17 -17
  10. data/lib/rattler/back_end/parser_generator/expr_generator.rb +129 -40
  11. data/lib/rattler/back_end/parser_generator/label_generator.rb +15 -15
  12. data/lib/rattler/back_end/parser_generator/list1_generator.rb +61 -0
  13. data/lib/rattler/back_end/parser_generator/list_generating.rb +71 -0
  14. data/lib/rattler/back_end/parser_generator/list_generator.rb +57 -0
  15. data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +14 -15
  16. data/lib/rattler/back_end/parser_generator/optional_generator.rb +24 -24
  17. data/lib/rattler/back_end/parser_generator/predicate_propogating.rb +9 -9
  18. data/lib/rattler/back_end/parser_generator/repeat_generating.rb +16 -16
  19. data/lib/rattler/back_end/parser_generator/sequence_generator.rb +40 -40
  20. data/lib/rattler/back_end/parser_generator/skip_generator.rb +18 -18
  21. data/lib/rattler/back_end/parser_generator/skip_propogating.rb +5 -5
  22. data/lib/rattler/back_end/parser_generator/sub_generating.rb +128 -0
  23. data/lib/rattler/back_end/parser_generator/token_generator.rb +15 -15
  24. data/lib/rattler/back_end/parser_generator/token_propogating.rb +1 -1
  25. data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +12 -13
  26. data/lib/rattler/back_end/parser_generator.rb +10 -7
  27. data/lib/rattler/grammar/grammar_parser.rb +16 -21
  28. data/lib/rattler/grammar/metagrammar.rb +1039 -1035
  29. data/lib/rattler/grammar/rattler.rtlr +28 -28
  30. data/lib/rattler/parsers/action_code.rb +20 -9
  31. data/lib/rattler/parsers/fail.rb +7 -1
  32. data/lib/rattler/parsers/list.rb +57 -0
  33. data/lib/rattler/parsers/list1.rb +58 -0
  34. data/lib/rattler/parsers/parser_dsl.rb +60 -38
  35. data/lib/rattler/parsers.rb +5 -3
  36. data/lib/rattler/runtime/extended_packrat_parser.rb +88 -20
  37. data/lib/rattler/runtime/packrat_parser.rb +21 -14
  38. data/lib/rattler/runtime/parser.rb +74 -18
  39. data/lib/rattler/runtime/recursive_descent_parser.rb +15 -46
  40. data/spec/rattler/back_end/compiler_spec.rb +173 -107
  41. data/spec/rattler/back_end/parser_generator/list1_generator_spec.rb +304 -0
  42. data/spec/rattler/back_end/parser_generator/list_generator_spec.rb +288 -0
  43. data/spec/rattler/grammar/grammar_parser_spec.rb +65 -76
  44. data/spec/rattler/parsers/action_code_spec.rb +84 -34
  45. data/spec/rattler/parsers/direct_action_spec.rb +56 -34
  46. data/spec/rattler/parsers/fail_spec.rb +20 -0
  47. data/spec/rattler/parsers/list1_spec.rb +82 -0
  48. data/spec/rattler/parsers/list_spec.rb +82 -0
  49. data/spec/rattler/parsers/parser_dsl_spec.rb +48 -19
  50. data/spec/rattler/runtime/extended_packrat_parser_spec.rb +0 -1
  51. metadata +92 -173
  52. data/bin/rtlr.bat +0 -3
  53. data/lib/rattler/back_end/parser_generator/generator_helper.rb +0 -130
  54. data/lib/rattler/back_end/parser_generator/generators.rb +0 -86
  55. data/lib/rattler/back_end/parser_generator/nested_generators.rb +0 -15
  56. data/lib/rattler/back_end/parser_generator/top_level_generators.rb +0 -15
@@ -2,26 +2,35 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  describe DirectAction do
4
4
  include CombinatorParserSpecHelper
5
-
5
+
6
6
  describe '#parse' do
7
-
7
+
8
8
  context 'with a capturing parser' do
9
-
10
- subject { DirectAction[Match[/[[:digit:]]+/], '|_| _ * 2'] }
11
-
9
+
10
+ subject { DirectAction[Match[/[[:digit:]]+/], '|s| s * 2'] }
11
+
12
12
  context 'when the parser matches' do
13
13
  it 'applies the action binding the captured results as arguments' do
14
14
  parsing('451a').should result_in('451451').at(3)
15
15
  end
16
16
  end
17
-
17
+
18
18
  context 'when the parser fails' do
19
19
  it 'fails' do
20
20
  parsing('foo').should fail
21
21
  end
22
22
  end
23
+
24
+ context 'using the "_" character' do
25
+
26
+ subject { DirectAction[Match[/[[:digit:]]+/], '_ * 2'] }
27
+
28
+ it 'applies the action binding the captured result as "_"' do
29
+ parsing('451a').should result_in('451451').at(3)
30
+ end
31
+ end
23
32
  end
24
-
33
+
25
34
  context 'with a sequence parser' do
26
35
  subject do
27
36
  DirectAction[
@@ -29,86 +38,99 @@ describe DirectAction do
29
38
  '|l, r| "#{r} -> #{l}"'
30
39
  ]
31
40
  end
32
-
41
+
33
42
  context 'when the parser matches' do
34
43
  it 'applies the action binding the captured results as arguments' do
35
44
  parsing('val=42 ').should result_in('42 -> val').at(6)
36
45
  end
37
46
  end
47
+
48
+ context 'using the "_" character' do
49
+ subject do
50
+ DirectAction[
51
+ Sequence[Match[/[[:alpha:]]+/], Skip[Match[/\=/]], Match[/[[:digit:]]+/]],
52
+ '_.join " <- "'
53
+ ]
54
+ end
55
+
56
+ it 'applies the action binding the captured result array as "_"' do
57
+ parsing('val=42 ').should result_in('val <- 42').at(6)
58
+ end
59
+ end
38
60
  end
39
-
61
+
40
62
  context 'with an optional parser' do
41
-
42
- subject { DirectAction[Optional[Match[/\d+/]], '|_| _'] }
43
-
63
+
64
+ subject { DirectAction[Optional[Match[/\d+/]], '|s| s'] }
65
+
44
66
  context 'when the nested parser matches' do
45
67
  it 'applies the action to an array containing the match' do
46
68
  parsing('451a').should result_in(['451']).at(3)
47
69
  end
48
70
  end
49
-
71
+
50
72
  context 'when the nested parser fails' do
51
73
  it 'applies the action to an empty array' do
52
74
  parsing('foo').should result_in([]).at(0)
53
75
  end
54
76
  end
55
77
  end
56
-
78
+
57
79
  context 'with a zero-or-more parser' do
58
-
59
- subject { DirectAction[ZeroOrMore[Match[/\d/]], '|_| _'] }
60
-
80
+
81
+ subject { DirectAction[ZeroOrMore[Match[/\d/]], '|s| s'] }
82
+
61
83
  context 'when the nested parser matches' do
62
84
  it 'applies the action to an array containing the matches' do
63
85
  parsing('451a').should result_in(['4', '5', '1']).at(3)
64
86
  end
65
87
  end
66
-
88
+
67
89
  context 'when the nested parser fails' do
68
90
  it 'applies the action to an empty array' do
69
91
  parsing('foo').should result_in([]).at(0)
70
92
  end
71
93
  end
72
94
  end
73
-
95
+
74
96
  context 'with a one-or-more parser' do
75
-
76
- subject { DirectAction[OneOrMore[Match[/\d/]], '|_| _'] }
77
-
97
+
98
+ subject { DirectAction[OneOrMore[Match[/\d/]], '|s| s'] }
99
+
78
100
  context 'when the nested parser matches' do
79
101
  it 'applies the action to an array containing the matches' do
80
102
  parsing('451a').should result_in(['4', '5', '1']).at(3)
81
103
  end
82
104
  end
83
-
105
+
84
106
  context 'when the nested parser fails' do
85
107
  it 'fails' do
86
108
  parsing('foo').should fail
87
109
  end
88
110
  end
89
111
  end
90
-
112
+
91
113
  context 'with a token parser' do
92
- subject { DirectAction[Token[Match[/[[:digit:]]+/]], '|_| _.to_i'] }
93
-
114
+ subject { DirectAction[Token[Match[/[[:digit:]]+/]], '|s| s.to_i'] }
115
+
94
116
  context 'when the parser matches' do
95
117
  it 'applies the action to the matched string' do
96
118
  parsing('42 ').should result_in(42).at(2)
97
119
  end
98
120
  end
99
121
  end
100
-
122
+
101
123
  context 'with a non-capturing parser' do
102
-
124
+
103
125
  subject { DirectAction[Skip[Match[/\w+/]], '42'] }
104
-
126
+
105
127
  context 'when the parser matches' do
106
128
  it 'applies the action with no arguments' do
107
129
  parsing('abc123 ').should result_in(42).at(6)
108
130
  end
109
131
  end
110
132
  end
111
-
133
+
112
134
  context 'with a sequence of labeled parsers' do
113
135
  subject do
114
136
  DirectAction[
@@ -127,16 +149,16 @@ describe DirectAction do
127
149
  end
128
150
  end
129
151
  end
130
-
152
+
131
153
  describe '#capturing?' do
132
-
154
+
133
155
  context 'with a capturing parser' do
134
156
  subject { DirectAction[Match[/\w+/], ''] }
135
157
  it 'is true' do
136
158
  subject.should be_capturing
137
159
  end
138
160
  end
139
-
161
+
140
162
  context 'with a non-capturing parser' do
141
163
  subject { DirectAction[Skip[Match[/\s*/]], ''] }
142
164
  it 'is false' do
@@ -144,5 +166,5 @@ describe DirectAction do
144
166
  end
145
167
  end
146
168
  end
147
-
169
+
148
170
  end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Assert do
4
+ include CombinatorParserSpecHelper
5
+
6
+ subject { Fail[:expr, 'malformed expression'] }
7
+
8
+ describe '#parse' do
9
+ it 'fails' do
10
+ parsing('anything').should fail
11
+ end
12
+ end
13
+
14
+ describe '#capturing?' do
15
+ it 'is false' do
16
+ subject.should_not be_capturing
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe List1 do
4
+ include CombinatorParserSpecHelper
5
+
6
+ describe '#parse' do
7
+
8
+ context 'with a capturing term parser' do
9
+
10
+ subject { List1[Match[/\w+/], Match[/[,;]/]] }
11
+
12
+ context 'when no terms match' do
13
+ it 'fails' do
14
+ parsing(' ').should fail
15
+ end
16
+ end
17
+
18
+ context 'when a single term matches' do
19
+ it 'returns an array with the result of matching the term' do
20
+ parsing('foo').should result_in(['foo']).at(3)
21
+ end
22
+ end
23
+
24
+ context 'when multiple terms match' do
25
+ it 'returns an array with the results of matching the terms' do
26
+ parsing('foo,bar;baz ').should result_in(['foo', 'bar', 'baz']).at(11)
27
+ end
28
+ end
29
+
30
+ context 'with a separator not followed by a term' do
31
+ it 'matches without consuming the extra separator' do
32
+ parsing('foo,bar,').should result_in(['foo', 'bar']).at(7)
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'with a non-capturing parser' do
38
+
39
+ subject { List1[Skip[Match[/\w+/]], Match[/[,;]/]] }
40
+
41
+ context 'when no terms match' do
42
+ it 'fails' do
43
+ parsing(' ').should fail
44
+ end
45
+ end
46
+
47
+ context 'when a single term matches' do
48
+ it 'returns true consuming the term' do
49
+ parsing('foo ').should result_in(true).at(3)
50
+ end
51
+ end
52
+
53
+ context 'when multiple terms match' do
54
+ it 'returns true consuming the list' do
55
+ parsing('foo,bar;baz ').should result_in(true).at(11)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#capturing?' do
62
+
63
+ context 'with a capturing term parser' do
64
+
65
+ subject { List1[Match[/\w+/], Match[/[,;]/]] }
66
+
67
+ it 'is true' do
68
+ subject.should be_capturing
69
+ end
70
+ end
71
+
72
+ context 'with a non-capturing term parser' do
73
+
74
+ subject { List1[Skip[Match[/\w+/]], Match[/[,;]/]] }
75
+
76
+ it 'is true' do
77
+ subject.should_not be_capturing
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe List do
4
+ include CombinatorParserSpecHelper
5
+
6
+ describe '#parse' do
7
+
8
+ context 'with a capturing term parser' do
9
+
10
+ subject { List[Match[/\w+/], Match[/[,;]/]] }
11
+
12
+ context 'when no terms match' do
13
+ it 'returns an empty array without advancing' do
14
+ parsing(' ').should result_in([]).at(0)
15
+ end
16
+ end
17
+
18
+ context 'when a single term matches' do
19
+ it 'returns an array with the result of matching the term' do
20
+ parsing('foo').should result_in(['foo']).at(3)
21
+ end
22
+ end
23
+
24
+ context 'when multiple terms match' do
25
+ it 'returns an array with the results of matching the terms' do
26
+ parsing('foo,bar;baz ').should result_in(['foo', 'bar', 'baz']).at(11)
27
+ end
28
+ end
29
+
30
+ context 'with a separator not followed by a term' do
31
+ it 'matches without consuming the extra separator' do
32
+ parsing('foo,bar,').should result_in(['foo', 'bar']).at(7)
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'with a non-capturing parser' do
38
+
39
+ subject { List[Skip[Match[/\w+/]], Match[/[,;]/]] }
40
+
41
+ context 'when no terms match' do
42
+ it 'returns true without advancing' do
43
+ parsing(' ').should result_in(true).at(0)
44
+ end
45
+ end
46
+
47
+ context 'when a single term matches' do
48
+ it 'returns true consuming the term' do
49
+ parsing('foo ').should result_in(true).at(3)
50
+ end
51
+ end
52
+
53
+ context 'when multiple terms match' do
54
+ it 'returns true consuming the list' do
55
+ parsing('foo,bar;baz ').should result_in(true).at(11)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#capturing?' do
62
+
63
+ context 'with a capturing term parser' do
64
+
65
+ subject { List[Match[/\w+/], Match[/[,;]/]] }
66
+
67
+ it 'is true' do
68
+ subject.should be_capturing
69
+ end
70
+ end
71
+
72
+ context 'with a non-capturing term parser' do
73
+
74
+ subject { List[Skip[Match[/\w+/]], Match[/[,;]/]] }
75
+
76
+ it 'is true' do
77
+ subject.should_not be_capturing
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -3,9 +3,9 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
3
  include Rattler::Parsers
4
4
 
5
5
  describe ParserDSL do
6
-
6
+
7
7
  subject { ParserDSL.new }
8
-
8
+
9
9
  describe '#match' do
10
10
  context 'given a regexp' do
11
11
  it 'creates a regexp match parser' do
@@ -34,7 +34,7 @@ describe ParserDSL do
34
34
  end
35
35
  end
36
36
  end
37
-
37
+
38
38
  describe '#optional' do
39
39
  context 'given a parser' do
40
40
  it 'creates an optional parser' do
@@ -47,7 +47,7 @@ describe ParserDSL do
47
47
  end
48
48
  end
49
49
  end
50
-
50
+
51
51
  describe '#zero_or_more' do
52
52
  context 'given a parser' do
53
53
  it 'creates a zero-or-more parser' do
@@ -60,7 +60,7 @@ describe ParserDSL do
60
60
  end
61
61
  end
62
62
  end
63
-
63
+
64
64
  describe '#one_or_more' do
65
65
  context 'given a parser' do
66
66
  it 'creates a one-or-more parser' do
@@ -73,7 +73,36 @@ describe ParserDSL do
73
73
  end
74
74
  end
75
75
  end
76
-
76
+
77
+ describe '#list' do
78
+ context 'given parsers' do
79
+ it 'creates a list parser' do
80
+ subject.list(subject.match(/\w+/), subject.match(/,/)).
81
+ should == List[Match[/\w+/], Match[/,/]]
82
+ end
83
+ end
84
+ context 'given match arguments' do
85
+ it 'creates a list parser' do
86
+ subject.list(/\w+/, ',').should == List[Match[/\w+/], Match[/,/]]
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#list1' do
92
+ context 'given parsers' do
93
+ it 'creates a list parser' do
94
+ subject.list1(subject.match(/\w+/), subject.match(/,/)).
95
+ should == List1[Match[/\w+/], Match[/,/]]
96
+ end
97
+ end
98
+ context 'given match arguments' do
99
+ it 'creates a list parser' do
100
+ subject.list1(/\w+/, ',').
101
+ should == List1[Match[/\w+/], Match[/,/]]
102
+ end
103
+ end
104
+ end
105
+
77
106
  describe '#assert' do
78
107
  context 'given a parser' do
79
108
  it 'create an assert parser' do
@@ -86,7 +115,7 @@ describe ParserDSL do
86
115
  end
87
116
  end
88
117
  end
89
-
118
+
90
119
  describe '#disallow' do
91
120
  context 'given a parser' do
92
121
  it 'creates a disallow parser' do
@@ -99,13 +128,13 @@ describe ParserDSL do
99
128
  end
100
129
  end
101
130
  end
102
-
131
+
103
132
  describe '#eof' do
104
133
  it 'creates the eof parser' do
105
134
  subject.eof.should == Eof[]
106
135
  end
107
136
  end
108
-
137
+
109
138
  describe '#dispatch_action' do
110
139
  context 'given a parser' do
111
140
  context 'given options' do
@@ -134,7 +163,7 @@ describe ParserDSL do
134
163
  end
135
164
  end
136
165
  end
137
-
166
+
138
167
  describe '#direct_action' do
139
168
  context 'given a parser' do
140
169
  it 'creates a direct-action parser' do
@@ -149,7 +178,7 @@ describe ParserDSL do
149
178
  end
150
179
  end
151
180
  end
152
-
181
+
153
182
  describe '#token' do
154
183
  context 'given a parser' do
155
184
  it 'creates a token parser' do
@@ -168,7 +197,7 @@ describe ParserDSL do
168
197
  end
169
198
  end
170
199
  end
171
-
200
+
172
201
  describe '#skip' do
173
202
  context 'given a parser' do
174
203
  it 'creates a skip parser' do
@@ -181,7 +210,7 @@ describe ParserDSL do
181
210
  end
182
211
  end
183
212
  end
184
-
213
+
185
214
  describe '#label' do
186
215
  context 'given a parser' do
187
216
  it 'creates a label parser' do
@@ -194,25 +223,25 @@ describe ParserDSL do
194
223
  end
195
224
  end
196
225
  end
197
-
226
+
198
227
  describe '#fail' do
199
228
  it 'creates a fail-expr parser' do
200
229
  subject.fail('var expected').should == Fail[:expr, 'var expected']
201
230
  end
202
231
  end
203
-
232
+
204
233
  describe '#fail_rule' do
205
234
  it 'creates a fail-rule parser' do
206
235
  subject.fail_rule('var expected').should == Fail[:rule, 'var expected']
207
236
  end
208
237
  end
209
-
238
+
210
239
  describe '#fail_parse' do
211
240
  it 'creates a fail-parse parser' do
212
241
  subject.fail_parse('var expected').should == Fail[:parse, 'var expected']
213
242
  end
214
243
  end
215
-
244
+
216
245
  describe '#rule' do
217
246
  context 'given a block' do
218
247
  it 'creates a rule' do
@@ -227,7 +256,7 @@ describe ParserDSL do
227
256
  end
228
257
  end
229
258
  end
230
-
259
+
231
260
  describe '#rules' do
232
261
  context 'given a block' do
233
262
  it 'creates multiple rules' do
@@ -242,5 +271,5 @@ describe ParserDSL do
242
271
  end
243
272
  end
244
273
  end
245
-
274
+
246
275
  end
@@ -21,7 +21,6 @@ describe Rattler::Runtime::ExtendedPackratParser do
21
21
  end
22
22
 
23
23
  it 'supports indirectly left-recursive rules' do
24
- pending 'implement full algorithm'
25
24
  given_rules do
26
25
  rule(:a) { match(:b) | match(/\d/) }
27
26
  rule(:b) { match(:a) & match(/\d/) }