rattler 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/features/grammar/node_action.feature +15 -2
- data/lib/rattler/back_end/parser_generator/group_match.rb +8 -0
- data/lib/rattler/grammar/metagrammar.rb +65 -1
- data/lib/rattler/grammar/rattler.rtlr +1 -1
- data/lib/rattler/parsers/dispatch_action.rb +10 -4
- data/lib/rattler/parsers/node_code.rb +4 -2
- data/lib/rattler/util/graphviz/node_builder.rb +26 -3
- data/spec/rattler/back_end/compiler_spec.rb +7 -0
- data/spec/rattler/back_end/optimizer_spec.rb +16 -0
- data/spec/rattler/back_end/parser_generator/group_match_spec.rb +51 -0
- data/spec/rattler/back_end/shared_compiler_examples.rb +2 -8
- data/spec/rattler/grammar/grammar_parser_spec.rb +80 -10
- data/spec/rattler/parsers/dispatch_action_spec.rb +18 -5
- data/spec/rattler/parsers/node_code_spec.rb +12 -1
- data/spec/rattler/util/graphviz/node_builder_spec.rb +43 -3
- data/spec/support/compiler_spec_helper.rb +4 -0
- metadata +23 -19
data/README.rdoc
CHANGED
@@ -14,7 +14,7 @@ A language syntax is specified in a grammar using the Rattler syntax. Parser
|
|
14
14
|
classes and modules can be generated statically using the "rtlr" command or
|
15
15
|
dynamically from strings.
|
16
16
|
|
17
|
-
{RDoc}[http://rubydoc.info/gems/rattler/0.4.
|
17
|
+
{RDoc}[http://rubydoc.info/gems/rattler/0.4.1/frames]
|
18
18
|
|
19
19
|
== FEATURES:
|
20
20
|
|
@@ -4,7 +4,8 @@ Feature: Node Actions
|
|
4
4
|
expression. A node type is an object (typically a class) that responds to
|
5
5
|
the #parsed method. It means to pass the parse results to #parsed and use
|
6
6
|
the value returned as the parse result. The parse results are passed as an
|
7
|
-
Array and any attributes are included in a Hash.
|
7
|
+
Array and any attributes are included in a Hash. The action can optionally
|
8
|
+
include a name.
|
8
9
|
|
9
10
|
In order to create node objects as parse results
|
10
11
|
As a language designer
|
@@ -28,4 +29,16 @@ Feature: Node Actions
|
|
28
29
|
integer <- @DIGIT+ <>
|
29
30
|
"""
|
30
31
|
When I parse "42"
|
31
|
-
Then the parse result should be Rattler::Runtime::ParseNode["42"]
|
32
|
+
Then the parse result should be Rattler::Runtime::ParseNode["42"]
|
33
|
+
|
34
|
+
Scenario: Class and node name
|
35
|
+
Given a grammar with:
|
36
|
+
"""
|
37
|
+
expr <- @DIGIT+ ~'+' @DIGIT+ <Expr "sum">
|
38
|
+
"""
|
39
|
+
And a class definition:
|
40
|
+
"""
|
41
|
+
class Expr < Rattler::Runtime::ParseNode; end
|
42
|
+
"""
|
43
|
+
When I parse "42+23"
|
44
|
+
Then the parse result should be Expr[["42", "23"], {:name => "sum"}]
|
@@ -218,8 +218,72 @@ module Rattler
|
|
218
218
|
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>[[:lower:]])(?>(?>[[:alnum:]_])*))|(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>(?>(?>[[:upper:]])(?>(?>[[:alnum:]_])*)(?>::))*)(?>[[:upper:]])(?>(?>[[:alnum:]_])*)))(?>(?>(?>\.)(?>(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>[[:lower:]])(?>(?>[[:alnum:]_])*))))?))/) &&
|
219
219
|
@scanner[1]
|
220
220
|
end) ? [r] : [])) &&
|
221
|
+
(r0_2 = ((r = begin
|
222
|
+
begin
|
223
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>")(?>(?>(?>\\)(?>.)|[^"])*)(?>"))/) &&
|
224
|
+
@scanner[1]
|
225
|
+
end ||
|
226
|
+
begin
|
227
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>')(?>(?>(?>\\)(?>.)|[^'])*)(?>'))/) &&
|
228
|
+
@scanner[1]
|
229
|
+
end ||
|
230
|
+
begin
|
231
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>%\()(?>(?>(?>\\)(?>.)|[^)])*)(?>\)))/) &&
|
232
|
+
@scanner[1]
|
233
|
+
end ||
|
234
|
+
begin
|
235
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>%\{)(?>(?>(?>\\)(?>.)|[^}])*)(?>\}))/) &&
|
236
|
+
@scanner[1]
|
237
|
+
end ||
|
238
|
+
begin
|
239
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>%\[)(?>(?>(?>\\)(?>.)|[^\]])*)(?>\]))/) &&
|
240
|
+
@scanner[1]
|
241
|
+
end ||
|
242
|
+
begin
|
243
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)((?>%<)(?>(?>(?>\\)(?>.)|[^>])*)(?>>))/) &&
|
244
|
+
@scanner[1]
|
245
|
+
end ||
|
246
|
+
begin
|
247
|
+
p1 = @scanner.pos
|
248
|
+
begin
|
249
|
+
@scanner.skip(/(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*/) &&
|
250
|
+
begin
|
251
|
+
p2 = @scanner.pos
|
252
|
+
begin
|
253
|
+
@scanner.skip(/%/) &&
|
254
|
+
(r2_0 = @scanner.scan(/[[:punct:]]/)) &&
|
255
|
+
begin
|
256
|
+
while begin
|
257
|
+
@scanner.skip(/(?>\\)(?>.)/) ||
|
258
|
+
begin
|
259
|
+
p3 = @scanner.pos
|
260
|
+
begin
|
261
|
+
@scanner.skip(/(?!#{r2_0})/) &&
|
262
|
+
@scanner.skip(/./) &&
|
263
|
+
true
|
264
|
+
end || begin
|
265
|
+
@scanner.pos = p3
|
266
|
+
false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end; end
|
270
|
+
true
|
271
|
+
end &&
|
272
|
+
@scanner.skip(/#{r2_0}/) &&
|
273
|
+
@scanner.string[p2...(@scanner.pos)]
|
274
|
+
end || begin
|
275
|
+
@scanner.pos = p2
|
276
|
+
false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end || begin
|
280
|
+
@scanner.pos = p1
|
281
|
+
false
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end) ? [r] : [])) &&
|
221
285
|
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>>)/) &&
|
222
|
-
DispatchAction.parsed(select_captures([r0_0, r0_1]))
|
286
|
+
DispatchAction.parsed(select_captures([r0_0, r0_1, r0_2]))
|
223
287
|
end || begin
|
224
288
|
@scanner.pos = p0
|
225
289
|
false
|
@@ -44,7 +44,7 @@ include Rattler::Parsers
|
|
44
44
|
expression <- expression ~'/' attributed <Choice>
|
45
45
|
/ attributed
|
46
46
|
|
47
|
-
attributed <- attributed ~'<' node_action? ~'>'
|
47
|
+
attributed <- attributed ~'<' node_action? literal? ~'>' <DispatchAction>
|
48
48
|
/ attributed ~'{' action ~'}' <DirectAction>
|
49
49
|
/ terms
|
50
50
|
|
@@ -25,8 +25,13 @@ module Rattler::Parsers
|
|
25
25
|
|
26
26
|
# @private
|
27
27
|
def self.parsed(results, *_) #:nodoc:
|
28
|
-
attributed, optional_attribute = results
|
29
|
-
self[attributed, optional_attribute.first || @@node_defaults[:target]]
|
28
|
+
attributed, optional_attribute, optional_name = results
|
29
|
+
a = self[attributed, optional_attribute.first || @@node_defaults[:target]]
|
30
|
+
unless optional_name.empty?
|
31
|
+
a.with_attrs(:target_attrs => {:name => eval(optional_name.first, TOPLEVEL_BINDING)})
|
32
|
+
else
|
33
|
+
a
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
# @private
|
@@ -59,10 +64,11 @@ module Rattler::Parsers
|
|
59
64
|
super(children, self.class.parse_attrs_arg(attrs_arg))
|
60
65
|
@@node_defaults.each {|k, v| attrs[k] ||= v } unless attrs[:code]
|
61
66
|
@method_name = attrs[:method]
|
67
|
+
@target_attrs = attrs[:target_attrs] || {}
|
62
68
|
end
|
63
69
|
|
64
70
|
# the name of the method used as the symantic action
|
65
|
-
attr_reader :method_name
|
71
|
+
attr_reader :method_name, :target_attrs
|
66
72
|
|
67
73
|
# If the wrapped parser matches at the parse position, return the result
|
68
74
|
# of applying the symantic action, otherwise return a false value.
|
@@ -84,7 +90,7 @@ module Rattler::Parsers
|
|
84
90
|
end
|
85
91
|
|
86
92
|
def bindable_code
|
87
|
-
@bindable_code ||= NodeCode.new(target, method_name)
|
93
|
+
@bindable_code ||= NodeCode.new(target, method_name, target_attrs)
|
88
94
|
end
|
89
95
|
|
90
96
|
def bind(scope, bind_args)
|
@@ -15,12 +15,13 @@ module Rattler::Parsers
|
|
15
15
|
self.new(action.target, action.method_name)
|
16
16
|
end
|
17
17
|
|
18
|
-
def initialize(target_name, method_name)
|
18
|
+
def initialize(target_name, method_name, target_attrs = {})
|
19
19
|
@target_name = target_name
|
20
20
|
@method_name = method_name
|
21
|
+
@target_attrs = target_attrs
|
21
22
|
end
|
22
23
|
|
23
|
-
attr_reader :target_name, :method_name
|
24
|
+
attr_reader :target_name, :method_name, :target_attrs
|
24
25
|
|
25
26
|
def bind(scope, bind_args)
|
26
27
|
args = [array_expr(bind_args)]
|
@@ -28,6 +29,7 @@ module Rattler::Parsers
|
|
28
29
|
labeled = '{' + scope.map {|k, v| ":#{k} => #{v}"}.join(', ') + '}'
|
29
30
|
args << ":labeled => #{labeled}"
|
30
31
|
end
|
32
|
+
target_attrs.each {|k, v| args << ":#{k} => #{v.inspect}" }
|
31
33
|
t = target_name == 'self' ? '' : "#{target_name}."
|
32
34
|
"#{t}#{method_name}(#{args.join ', '})"
|
33
35
|
end
|
@@ -18,7 +18,13 @@ module Rattler::Util::GraphViz
|
|
18
18
|
# Yield any children of +o+ that should be represented as separate nodes in
|
19
19
|
# the graph.
|
20
20
|
def each_child_of(o)
|
21
|
-
|
21
|
+
if array_like? o and not record_like? o
|
22
|
+
if o.respond_to? :to_hash
|
23
|
+
o.each {|k, v| yield Mapping.new(k, v) }
|
24
|
+
else
|
25
|
+
o.each {|_| yield _ }
|
26
|
+
end
|
27
|
+
end
|
22
28
|
end
|
23
29
|
|
24
30
|
# Return the options for a node representing +o+.
|
@@ -31,7 +37,7 @@ module Rattler::Util::GraphViz
|
|
31
37
|
# @return the shape option for a node representing +o+.
|
32
38
|
def node_shape(o)
|
33
39
|
case o
|
34
|
-
when Hash,
|
40
|
+
when Array, Hash, Mapping
|
35
41
|
'circle'
|
36
42
|
when String, Numeric, Symbol
|
37
43
|
'plaintext'
|
@@ -49,6 +55,8 @@ module Rattler::Util::GraphViz
|
|
49
55
|
record_label(o, o)
|
50
56
|
elsif array_like? o
|
51
57
|
type_label(o)
|
58
|
+
elsif o.respond_to? :to_str
|
59
|
+
"\"#{o}\""
|
52
60
|
else
|
53
61
|
o.inspect
|
54
62
|
end
|
@@ -58,6 +66,7 @@ module Rattler::Util::GraphViz
|
|
58
66
|
case o
|
59
67
|
when Hash then '\\{\\}'
|
60
68
|
when Array then '\\[\\]'
|
69
|
+
when Mapping then '->'
|
61
70
|
else o.respond_to?(:name) ? o.name : o.class.name
|
62
71
|
end
|
63
72
|
end
|
@@ -73,12 +82,26 @@ module Rattler::Util::GraphViz
|
|
73
82
|
end
|
74
83
|
|
75
84
|
def record_label(o, h)
|
76
|
-
|
85
|
+
fields = h.reject {|k,v| k == :name }
|
86
|
+
'{' + ([type_label(o)] + hash_content_labels(fields)).join('|') + '}'
|
77
87
|
end
|
78
88
|
|
79
89
|
def hash_content_labels(h)
|
80
90
|
h.map {|pair| '{' + pair.map {|_| _.inspect }.join('|') + '}' }
|
81
91
|
end
|
82
92
|
|
93
|
+
# @private
|
94
|
+
class Mapping #:nodoc:
|
95
|
+
def initialize(key, value)
|
96
|
+
@key = key
|
97
|
+
@value = value
|
98
|
+
end
|
99
|
+
attr_reader :key, :value
|
100
|
+
def each
|
101
|
+
yield key
|
102
|
+
yield value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
83
106
|
end
|
84
107
|
end
|
@@ -5,6 +5,13 @@ describe Rattler::BackEnd::Compiler do
|
|
5
5
|
include CompilerSpecHelper
|
6
6
|
|
7
7
|
describe '.compile_parser result' do
|
8
|
+
|
9
|
+
let :compiled_parser do
|
10
|
+
described_class.compile_parser compiled_parser_base, grammar
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:compiled_parser_base) { Rattler::Runtime::RecursiveDescentParser }
|
14
|
+
|
8
15
|
it_behaves_like 'a compiled parser'
|
9
16
|
end
|
10
17
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/shared_compiler_examples')
|
3
|
+
|
4
|
+
describe Rattler::BackEnd::Optimizer do
|
5
|
+
include CompilerSpecHelper
|
6
|
+
|
7
|
+
describe '.optimize result' do
|
8
|
+
|
9
|
+
let :compiled_parser do
|
10
|
+
combinator_parser described_class.optimize(grammar)
|
11
|
+
end
|
12
|
+
|
13
|
+
it_behaves_like 'a compiled parser'
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
|
+
|
3
|
+
include Rattler::BackEnd::ParserGenerator
|
4
|
+
|
5
|
+
describe GroupMatch do
|
6
|
+
include CombinatorParserSpecHelper
|
7
|
+
|
8
|
+
let(:single_group) { GroupMatch[Match[/\s*(\w+)/], {:num_groups => 1}] }
|
9
|
+
let(:multi_group) { GroupMatch[Match[/\s*(\w+)\s+(\w+)/], {:num_groups => 2}] }
|
10
|
+
|
11
|
+
describe '#parse' do
|
12
|
+
|
13
|
+
context 'with a single group' do
|
14
|
+
|
15
|
+
subject { single_group }
|
16
|
+
|
17
|
+
context 'when the regexp matches' do
|
18
|
+
it 'succeeds returning the matched group' do
|
19
|
+
parsing(' abc123 ').should result_in('abc123').at(8)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when the regexp does not match' do
|
24
|
+
it 'fails' do
|
25
|
+
parsing('==').should fail
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with multiple groups' do
|
31
|
+
|
32
|
+
subject { multi_group }
|
33
|
+
|
34
|
+
context 'when the regexp matches' do
|
35
|
+
it 'succeeds returning the matched groups in an array' do
|
36
|
+
parsing(' abc 123 ').should result_in(['abc', '123']).at(8)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#capturing?' do
|
43
|
+
|
44
|
+
subject { single_group }
|
45
|
+
|
46
|
+
it 'is true' do
|
47
|
+
subject.should be_capturing
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -1,15 +1,9 @@
|
|
1
1
|
shared_examples_for 'a compiled parser' do
|
2
2
|
include CompilerSpecHelper
|
3
3
|
|
4
|
-
subject
|
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
|
4
|
+
subject { compiled_parser }
|
11
5
|
|
12
|
-
let(:
|
6
|
+
let(:reference_parser) { combinator_parser grammar }
|
13
7
|
|
14
8
|
########## match ##########
|
15
9
|
context 'with a match rule' do
|
@@ -167,20 +167,90 @@ describe Rattler::Grammar::GrammarParser do
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
170
|
+
context 'given a dispatch-action-attributed expression' do
|
171
|
+
|
172
|
+
context 'given an action with a class name' do
|
173
|
+
it 'parses as a DispatchAction with the default method' do
|
174
|
+
matching(' expr <Expr> ').as(:expression).
|
175
|
+
should result_in(DispatchAction[Apply[:expr],
|
176
|
+
{:target => 'Expr', :method => 'parsed'}
|
177
|
+
]).at(12)
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'with a literal' do
|
181
|
+
it 'uses the literal as the name' do
|
182
|
+
matching(' expr <Expr "expression"> ').as(:expression).
|
183
|
+
should result_in(DispatchAction[Apply[:expr],
|
184
|
+
{:target => 'Expr', :method => 'parsed',
|
185
|
+
:target_attrs => {:name => 'expression'} }
|
186
|
+
]).at(25)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'given an action with a class and method names' do
|
192
|
+
it 'parses as a DispatchAction with the default method' do
|
193
|
+
matching(' expr <Expr.eval> ').as(:expression).
|
194
|
+
should result_in(DispatchAction[Apply[:expr],
|
195
|
+
{:target => 'Expr', :method => 'eval'}
|
196
|
+
]).at(17)
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'with a literal' do
|
200
|
+
it 'uses the literal as the name' do
|
201
|
+
matching(' expr <Expr.eval "expression"> ').as(:expression).
|
202
|
+
should result_in(DispatchAction[Apply[:expr],
|
203
|
+
{ :target => 'Expr', :method => 'eval',
|
204
|
+
:target_attrs => {:name => 'expression'} }
|
205
|
+
]).at(30)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'given an empty action' do
|
211
|
+
it 'parses as a DispatchAction with the default target and method' do
|
212
|
+
matching(' expr <> ').as(:expression).
|
213
|
+
should result_in(DispatchAction[Apply[:expr],
|
214
|
+
{:target => 'Rattler::Runtime::ParseNode', :method => 'parsed'}
|
215
|
+
]).at(8)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'given just a literal' do
|
220
|
+
it 'parses as a default node with the literal as the name' do
|
221
|
+
matching(' expr <"expression"> ').as(:expression).
|
222
|
+
should result_in(DispatchAction[Apply[:expr],
|
223
|
+
{ :target => 'Rattler::Runtime::ParseNode',
|
224
|
+
:method => 'parsed',
|
225
|
+
:target_attrs => {:name => 'expression'} }
|
226
|
+
]).at(20)
|
227
|
+
end
|
228
|
+
end
|
174
229
|
end
|
175
230
|
|
176
|
-
|
177
|
-
|
178
|
-
|
231
|
+
context 'given direct-action-attributed expression' do
|
232
|
+
it 'parses as a DirectAction' do
|
233
|
+
matching(' digits {|_| _.to_i} ').as(:expression).
|
234
|
+
should result_in(DirectAction[Apply[:digits], '|_| _.to_i']).at(20)
|
235
|
+
end
|
179
236
|
end
|
180
237
|
|
181
|
-
|
182
|
-
|
183
|
-
|
238
|
+
context 'given a sequence expression' do
|
239
|
+
it 'parses as a Sequence' do
|
240
|
+
matching(' name "=" value ').as(:expression).
|
241
|
+
should result_in(Sequence[Apply[:name], Match[%r{=}], Apply[:value]]).at(15)
|
242
|
+
end
|
243
|
+
|
244
|
+
context 'with a nested sequence expression' do
|
245
|
+
it 'parses as a nested Sequence' do
|
246
|
+
matching(' a (b c) d ').as(:expression).
|
247
|
+
should result_in(Sequence[
|
248
|
+
Apply[:a],
|
249
|
+
Sequence[Apply[:b], Apply[:c]],
|
250
|
+
Apply[:d]
|
251
|
+
]).at(10)
|
252
|
+
end
|
253
|
+
end
|
184
254
|
end
|
185
255
|
|
186
256
|
it 'parses attributed sequence expressions' do
|
@@ -3,14 +3,15 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
3
3
|
describe DispatchAction do
|
4
4
|
include CombinatorParserSpecHelper
|
5
5
|
|
6
|
+
subject { DispatchAction[nested_parser] }
|
7
|
+
|
6
8
|
describe '#parse' do
|
7
9
|
|
8
10
|
context 'with a capturing parser' do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
11
|
+
|
12
|
+
let (:nested_parser) { Sequence[
|
13
|
+
Match[/[[:alpha:]]+/], Match[/\=/], Match[/[[:digit:]]+/]
|
14
|
+
] }
|
14
15
|
|
15
16
|
context 'when the parser matches' do
|
16
17
|
it 'applies the action to the result' do
|
@@ -26,6 +27,18 @@ describe DispatchAction do
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
30
|
+
context 'with a non-capturing parser' do
|
31
|
+
|
32
|
+
let (:nested_parser) { Skip[Match[/,/]] }
|
33
|
+
|
34
|
+
context 'when the parser matches' do
|
35
|
+
it 'applies the action to an empty array' do
|
36
|
+
parsing(', ').
|
37
|
+
should result_in(Rattler::Runtime::ParseNode.parsed([])).at(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
29
42
|
context 'with a token parser' do
|
30
43
|
|
31
44
|
subject { DispatchAction[Token[Match[/\w+/]]] }
|
@@ -2,10 +2,11 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
2
|
|
3
3
|
describe Rattler::Parsers::NodeCode do
|
4
4
|
|
5
|
-
subject { described_class.new(target_name, method_name) }
|
5
|
+
subject { described_class.new(target_name, method_name, target_attrs) }
|
6
6
|
|
7
7
|
let(:target_name) { 'Expr' }
|
8
8
|
let(:method_name) { 'parsed' }
|
9
|
+
let(:target_attrs) { {} }
|
9
10
|
|
10
11
|
describe '#bind' do
|
11
12
|
|
@@ -44,5 +45,15 @@ describe Rattler::Parsers::NodeCode do
|
|
44
45
|
should == 'Expr.parsed([a, b], :labeled => {:word => w})'
|
45
46
|
end
|
46
47
|
end
|
48
|
+
|
49
|
+
context 'with target_attrs' do
|
50
|
+
|
51
|
+
let(:target_attrs) { {:name => 'expression'} }
|
52
|
+
|
53
|
+
it '' do
|
54
|
+
subject.bind(scope, ['a', 'b']).
|
55
|
+
should == 'Expr.parsed([a, b], :name => "expression")'
|
56
|
+
end
|
57
|
+
end
|
47
58
|
end
|
48
59
|
end
|
@@ -3,6 +3,46 @@ require 'set'
|
|
3
3
|
|
4
4
|
describe Rattler::Util::GraphViz::NodeBuilder do
|
5
5
|
|
6
|
+
describe '#node_label' do
|
7
|
+
|
8
|
+
context 'given a number' do
|
9
|
+
it 'returns a number as a string' do
|
10
|
+
subject.node_label(42).should == '42'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'given a string' do
|
15
|
+
it 'returns the string surrounded by escaped quotes' do
|
16
|
+
subject.node_label('abc').should == '"abc"'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'given an unnamed record-like object' do
|
21
|
+
it 'returns a record label with the class name' do
|
22
|
+
subject.node_label(Rattler::Util::Node[]).should == '{Rattler::Util::Node}'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'given a named record-like object' do
|
27
|
+
it 'returns a record label with a name' do
|
28
|
+
subject.node_label(Rattler::Util::Node[{:name => 'IDENT'}]).
|
29
|
+
should == '{IDENT}'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'given an array-like object' do
|
34
|
+
it 'returns "[]" escaped' do
|
35
|
+
subject.node_label(['let', 'x', '=', '1']).should == '\\[\\]'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'given a hash with compound values' do
|
40
|
+
it 'returns "{}" escaped' do
|
41
|
+
subject.node_label({:a => 'a', :b => ['a1', 'a2']}).should == '\\{\\}'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
6
46
|
describe '#array_like?' do
|
7
47
|
|
8
48
|
context 'given an array' do
|
@@ -11,7 +51,7 @@ describe Rattler::Util::GraphViz::NodeBuilder do
|
|
11
51
|
end
|
12
52
|
end
|
13
53
|
|
14
|
-
context 'given a hash' do
|
54
|
+
context 'given a simple hash' do
|
15
55
|
it 'returns true' do
|
16
56
|
subject.array_like?({:a => 'a'}).should be_true
|
17
57
|
end
|
@@ -62,10 +102,10 @@ describe Rattler::Util::GraphViz::NodeBuilder do
|
|
62
102
|
|
63
103
|
let(:object) { {:a => ['a1', 'a2'], :b => 'b'} }
|
64
104
|
|
65
|
-
it 'iterates over the pairs' do
|
105
|
+
it 'iterates over the pairs yielding Mappings' do
|
66
106
|
children = Set[]
|
67
107
|
subject.each_child_of(object) {|_| children << _ }
|
68
|
-
children.should
|
108
|
+
children.should have(2).mappings
|
69
109
|
end
|
70
110
|
end
|
71
111
|
|
@@ -6,6 +6,10 @@ module CompilerSpecHelper
|
|
6
6
|
Rattler::Grammar::Grammar.new(Rattler::Parsers.define(&block))
|
7
7
|
end
|
8
8
|
|
9
|
+
def combinator_parser(g)
|
10
|
+
Rattler::Parsers::CombinatorParser.as_class(g.rules.first, g.rules)
|
11
|
+
end
|
12
|
+
|
9
13
|
RSpec::Matchers.define :parse do |source|
|
10
14
|
match do |parser_class|
|
11
15
|
parser = parser_class.new(source)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rattler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-03-
|
12
|
+
date: 2011-03-15 00:00:00.000000000 -07:00
|
13
13
|
default_executable: rtlr
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
17
|
-
requirement: &
|
17
|
+
requirement: &72971210 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 1.0.0
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *72971210
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: jeweler
|
28
|
-
requirement: &
|
28
|
+
requirement: &72970960 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: 1.5.2
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *72970960
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: rspec
|
39
|
-
requirement: &
|
39
|
+
requirement: &72970700 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: 2.3.0
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *72970700
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: cucumber
|
50
|
-
requirement: &
|
50
|
+
requirement: &72970430 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: 0.8.0
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *72970430
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: aruba
|
61
|
-
requirement: &
|
61
|
+
requirement: &72970180 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: 0.3.0
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *72970180
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: yard
|
72
|
-
requirement: &
|
72
|
+
requirement: &72969930 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: 0.6.0
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *72969930
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: watchr
|
83
|
-
requirement: &
|
83
|
+
requirement: &72969680 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ! '>='
|
@@ -88,10 +88,10 @@ dependencies:
|
|
88
88
|
version: 0.5.5
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *72969680
|
92
92
|
- !ruby/object:Gem::Dependency
|
93
93
|
name: ruby-graphviz
|
94
|
-
requirement: &
|
94
|
+
requirement: &72969430 !ruby/object:Gem::Requirement
|
95
95
|
none: false
|
96
96
|
requirements:
|
97
97
|
- - ! '>='
|
@@ -99,7 +99,7 @@ dependencies:
|
|
99
99
|
version: 0.9.6
|
100
100
|
type: :development
|
101
101
|
prerelease: false
|
102
|
-
version_requirements: *
|
102
|
+
version_requirements: *72969430
|
103
103
|
description: Simple language recognition tool for Ruby based on packrat parsing
|
104
104
|
email: jarhart@gmail.com
|
105
105
|
executables:
|
@@ -274,6 +274,7 @@ files:
|
|
274
274
|
- spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb
|
275
275
|
- spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb
|
276
276
|
- spec/rattler/back_end/optimizer/simplify_token_match_spec.rb
|
277
|
+
- spec/rattler/back_end/optimizer_spec.rb
|
277
278
|
- spec/rattler/back_end/parser_generator/apply_generator_spec.rb
|
278
279
|
- spec/rattler/back_end/parser_generator/assert_generator_spec.rb
|
279
280
|
- spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb
|
@@ -283,6 +284,7 @@ files:
|
|
283
284
|
- spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb
|
284
285
|
- spec/rattler/back_end/parser_generator/fail_generator_spec.rb
|
285
286
|
- spec/rattler/back_end/parser_generator/group_match_generator_spec.rb
|
287
|
+
- spec/rattler/back_end/parser_generator/group_match_spec.rb
|
286
288
|
- spec/rattler/back_end/parser_generator/label_generator_spec.rb
|
287
289
|
- spec/rattler/back_end/parser_generator/list1_generator_spec.rb
|
288
290
|
- spec/rattler/back_end/parser_generator/list_generator_spec.rb
|
@@ -356,7 +358,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
356
358
|
version: '0'
|
357
359
|
segments:
|
358
360
|
- 0
|
359
|
-
hash:
|
361
|
+
hash: 156917203
|
360
362
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
361
363
|
none: false
|
362
364
|
requirements:
|
@@ -416,6 +418,7 @@ test_files:
|
|
416
418
|
- spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb
|
417
419
|
- spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb
|
418
420
|
- spec/rattler/back_end/optimizer/simplify_token_match_spec.rb
|
421
|
+
- spec/rattler/back_end/optimizer_spec.rb
|
419
422
|
- spec/rattler/back_end/parser_generator/apply_generator_spec.rb
|
420
423
|
- spec/rattler/back_end/parser_generator/assert_generator_spec.rb
|
421
424
|
- spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb
|
@@ -425,6 +428,7 @@ test_files:
|
|
425
428
|
- spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb
|
426
429
|
- spec/rattler/back_end/parser_generator/fail_generator_spec.rb
|
427
430
|
- spec/rattler/back_end/parser_generator/group_match_generator_spec.rb
|
431
|
+
- spec/rattler/back_end/parser_generator/group_match_spec.rb
|
428
432
|
- spec/rattler/back_end/parser_generator/label_generator_spec.rb
|
429
433
|
- spec/rattler/back_end/parser_generator/list1_generator_spec.rb
|
430
434
|
- spec/rattler/back_end/parser_generator/list_generator_spec.rb
|