babel_bridge 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,256 @@
1
+ module BabelBridge
2
+
3
+ # this is just so we can distinguish between normal arrays and arrays of matches
4
+ # - since a match can be an Array in the case of Poly-matches
5
+ class MultiMatchesArray < Array
6
+ end
7
+
8
+ # base class for all parse-tree nodes
9
+ class Node
10
+ attr_accessor :src,:offset,:match_length,:parent,:parser
11
+
12
+ def to_s
13
+ text
14
+ end
15
+
16
+ def node_init(parent_or_parser)
17
+ self.match_length=0
18
+ case parent_or_parser
19
+ when Parser then
20
+ self.parser=parent_or_parser
21
+ self.offset=0
22
+ self.src=parser.src
23
+ when Node then
24
+ self.parent=parent_or_parser
25
+ self.parser=parent.parser
26
+ self.offset=parent.next
27
+ self.src=parent.src
28
+ raise "parent node does not have parser set" unless parser
29
+ else
30
+ raise "parent_or_parser(#{parent_or_parser.class}) must be a Node or a Parser"
31
+ end
32
+ end
33
+
34
+ def initialize(parent)
35
+ node_init(parent)
36
+ end
37
+
38
+ #********************
39
+ # info methods
40
+ #********************
41
+ def next; offset+match_length end # index of first character after match
42
+ def text; src[offset,match_length] end # the substring in src matched
43
+
44
+ # length returns the number of sub-nodes
45
+ def length
46
+ 0
47
+ end
48
+
49
+ def parent_list
50
+ return parent ? parent.parent_list+[parent] : []
51
+ end
52
+
53
+ def node_path
54
+ "#{parent && (parent.node_path+' > ')}#{self.class}(#{offset})"
55
+ end
56
+
57
+ #*****************************
58
+ # Array interface implementation
59
+ #*****************************
60
+ def matches # override this with function that returns array of matches to be used for Array indexing and iteration
61
+ []
62
+ end
63
+
64
+ include Enumerable
65
+ def length
66
+ matches.length
67
+ end
68
+
69
+ def <<(node)
70
+ matches<<node
71
+ end
72
+
73
+ def add_delimiter(node)
74
+ delimiter_matches<<node
75
+ end
76
+
77
+ def [](i)
78
+ matches[i]
79
+ end
80
+
81
+ def each(&block)
82
+ matches.each(&block)
83
+ end
84
+ end
85
+
86
+ class RootNode < Node
87
+ end
88
+
89
+ # non-terminal node
90
+ # subclassed automatically by parser.rule for each unique non-terminal
91
+ class NodeNT < Node
92
+ attr_accessor :matches,:match_names
93
+
94
+ def match_names
95
+ @match_names ||= []
96
+ end
97
+ def matches
98
+ @matches ||= []
99
+ end
100
+
101
+ # length returns the number of sub-nodes
102
+ def length
103
+ matches.length
104
+ end
105
+
106
+ def matches_by_name
107
+ @matches_by_name||= begin
108
+ raise "matches.length #{matches.length} != match_names.length #{match_names.length}" unless matches.length==match_names.length
109
+ mbn={}
110
+ mn=match_names
111
+ matches.each_with_index do |match,i|
112
+ name=mn[i]
113
+ next unless name
114
+ if current=mbn[name] # name already used
115
+ # convert to MultiMatchesArray if not already
116
+ mbn[name]=MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
117
+ # add to array
118
+ mbn[name]<<match
119
+ else
120
+ mbn[name]=match
121
+ end
122
+ end
123
+ mbn
124
+ end
125
+ end
126
+
127
+ def inspect(options={})
128
+ return "#{self.class}" if matches.length==0
129
+ matches_inspected=matches.collect{|a|a.inspect(options)}.compact
130
+ if matches_inspected.length==0 then nil
131
+ elsif matches_inspected.length==1
132
+ m=matches_inspected[0]
133
+ ret="#{self.class} > "+matches_inspected[0]
134
+ if options[:simple]
135
+ ret=if m["\n"] then m
136
+ else
137
+ # just show the first and last nodes in the chain
138
+ ret.gsub(/( > [A-Z][a-zA-Z0-9:]+ > (\.\.\. > )?)/," > ... > ")
139
+ end
140
+ end
141
+ ret
142
+ else
143
+ (["#{self.class}"]+matches_inspected).join("\n").gsub("\n","\n ")
144
+ end
145
+ end
146
+
147
+ #********************
148
+ # alter methods
149
+ #********************
150
+ def reset_matches_by_name
151
+ @matches_by_name=nil
152
+ end
153
+
154
+ def method_missing(method_name, *args) #method_name is a symbol
155
+ unless matches_by_name.has_key? method_name
156
+ if matches[0]
157
+ puts "sending #{method_name.inspect} to #{matches[0].class}"
158
+ return matches[0].send(method_name,*args)
159
+ end
160
+ raise "#{self.class}: missing method #{method_name.inspect} / doesn't match named pattern element: #{matches_by_name.keys.inspect}"
161
+ end
162
+ matches_by_name[method_name]
163
+ end
164
+
165
+ # adds a match with name (optional)
166
+ # returns self so you can chain add_match or concat methods
167
+ def add_match(match,name=nil)
168
+ raise "match must be a Node (match is a #{match.class})" unless match.kind_of?(Node)
169
+ raise "name must be a Symbol or nil (name is a #{name.class})" if name && !name.kind_of?(Symbol)
170
+ reset_matches_by_name
171
+ matches<<match
172
+ match_names<<name
173
+
174
+ self.match_length=match.next - offset
175
+ self
176
+ end
177
+
178
+ # concatinate all matches from another node
179
+ # returns self so you can chain add_match or concat methods
180
+ def concat(node)
181
+ names=node.match_names
182
+ node.matches.each_with_index { |match,i| add_match(match,names[i])}
183
+ self
184
+ end
185
+ end
186
+
187
+ # generated by a :poly PatternElement
188
+ # Not subclassed
189
+ class ManyNode < Node
190
+ attr_accessor :matches,:delimiter_matches
191
+ def initialize(parent)
192
+ node_init(parent)
193
+ self.matches=[]
194
+ self.delimiter_matches=[]
195
+ self.match_length=nil # use match_length as an override; if nil, then match_length is determined by the last node and delimiter_match
196
+ end
197
+
198
+ def match_length; @match_length || (self.next-offset) end
199
+ def next
200
+ return offset+@match_length if @match_length
201
+ ret=nil
202
+ ret=matches[-1].next if matches[-1] && (!ret || matches[-1].next > ret)
203
+ ret=delimiter_matches[-1].next if delimiter_matches[-1] && (!ret || delimiter_matches[-1].next > ret)
204
+ ret||=parent.next
205
+ end
206
+
207
+ def inspect_helper(list,options)
208
+ simple=options[:simple]
209
+ ret=list.collect {|a|a.inspect(options)}.compact
210
+ ret= if ret.length==0 then simple ? nil : "[]"
211
+ elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
212
+ else (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
213
+ end
214
+ ret
215
+ end
216
+
217
+ def inspect(options={})
218
+ if options[:simple]
219
+ c=[]
220
+ matches.each_with_index {|n,i| c<<n;c<<delimiter_matches[i]}
221
+ c=c.compact
222
+ inspect_helper(c,options)
223
+ else
224
+ ret=inspect_helper(matches,options)
225
+ ret+=" delimiters="+inspect_helper(delimiter_matches,options) if delimiter_matches.length>0
226
+ ret
227
+ end
228
+ end
229
+ end
230
+
231
+ # used for String and Regexp PatternElements
232
+ # not subclassed
233
+ class TerminalNode < Node
234
+ attr_accessor :pattern
235
+ def initialize(parent,match_length,pattern)
236
+ node_init(parent)
237
+ self.match_length=match_length
238
+ self.pattern=pattern
239
+ end
240
+
241
+ def inspect(options={})
242
+ "#{text.inspect}" unless options[:simple] && text[/^\s*$/] # if simple && node only matched white-space, return nil
243
+ end
244
+
245
+ def matches; [self]; end
246
+ end
247
+
248
+ # used when a PatternElement matchs the empty string
249
+ # Example: when the PatternElement is optional and doesn't match
250
+ # not subclassed
251
+ class EmptyNode < Node
252
+ def inspect(options={})
253
+ "EmptyNode" unless options[:simple]
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,387 @@
1
+ require File.dirname(__FILE__) + "/../lib/babel_bridge"
2
+ require File.dirname(__FILE__) + "/test_helper"
3
+
4
+ class BBTests < TestHelper
5
+
6
+ def new_module
7
+ @module_counter||=0
8
+ @module_counter+=1
9
+ "TestModule#{@module_counter}"
10
+ end
11
+
12
+ def new_parser(&block)
13
+ @parser_counter||=0
14
+ @parser_counter+=1
15
+ Object.const_set(klass_name="TestParser#{@parser_counter}",Class.new(BabelBridge::Parser,&block))
16
+ Object.const_get(klass_name).new
17
+ end
18
+
19
+ def test_foobar
20
+ parser=new_parser do #Class.new BabelBridge::Parser do
21
+ rule :foo, "foo", :bar
22
+ rule :bar, "bar"
23
+ end
24
+
25
+ assert_nil parser.parse("foo")
26
+ assert parser.parse("foobar")
27
+ end
28
+
29
+ def test_as
30
+ parser=new_parser do
31
+ rule :foo, match("foo").as(:boo)
32
+ end
33
+
34
+ assert parser.parse("foo")
35
+ assert_equal "foo", parser.parse("foo").boo.text
36
+ end
37
+
38
+ def test_many_as
39
+ parser=new_parser do
40
+ rule :foo, many("foo").as(:boo)
41
+ end
42
+
43
+ assert parser.parse("foo")
44
+ assert parser.parse("foofoo")
45
+ assert_equal "foofoo", parser.parse("foofoo").boo.text
46
+ end
47
+
48
+ def test_negative
49
+ parser=new_parser do
50
+ rule :foo, match!("boo"), /[a-zA-Z]+/
51
+ end
52
+
53
+ assert_nil parser.parse("boo")
54
+ assert parser.parse("foo")
55
+ assert parser.parse("boO")
56
+ assert parser.parse("abc")
57
+ end
58
+
59
+ def test_foo
60
+ parser=new_parser do
61
+ rule :foo, ["foo"]
62
+ end
63
+
64
+ assert p=parser.parse("foo")
65
+ assert_equal 0,p.offset
66
+ assert_equal 3,p.match_length
67
+ end
68
+
69
+ def test_regex
70
+ parser=new_parser do
71
+ rule :foo, [/[0-9]+/]
72
+ end
73
+
74
+ %w{ 0 1 10 123 1001 }.each do |numstr|
75
+ assert_equal numstr,parser.parse(numstr).text
76
+ end
77
+ end
78
+
79
+ def test_optional
80
+ parser=new_parser do
81
+ rule :foo, ["foo", :bar?]
82
+ rule :bar, ["bar"]
83
+ end
84
+
85
+ assert parser.parse("foo")
86
+ assert parser.parse("foobar")
87
+ end
88
+
89
+ def test_could
90
+ parser=new_parser do
91
+ rule :foo, could.match(/[a-z]/), /[a-zA-Z]+/
92
+ end
93
+ assert_nil parser.parse("FOO")
94
+ assert parser.parse("fOO")
95
+ assert parser.parse("foo")
96
+ end
97
+
98
+ def test_optional_middle
99
+ parser=new_parser do
100
+ rule :foo, ["foo", :bar?, "foo"]
101
+ rule :bar, ["bar"]
102
+ end
103
+
104
+ assert parser.parse("foofoo")
105
+ assert parser.parse("foobarfoo")
106
+ end
107
+
108
+ def test_greedy_optional_middle
109
+ parser=new_parser do
110
+ rule :foo, ["foo", :bar?, "foo"]
111
+ rule :bar, ["foo"]
112
+ end
113
+
114
+ assert_nil parser.parse("foofoo")
115
+ assert parser.parse("foofoofoo")
116
+ end
117
+
118
+ def test_not
119
+ parser=new_parser do
120
+ rule :foo, ["foo", :bar!]
121
+ rule :bar, ["bar"]
122
+ end
123
+
124
+ assert_nil parser.parse("foofud") # this should fail because it doesn't match the entire input
125
+ assert parser.parse("foofud",0,:foo)
126
+ assert parser.parse("foo")
127
+ assert_nil parser.parse("foobar")
128
+ end
129
+
130
+ def test_recursive
131
+ parser=new_parser do
132
+ rule :foo, ["foo", :foo?]
133
+ end
134
+
135
+ assert parser.parse("foo")
136
+ assert parser.parse("foofoo")
137
+ assert f=parser.parse("foofoofoo")
138
+
139
+ # assert_nil parser[:foo].parse("foobar")
140
+ end
141
+
142
+ def test_alternate
143
+ v1=nil
144
+ v2=nil
145
+ parser=new_parser do
146
+ v1=rule :foo, ["foo"]
147
+ v2=rule :foo, ["bar"]
148
+ end
149
+
150
+ assert r1=parser.parse("foo")
151
+ assert r2=parser.parse("bar")
152
+ assert_equal v1,r1.class
153
+ assert_equal v2,r2.class
154
+ end
155
+
156
+ def test_add
157
+ parser=new_parser do
158
+ rule :add, [:number,"+",:number]
159
+ rule :number, [/[0-9]+/]
160
+ end
161
+
162
+ assert parser.parse("1+1")
163
+ assert parser.parse("987+123")
164
+ end
165
+
166
+ def test_method
167
+ parser=new_parser do
168
+ rule :number, [/[0-9]+/] do
169
+ def number
170
+ text.to_i
171
+ end
172
+ end
173
+ end
174
+
175
+ assert p=parser.parse("123")
176
+ assert_equal 123,p.number
177
+ end
178
+
179
+ def test_adder
180
+ parser=new_parser do
181
+ rule :adder, :number, "+", :number do
182
+ def answer;
183
+ number[0].number + number[1].number
184
+ end
185
+ end
186
+ rule :number, /[0-9]+/ do
187
+ def number; text.to_i end
188
+ end
189
+ end
190
+
191
+
192
+ assert p=parser.parse("123+654")
193
+ assert_equal 777,p.answer
194
+ end
195
+
196
+ def test_adder_multiplier
197
+ parser=new_parser do
198
+
199
+ rule :adder, :multiplier, "+", :adder do
200
+ def value
201
+ multiplier.value + adder.value
202
+ end
203
+ end
204
+
205
+ rule :adder, :multiplier do
206
+ def value
207
+ multiplier.value
208
+ end
209
+ end
210
+
211
+ rule :multiplier, :number, "*", :multiplier do
212
+ def value
213
+ number.value * multiplier.value
214
+ end
215
+ end
216
+
217
+ rule :multiplier, :number do
218
+ def value
219
+ number.value
220
+ end
221
+ end
222
+
223
+ rule :number, /[0-9]+/ do
224
+ def value; text.to_i end
225
+ end
226
+
227
+ end
228
+
229
+ assert_equal 123,parser.parse("123").value
230
+ assert_equal 369,parser.parse("123*3").value
231
+ assert_equal 370,parser.parse("123*3+1").value
232
+ assert_equal 129,parser.parse("123+3*2").value
233
+ assert_equal 777,parser.parse("123+654").value
234
+ assert_equal 281,parser.parse("20*4+1+100*2").value
235
+ end
236
+
237
+ def test_rule_class
238
+ parser=new_parser do
239
+ rule :foo, "foo"
240
+ rule :foo, "bar"
241
+ node_class :foo do
242
+ def value; text end
243
+ end
244
+ end
245
+
246
+ assert_equal "foo",parser.parse("foo").value
247
+ assert_equal "bar",parser.parse("bar").value
248
+ end
249
+
250
+ def test_indexed_match_reference_with_optional
251
+ parser=new_parser do
252
+ rule :foo, :bar, :boo?, :bar do
253
+ def outter
254
+ bar[0].text + bar[1].text
255
+ end
256
+ end
257
+ rule :bar, /[0-9]+,?/
258
+ rule :boo, /[A-Z]+,?/
259
+ end
260
+
261
+ assert_equal "1,2", parser.parse("1,2").outter
262
+ assert_equal "1,3", parser.parse("1,A,3").outter
263
+
264
+ end
265
+
266
+ def test_verbose
267
+ parser=new_parser do
268
+ rule :foo, {:match=>"foo"}, {:match=>"bar",:optional=>true}
269
+ end
270
+
271
+ assert parser.parse("foo")
272
+ assert parser.parse("foobar")
273
+ end
274
+
275
+ def test_custom_parser
276
+ parser=new_parser do
277
+ rule :foo, {:parser=>lambda do |parent_node|
278
+ offset=parent_node.next
279
+ src=parent_node.src
280
+ if src.index(/[A-Z]+/,offset)==offset
281
+ endpattern=$~.to_s
282
+ if i=src.index(endpattern,offset+endpattern.length)
283
+ BabelBridge::TerminalNode.new(parent_node,i+endpattern.length-offset,"endpattern")
284
+ end
285
+ end
286
+ end}
287
+ end
288
+
289
+ assert parser.parse("END this is in the middle END")
290
+ assert_equal "END this is in END",parser.parse("END this is in END the middle END",0,:foo).text
291
+ assert_nil parser.parse("END this is in the middle EN")
292
+ end
293
+
294
+ def test_poly
295
+ parser=new_parser do
296
+ rule :foo, many("foo").as(:foo)
297
+ end
298
+ assert_equal ["foo"], parser.parse("foo").foo.collect {|f| f.text}
299
+ assert_equal ["foo","foo"], parser.parse("foofoo").foo.collect {|f| f.text}
300
+ assert_equal ["foo","foo","foo"], parser.parse("foofoofoo").foo.collect {|f| f.text}
301
+ end
302
+
303
+ def test_poly_delimiter
304
+ parser=new_parser do
305
+ rule :foo, many("foo",/ +/).as(:foo)
306
+ end
307
+ assert_equal ["foo"], parser.parse("foo").foo.collect {|f| f.text}
308
+ assert parser.parse("foo foo")
309
+ assert_equal ["foo","foo"], parser.parse("foo foo").foo.collect {|f| f.text}
310
+ end
311
+
312
+ def test_poly_post_delimiter
313
+ parser=new_parser do
314
+ rule :foo, many?("foo",/ +/,true).as(:foo), "end"
315
+ end
316
+
317
+ assert_equal 0,parser.parse("end").foo.length
318
+ assert_equal nil,parser.parse(" end")
319
+ assert_equal nil,parser.parse("foofoo end")
320
+ assert_equal ["foo"], parser.parse("foo end").foo.collect {|f| f.text}
321
+ assert_equal ["foo","foo"], parser.parse("foo foo end").foo.collect {|f| f.text}
322
+ assert_equal 5, parser.parse("foo foo foo foo foo end").foo.length
323
+ end
324
+
325
+ def test_poly_optional_delimiter
326
+ parser=new_parser do
327
+ rule :foo, many(";",match?(/ +/))
328
+ end
329
+ assert parser.parse(";")
330
+ assert parser.parse_and_puts_errors(";;")
331
+ assert parser.parse("; ; ;")
332
+ end
333
+
334
+ def test_many
335
+ assert_equal({:many=>";",:match=>true}, BabelBridge::Parser.many(";"))
336
+ end
337
+
338
+ def test_many?
339
+ assert_equal({:many=>";", :optionally=>true, :match=>true}, BabelBridge::Parser.many?(";"))
340
+ end
341
+
342
+ def test_many!
343
+ assert_equal({:many=>";", :dont=>true, :match=>true}, BabelBridge::Parser.many!(";"))
344
+ end
345
+
346
+ def test_match
347
+ assert_equal({:match=>";"}, BabelBridge::Parser.match(";"))
348
+ end
349
+
350
+ def test_match?
351
+ assert_equal({:match=>";",:optionally=>true}, BabelBridge::Parser.match?(";"))
352
+ end
353
+
354
+ def test_match!
355
+ assert_equal({:match=>";",:dont=>true}, BabelBridge::Parser.match!(";"))
356
+ end
357
+
358
+ def test_dont
359
+ assert_equal({:match=>";",:dont=>true}, BabelBridge::Parser.dont.match(";"))
360
+ end
361
+
362
+ def test_optionally
363
+ assert_equal({:match=>";",:optionally=>true}, BabelBridge::Parser.optionally.match(";"))
364
+ end
365
+
366
+ def test_could
367
+ assert_equal({:match=>";",:could=>true}, BabelBridge::Parser.could.match(";"))
368
+ end
369
+
370
+ def disabled_test_recursive_block
371
+ # PEG does have this problem, so this isn't really an error
372
+ # But maybe in the future we'll handle it better.
373
+ # SBD: Disabled 2010-10-24
374
+ parser=new_parser do
375
+ rule :foo, :foo, ";"
376
+ rule :foo, "-"
377
+
378
+ end
379
+ parser.parse "-"
380
+ end
381
+ end
382
+
383
+ tests=BBTests.new
384
+
385
+
386
+
387
+ tests.run_tests(ARGV.length>0 && ARGV)