babel_bridge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|