babel_bridge 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/lib/nodes/empty_node.rb +17 -5
- data/lib/nodes/many_node.rb +1 -1
- data/lib/nodes/node.rb +26 -4
- data/lib/nodes/non_terminal_node.rb +8 -7
- data/lib/nodes/rule_node.rb +1 -1
- data/lib/nodes/terminal_node.rb +1 -1
- data/lib/parser.rb +3 -8
- data/lib/pattern_element.rb +22 -27
- data/lib/rule_variant.rb +3 -2
- data/lib/version.rb +1 -1
- data/spec/bb_spec.rb +54 -7
- data/test/test_bb.rb +15 -15
- metadata +1 -1
data/lib/nodes/empty_node.rb
CHANGED
@@ -14,14 +14,26 @@ class EmptyNode < Node
|
|
14
14
|
"EmptyNode" unless options[:simple]
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
def matches; [self]; end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class RollbackWhitespaceNode < Node
|
22
|
+
def inspect(options={})
|
23
|
+
"RollbackWhitespace" unless options[:simple]
|
22
24
|
end
|
23
25
|
|
24
26
|
def matches; [self]; end
|
25
27
|
|
28
|
+
def initialize(parent)
|
29
|
+
super
|
30
|
+
self.match_length = 0
|
31
|
+
self.offset = parent.postwhitespace_range.first
|
32
|
+
end
|
33
|
+
|
34
|
+
def postwhitespace_range
|
35
|
+
@postwhitespace_range ||= offset_after_match .. offset_after_match-1
|
36
|
+
end
|
37
|
+
|
26
38
|
end
|
27
39
|
end
|
data/lib/nodes/many_node.rb
CHANGED
@@ -15,7 +15,7 @@ class ManyNode < NonTerminalNode
|
|
15
15
|
|
16
16
|
def separate_delimiter_matches
|
17
17
|
count = 0
|
18
|
-
@matches, @delimiter_matches =
|
18
|
+
@matches, @delimiter_matches = matches.partition {|el| count+=1;(count%2==1)}
|
19
19
|
@delimiter_matches = @delimiter_matches[0..@matches.length-2] #delimiter_matches should be exactly 1 shorter
|
20
20
|
update_match_length
|
21
21
|
end
|
data/lib/nodes/node.rb
CHANGED
@@ -7,7 +7,12 @@ end
|
|
7
7
|
|
8
8
|
# base class for all parse-tree nodes
|
9
9
|
class Node
|
10
|
-
attr_accessor :src,:offset,:match_length,:parent,:parser,:
|
10
|
+
attr_accessor :src,:offset,:match_length,:parent,:parser,:prewhitespace_range
|
11
|
+
|
12
|
+
# no_postwhitespace is used when parsing to temporarilly rollback the preceeding whitespace while
|
13
|
+
# attempting to match an ignore_whitespace pattern.
|
14
|
+
# It should always be false again once parsing completes or fails.
|
15
|
+
attr_accessor :no_postwhitespace
|
11
16
|
|
12
17
|
def whitespace_regexp
|
13
18
|
parser.whitespace_regexp
|
@@ -22,10 +27,27 @@ class Node
|
|
22
27
|
offset..(offset+match_length-1)
|
23
28
|
end
|
24
29
|
|
25
|
-
def
|
30
|
+
def postwhitespace_range_without_no_postwhitespace
|
26
31
|
parser.white_space_range offset_after_match
|
27
32
|
end
|
28
33
|
|
34
|
+
def postwhitespace_range
|
35
|
+
r = postwhitespace_range_without_no_postwhitespace
|
36
|
+
no_postwhitespace ? r.first..r.first-1 : r
|
37
|
+
end
|
38
|
+
|
39
|
+
def postwhitespace
|
40
|
+
src[postwhitespace_range]
|
41
|
+
end
|
42
|
+
|
43
|
+
def prewhitespace
|
44
|
+
src[prewhitespace_range]
|
45
|
+
end
|
46
|
+
|
47
|
+
# called when a ruled is matched
|
48
|
+
def matched
|
49
|
+
end
|
50
|
+
|
29
51
|
def to_s
|
30
52
|
text
|
31
53
|
end
|
@@ -45,7 +67,7 @@ class Node
|
|
45
67
|
self.parent=parent_or_parser
|
46
68
|
self.parser=parent.parser
|
47
69
|
self.offset=parent.next
|
48
|
-
self.
|
70
|
+
self.prewhitespace_range=parent.postwhitespace_range
|
49
71
|
self.src=parent.src
|
50
72
|
raise "parent node does not have parser set" unless parser
|
51
73
|
else
|
@@ -73,7 +95,7 @@ class Node
|
|
73
95
|
#********************
|
74
96
|
# info methods
|
75
97
|
#********************
|
76
|
-
def next;
|
98
|
+
def next; postwhitespace_range.last+1 end # index of first character after match and any trailing whitespace
|
77
99
|
def text; src[match_range] end # the substring in src matched
|
78
100
|
|
79
101
|
# length returns the number of sub-nodes
|
@@ -8,18 +8,18 @@ module BabelBridge
|
|
8
8
|
# rule node
|
9
9
|
# subclassed automatically by parser.rule for each unique non-terminal
|
10
10
|
class NonTerminalNode < Node
|
11
|
+
attr_accessor :last_non_empty_node
|
11
12
|
|
12
|
-
def
|
13
|
-
if
|
14
|
-
|
13
|
+
def postwhitespace_range_without_no_postwhitespace
|
14
|
+
if last_non_empty_node
|
15
|
+
last_non_empty_node.postwhitespace_range
|
15
16
|
else
|
16
|
-
|
17
|
+
prewhitespace_range || (0..-1)
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
def update_match_length
|
21
|
-
|
22
|
-
@match_length = m ? m.offset_after_match - offset : 0
|
22
|
+
@match_length = last_non_empty_node ? last_non_empty_node.offset_after_match - offset : 0
|
23
23
|
end
|
24
24
|
|
25
25
|
#*****************************
|
@@ -34,7 +34,8 @@ class NonTerminalNode < Node
|
|
34
34
|
matches.length
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
37
|
+
def add_match(node)
|
38
|
+
@last_non_empty_node = node unless node.kind_of?(EmptyNode)
|
38
39
|
matches<<node
|
39
40
|
update_match_length
|
40
41
|
end
|
data/lib/nodes/rule_node.rb
CHANGED
data/lib/nodes/terminal_node.rb
CHANGED
@@ -8,7 +8,7 @@ module BabelBridge
|
|
8
8
|
# used for String and Regexp PatternElements
|
9
9
|
# not subclassed
|
10
10
|
class TerminalNode < Node
|
11
|
-
attr_accessor :pattern, :
|
11
|
+
attr_accessor :pattern, :postwhitespace_offset
|
12
12
|
def initialize(parent,range,pattern)
|
13
13
|
node_init(parent)
|
14
14
|
self.offset = range.min
|
data/lib/parser.rb
CHANGED
@@ -18,8 +18,6 @@ class Parser
|
|
18
18
|
#
|
19
19
|
# rules can be specified as:
|
20
20
|
# rule :name, to_match1, to_match2, etc...
|
21
|
-
#or
|
22
|
-
# rule :name, [to_match1, to_match2, etc...]
|
23
21
|
#
|
24
22
|
# Can define rules INSIDE class:
|
25
23
|
# class MyParser < BabelBridge::Parser
|
@@ -41,9 +39,8 @@ class Parser
|
|
41
39
|
# This allows you to add whatever functionality you want to a your nodes in the final parse tree.
|
42
40
|
# Also note you can override the post_match method. This allows you to restructure the parse tree as it is parsed.
|
43
41
|
def rule(name,*pattern,&block)
|
44
|
-
|
45
|
-
|
46
|
-
self.root_rule||=name
|
42
|
+
rule = self.rules[name] ||= Rule.new(name,self)
|
43
|
+
self.root_rule ||= name
|
47
44
|
rule.add_variant(pattern,&block)
|
48
45
|
end
|
49
46
|
|
@@ -140,9 +137,7 @@ class Parser
|
|
140
137
|
def match(*args) PatternElementHash.new.match(*args) end
|
141
138
|
def match!(*args) PatternElementHash.new.dont.match(*args) end
|
142
139
|
|
143
|
-
|
144
|
-
# NOTE: you can ALWAYS explicitly match any trailing whitespace
|
145
|
-
def include_whitespace(*args) PatternElementHash.new.include_whitespace.match(*args) end
|
140
|
+
def rewind_whitespace; PatternElementHash.new.rewind_whitespace end
|
146
141
|
|
147
142
|
def dont; PatternElementHash.new.dont end
|
148
143
|
def optionally; PatternElementHash.new.optionally end
|
data/lib/pattern_element.rb
CHANGED
@@ -21,7 +21,7 @@ end
|
|
21
21
|
# :optional
|
22
22
|
class PatternElement
|
23
23
|
attr_accessor :parser,:optional,:negative,:name,:terminal,:could_match
|
24
|
-
attr_accessor :match,:rule_variant,:
|
24
|
+
attr_accessor :match,:rule_variant,:rewind_whitespace
|
25
25
|
|
26
26
|
#match can be:
|
27
27
|
# true, Hash, Symbol, String, Regexp
|
@@ -42,21 +42,23 @@ class PatternElement
|
|
42
42
|
|
43
43
|
# attempt to match the pattern defined in self.parser in parent_node.src starting at offset parent_node.next
|
44
44
|
def parse(parent_node)
|
45
|
+
return RollbackWhitespaceNode.new(parent_node) if rewind_whitespace
|
46
|
+
|
45
47
|
# run element parser
|
46
|
-
match=parser.call(parent_node)
|
48
|
+
match = parser.call(parent_node)
|
47
49
|
|
48
50
|
# Negative patterns (PEG: !element)
|
49
|
-
match=match ? nil : EmptyNode.new(parent_node) if negative
|
51
|
+
match = match ? nil : EmptyNode.new(parent_node) if negative
|
50
52
|
|
51
53
|
# Optional patterns (PEG: element?)
|
52
|
-
match=EmptyNode.new(parent_node) if !match && optional
|
54
|
+
match = EmptyNode.new(parent_node) if !match && optional
|
53
55
|
|
54
56
|
# Could-match patterns (PEG: &element)
|
55
|
-
match.match_length=0 if match && could_match
|
57
|
+
match.match_length = 0 if match && could_match
|
56
58
|
|
57
59
|
if !match && terminal
|
58
60
|
# log failures on Terminal patterns for debug output if overall parse fails
|
59
|
-
parent_node.parser.log_parsing_failure(
|
61
|
+
parent_node.parser.log_parsing_failure(parent_node.next,:pattern=>self.match,:node=>parent_node)
|
60
62
|
end
|
61
63
|
|
62
64
|
# return match
|
@@ -67,7 +69,7 @@ class PatternElement
|
|
67
69
|
|
68
70
|
# initialize PatternElement based on the type of: match
|
69
71
|
def init(match)
|
70
|
-
self.match=match
|
72
|
+
self.match = match
|
71
73
|
case match
|
72
74
|
when TrueClass then init_true
|
73
75
|
when String then init_string match
|
@@ -83,15 +85,6 @@ class PatternElement
|
|
83
85
|
self.parser=lambda {|parent_node| EmptyNode.new(parent_node)}
|
84
86
|
end
|
85
87
|
|
86
|
-
def match_start_index(parent_node)
|
87
|
-
if include_whitespace
|
88
|
-
parent_node.trailing_whitespace_range.first
|
89
|
-
else
|
90
|
-
parent_node.next
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
88
|
# initialize PatternElement as a parser that matches exactly the string specified
|
96
89
|
def init_string(string)
|
97
90
|
init_regex Regexp.escape(string)
|
@@ -101,7 +94,7 @@ class PatternElement
|
|
101
94
|
def init_regex(regex)
|
102
95
|
optimized_regex=/\A#{regex}/ # anchor the search
|
103
96
|
self.parser=lambda do |parent_node|
|
104
|
-
offset =
|
97
|
+
offset = parent_node.next
|
105
98
|
if parent_node.src[offset..-1].index(optimized_regex)==0
|
106
99
|
range=$~.offset(0)
|
107
100
|
range = (range.min+offset)..(range.max+offset)
|
@@ -114,16 +107,16 @@ class PatternElement
|
|
114
107
|
# initialize PatternElement as a parser that matches a named sub-rule
|
115
108
|
def init_rule(rule_name)
|
116
109
|
rule_name.to_s[/^([^?!]*)([?!])?$/]
|
117
|
-
rule_name
|
118
|
-
option
|
119
|
-
match_rule=rule_variant.rule.parser.rules[rule_name]
|
110
|
+
rule_name = $1.to_sym
|
111
|
+
option = $2
|
112
|
+
match_rule = rule_variant.rule.parser.rules[rule_name]
|
120
113
|
raise "no rule for #{rule_name}" unless match_rule
|
121
114
|
|
122
115
|
self.parser = lambda {|parent_node| match_rule.parse(parent_node)}
|
123
|
-
self.name
|
116
|
+
self.name = rule_name
|
124
117
|
case option
|
125
|
-
when "?" then self.optional=true
|
126
|
-
when "!" then self.negative=true
|
118
|
+
when "?" then self.optional = true
|
119
|
+
when "!" then self.negative = true
|
127
120
|
end
|
128
121
|
end
|
129
122
|
|
@@ -135,6 +128,9 @@ class PatternElement
|
|
135
128
|
init_many hash
|
136
129
|
elsif hash[:match]
|
137
130
|
init hash[:match]
|
131
|
+
elsif hash[:rewind_whitespace]
|
132
|
+
self.rewind_whitespace = true
|
133
|
+
return
|
138
134
|
else
|
139
135
|
raise "extended-options patterns (specified by a hash) must have either :parser=> or a :match=> set"
|
140
136
|
end
|
@@ -143,7 +139,6 @@ class PatternElement
|
|
143
139
|
self.optional ||= hash[:optional] || hash[:optionally]
|
144
140
|
self.could_match ||= hash[:could]
|
145
141
|
self.negative ||= hash[:dont]
|
146
|
-
self.include_whitespace ||= hash[:include_whitespace]
|
147
142
|
end
|
148
143
|
|
149
144
|
# initialize the PatternElement as a many-parser from hashed parameters (hash[:many] is assumed to be set)
|
@@ -165,12 +160,12 @@ class PatternElement
|
|
165
160
|
#match primary
|
166
161
|
match = single_parser.call many_node
|
167
162
|
break unless match
|
168
|
-
many_node
|
163
|
+
many_node.add_match match
|
169
164
|
|
170
165
|
#match delimiter
|
171
166
|
delimiter_match = delimiter_pattern_element.parse many_node
|
172
167
|
break unless delimiter_match
|
173
|
-
many_node
|
168
|
+
many_node.add_match delimiter_match
|
174
169
|
end
|
175
170
|
many_node.separate_delimiter_matches
|
176
171
|
else
|
@@ -178,7 +173,7 @@ class PatternElement
|
|
178
173
|
while true
|
179
174
|
match = single_parser.call many_node
|
180
175
|
break unless match
|
181
|
-
many_node
|
176
|
+
many_node.add_match match
|
182
177
|
end
|
183
178
|
end
|
184
179
|
|
data/lib/rule_variant.rb
CHANGED
@@ -26,6 +26,7 @@ class RuleVariant
|
|
26
26
|
|
27
27
|
# if parse failed
|
28
28
|
return if !match
|
29
|
+
match.matched
|
29
30
|
|
30
31
|
# parse succeeded, add to node and continue
|
31
32
|
node.add_match(match,pe.name)
|
@@ -34,6 +35,6 @@ class RuleVariant
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def inspect; pattern.collect {|a| a.inspect}.join(', '); end
|
37
|
-
def to_s; "variant_class: #{variant_node_class}, pattern: #{inspect}"; end
|
38
|
+
def to_s; "variant_class: #{variant_node_class}, pattern: #{inspect}"; end
|
39
|
+
end
|
38
40
|
end
|
39
|
-
end
|
data/lib/version.rb
CHANGED
data/spec/bb_spec.rb
CHANGED
@@ -13,9 +13,10 @@ describe BabelBridge do
|
|
13
13
|
#options
|
14
14
|
# :parser
|
15
15
|
# :failure_ok
|
16
|
-
def test_parse(string,options={})
|
16
|
+
def test_parse(string,options={},&block)
|
17
17
|
parser = options[:parser] || @parser
|
18
18
|
res = parser.parse(string)
|
19
|
+
yield res if res && block
|
19
20
|
if options[:should_fail_at]
|
20
21
|
res.should == nil
|
21
22
|
parser.failure_index.should == options[:should_fail_at]
|
@@ -69,7 +70,7 @@ describe BabelBridge do
|
|
69
70
|
ignore_whitespace
|
70
71
|
|
71
72
|
rule :pair, :statement, :end_statement, :statement
|
72
|
-
rule :end_statement,
|
73
|
+
rule :end_statement, rewind_whitespace, /([\t ]*[\n;])+/
|
73
74
|
rule :statement, "0"
|
74
75
|
end
|
75
76
|
|
@@ -89,7 +90,7 @@ describe BabelBridge do
|
|
89
90
|
ignore_whitespace
|
90
91
|
|
91
92
|
rule :pair, :statement, :end_statement, :statement
|
92
|
-
rule :end_statement,
|
93
|
+
rule :end_statement, rewind_whitespace, /([\t ]*[\n;])+/
|
93
94
|
rule :statement, "0", :one?, :one?, :one?
|
94
95
|
rule :one, "1"
|
95
96
|
end
|
@@ -106,7 +107,7 @@ describe BabelBridge do
|
|
106
107
|
new_parser do
|
107
108
|
ignore_whitespace
|
108
109
|
rule :statements, many(:statement,:end_statement)
|
109
|
-
rule :end_statement,
|
110
|
+
rule :end_statement, rewind_whitespace, /([\t ]*[;\n])+/
|
110
111
|
rule :statement, "0"
|
111
112
|
end
|
112
113
|
|
@@ -129,7 +130,7 @@ describe BabelBridge do
|
|
129
130
|
test_parse "foo-bar", :should_fail_at => 3
|
130
131
|
end
|
131
132
|
|
132
|
-
it "should work to have many
|
133
|
+
it "should work to have many-many parsing" do
|
133
134
|
new_parser do
|
134
135
|
rule :top, many(:bottom,";")
|
135
136
|
rule :bottom, many("0",",")
|
@@ -143,11 +144,11 @@ describe BabelBridge do
|
|
143
144
|
test_parse "0,0,0;0;0,0,0"
|
144
145
|
end
|
145
146
|
|
146
|
-
it "should work to have many
|
147
|
+
it "should work to have many parsing with whitespace tricks" do
|
147
148
|
new_parser do
|
148
149
|
ignore_whitespace
|
149
150
|
rule :statements, many(:statement,:end_statement)
|
150
|
-
rule :end_statement,
|
151
|
+
rule :end_statement, rewind_whitespace, /([\t ]*[;\n])+/
|
151
152
|
rule :statement, :bin_op
|
152
153
|
binary_operators_rule :bin_op, :int, ["**", [:/, :*], [:+, "-"]], :right_operators => ["**"]
|
153
154
|
rule :int, /\d+/
|
@@ -161,4 +162,50 @@ describe BabelBridge do
|
|
161
162
|
ENDCODE
|
162
163
|
end
|
163
164
|
|
165
|
+
it "should work to rewind_whitespace, :rule" do
|
166
|
+
new_parser do
|
167
|
+
ignore_whitespace
|
168
|
+
rule :all, :identifier, :parameter?, :identifier do
|
169
|
+
def to_model
|
170
|
+
[[identifier[0].to_sym, parameter && parameter.to_sym], identifier[1].to_sym]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
rule :parameter, rewind_whitespace, /[ \t]*/, rewind_whitespace, :identifier
|
174
|
+
rule :identifier, /[_a-zA-Z][_a-zA-Z0-9]*/
|
175
|
+
end
|
176
|
+
|
177
|
+
test_parse("fred\nbar") {|parsed|parsed.to_model.should == [[:fred,nil],:bar]}
|
178
|
+
test_parse("fred foo\nbar") {|parsed|parsed.to_model.should == [[:fred,:foo],:bar]}
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should work to rewind_whitespace, many" do
|
182
|
+
new_parser do
|
183
|
+
ignore_whitespace
|
184
|
+
rule :all, :identifier, :parameters?, :identifier do
|
185
|
+
def to_model
|
186
|
+
[[identifier[0].to_sym, parameters && parameters.to_s], identifier[1].to_sym]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
rule :parameters, rewind_whitespace, /[ \t]*/, rewind_whitespace, many(:identifier,",")
|
190
|
+
rule :identifier, /[_a-zA-Z][_a-zA-Z0-9]*/
|
191
|
+
end
|
192
|
+
|
193
|
+
test_parse("fred\nbar") {|parsed| parsed.to_model.should==[[:fred,nil],:bar]}
|
194
|
+
test_parse("fred foo\nbar") {|parsed| parsed.to_model.should==[[:fred,"foo"],:bar]}
|
195
|
+
test_parse("fred foo, bar\nbar") {|parsed| parsed.to_model.should==[[:fred,"foo, bar"],:bar]}
|
196
|
+
end
|
197
|
+
|
198
|
+
it "dont.match shouldn't consume any whitespace" do
|
199
|
+
new_parser do
|
200
|
+
ignore_whitespace
|
201
|
+
rule :statements, :statement, "bar"
|
202
|
+
rule :statement, :identifier, :parameters?
|
203
|
+
rule :parameters, rewind_whitespace, / */, rewind_whitespace, :identifier
|
204
|
+
rule :identifier, dont.match("end"), /[_a-zA-Z][_a-zA-Z0-9]*/
|
205
|
+
end
|
206
|
+
|
207
|
+
test_parse("fred\nbar")
|
208
|
+
test_parse("fred foo\nbar")
|
209
|
+
end
|
210
|
+
|
164
211
|
end
|
data/test/test_bb.rb
CHANGED
@@ -58,7 +58,7 @@ class BBTests < TestHelper
|
|
58
58
|
|
59
59
|
def test_foo
|
60
60
|
parser=new_parser do
|
61
|
-
rule :foo,
|
61
|
+
rule :foo, "foo"
|
62
62
|
end
|
63
63
|
|
64
64
|
assert p=parser.parse("foo")
|
@@ -88,8 +88,8 @@ class BBTests < TestHelper
|
|
88
88
|
|
89
89
|
def test_optional
|
90
90
|
parser=new_parser do
|
91
|
-
rule :foo,
|
92
|
-
rule :bar,
|
91
|
+
rule :foo, "foo", :bar?
|
92
|
+
rule :bar, "bar"
|
93
93
|
end
|
94
94
|
|
95
95
|
assert parser.parse("foo")
|
@@ -107,8 +107,8 @@ class BBTests < TestHelper
|
|
107
107
|
|
108
108
|
def test_optional_middle
|
109
109
|
parser=new_parser do
|
110
|
-
rule :foo,
|
111
|
-
rule :bar,
|
110
|
+
rule :foo, "foo", :bar?, "foo"
|
111
|
+
rule :bar, "bar"
|
112
112
|
end
|
113
113
|
|
114
114
|
assert parser.parse("foofoo")
|
@@ -117,8 +117,8 @@ class BBTests < TestHelper
|
|
117
117
|
|
118
118
|
def test_greedy_optional_middle
|
119
119
|
parser=new_parser do
|
120
|
-
rule :foo,
|
121
|
-
rule :bar,
|
120
|
+
rule :foo, "foo", :bar?, "foo"
|
121
|
+
rule :bar, "foo"
|
122
122
|
end
|
123
123
|
|
124
124
|
assert_nil parser.parse("foofoo")
|
@@ -127,8 +127,8 @@ class BBTests < TestHelper
|
|
127
127
|
|
128
128
|
def test_not
|
129
129
|
parser=new_parser do
|
130
|
-
rule :foo,
|
131
|
-
rule :bar,
|
130
|
+
rule :foo, "foo", :bar!
|
131
|
+
rule :bar, "bar"
|
132
132
|
end
|
133
133
|
|
134
134
|
assert_nil parser.parse("foofud") # this should fail because it doesn't match the entire input
|
@@ -139,7 +139,7 @@ class BBTests < TestHelper
|
|
139
139
|
|
140
140
|
def test_recursive
|
141
141
|
parser=new_parser do
|
142
|
-
rule :foo,
|
142
|
+
rule :foo, "foo", :foo?
|
143
143
|
end
|
144
144
|
|
145
145
|
assert parser.parse("foo")
|
@@ -153,8 +153,8 @@ class BBTests < TestHelper
|
|
153
153
|
v1=nil
|
154
154
|
v2=nil
|
155
155
|
parser=new_parser do
|
156
|
-
v1=rule :foo,
|
157
|
-
v2=rule :foo,
|
156
|
+
v1=rule :foo, "foo"
|
157
|
+
v2=rule :foo, "bar"
|
158
158
|
end
|
159
159
|
|
160
160
|
assert r1=parser.parse("foo")
|
@@ -165,8 +165,8 @@ class BBTests < TestHelper
|
|
165
165
|
|
166
166
|
def test_add
|
167
167
|
parser=new_parser do
|
168
|
-
rule :add,
|
169
|
-
rule :number,
|
168
|
+
rule :add, :number, "+", :number
|
169
|
+
rule :number, /[0-9]+/
|
170
170
|
end
|
171
171
|
|
172
172
|
assert parser.parse("1+1")
|
@@ -175,7 +175,7 @@ class BBTests < TestHelper
|
|
175
175
|
|
176
176
|
def test_method
|
177
177
|
parser=new_parser do
|
178
|
-
rule :number,
|
178
|
+
rule :number, /[0-9]+/ do
|
179
179
|
def number
|
180
180
|
text.to_i
|
181
181
|
end
|