babel_bridge 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +144 -0
- data/babel_bridge.gemspec +14 -0
- data/lib/babel_bridge.rb +529 -0
- data/lib/nodes.rb +256 -0
- data/test/test_bb.rb +387 -0
- data/test/test_helper.rb +44 -0
- metadata +60 -0
data/lib/nodes.rb
ADDED
@@ -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
|
data/test/test_bb.rb
ADDED
@@ -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)
|