rattler 0.4.0 → 0.4.1
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.
- 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
|