babel_bridge 0.3.1 → 0.4.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/babel_bridge.gemspec +18 -15
- data/examples/turing/turing++.rb +154 -0
- data/examples/turing/turing_demo.rb +19 -19
- data/lib/nodes.rb +2 -1
- data/lib/nodes/empty_node.rb +11 -1
- data/lib/nodes/many_node.rb +37 -48
- data/lib/nodes/node.rb +26 -32
- data/lib/nodes/non_terminal_node.rb +23 -90
- data/lib/nodes/rule_node.rb +102 -0
- data/lib/nodes/terminal_node.rb +6 -18
- data/lib/parser.rb +45 -31
- data/lib/pattern_element.rb +34 -47
- data/lib/rule.rb +3 -3
- data/lib/shell.rb +35 -16
- data/lib/version.rb +2 -2
- data/spec/bb_spec.rb +164 -0
- data/test/test_bb.rb +12 -17
- metadata +44 -5
data/lib/rule.rb
CHANGED
@@ -4,10 +4,10 @@ class Rule
|
|
4
4
|
attr_accessor :name, :variants, :parser, :node_class
|
5
5
|
|
6
6
|
private
|
7
|
-
# creates a subclass of the
|
7
|
+
# creates a subclass of the RuleNode for this Rule's node_class
|
8
8
|
def create_node_class
|
9
9
|
class_name = "#{parser.module_name}_#{name}_node".camelize
|
10
|
-
parser.const_set class_name, Class.new(
|
10
|
+
parser.const_set class_name, Class.new(RuleNode)
|
11
11
|
end
|
12
12
|
|
13
13
|
# creates a new sub_class of the node_class for a variant
|
@@ -59,4 +59,4 @@ class Rule
|
|
59
59
|
"#{variants.collect {|v|v.to_s}.join("\n\t")}"
|
60
60
|
end
|
61
61
|
end
|
62
|
-
end
|
62
|
+
end
|
data/lib/shell.rb
CHANGED
@@ -7,19 +7,42 @@ class Shell
|
|
7
7
|
@parser = parser
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
10
|
+
def pretty_print_parse_tree(parse_tree_node)
|
11
|
+
puts "\nParse tree:\n #{parse_tree_node.inspect.gsub("\n","\n ")}\n\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Providing a block overrides all evaluate behavor and simply: yield parse_tree_node, self
|
15
|
+
# elsif parse_tree_node responds to "evaluate": puts_result parse_tree_node.evaluate
|
16
|
+
# else pretty-print the parse-tree
|
17
|
+
# rescue and pretty-print errors
|
18
|
+
def evaluate(parse_tree_node, &block)
|
19
|
+
if block
|
20
|
+
yield parse_tree_node, self
|
21
|
+
elsif parse_tree_node.respond_to? :evaluate
|
22
|
+
puts_result parse_tree_node.evaluate
|
13
23
|
else
|
14
|
-
|
15
|
-
parse_tree_node.inspect.gsub("\n","\n ")+"\n\n"
|
24
|
+
pretty_print_parse_tree parse_tree_node
|
16
25
|
end
|
17
26
|
rescue Exception => e
|
18
|
-
|
27
|
+
errputs "Error evaluating parse tree: #{e}\n" + ["Backtrace:",e.backtrace].flatten.join("\n ")
|
28
|
+
pretty_print_parse_tree parse_tree_node
|
29
|
+
end
|
30
|
+
|
31
|
+
def puts(str)
|
32
|
+
@stdout.puts str
|
33
|
+
end
|
34
|
+
|
35
|
+
def puts_result(str)
|
36
|
+
@stdout.puts " => "+str.inspect
|
37
|
+
end
|
38
|
+
|
39
|
+
def errputs(str)
|
40
|
+
@stderr.puts str
|
19
41
|
end
|
20
42
|
|
21
|
-
#
|
22
|
-
#
|
43
|
+
# Each line of input is parsed.
|
44
|
+
# If parser fails, output explaination of why.
|
45
|
+
# If parser succeeds, evaluate parse_tree_node, &block
|
23
46
|
def start(options={},&block)
|
24
47
|
@stdout = options[:stdout] || $stdout
|
25
48
|
@stderr = options[:stdout] || @stdout
|
@@ -27,15 +50,11 @@ class Shell
|
|
27
50
|
while line = @stdin == $stdin ? Readline.readline("> ", true) : @stdin.gets
|
28
51
|
line.strip!
|
29
52
|
next if line.length==0
|
30
|
-
|
31
|
-
if
|
32
|
-
|
33
|
-
yield ret
|
34
|
-
else
|
35
|
-
@stdout.puts evaluate(ret)
|
36
|
-
end
|
53
|
+
parse_tree_node = parser.parse line
|
54
|
+
if parse_tree_node
|
55
|
+
evaluate parse_tree_node, &block
|
37
56
|
else
|
38
|
-
|
57
|
+
errputs parser.parser_failure_info :verbose => true
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
data/lib/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module BabelBridge
|
2
|
-
VERSION = "0.
|
3
|
-
end
|
2
|
+
VERSION = "0.4.0"
|
3
|
+
end
|
data/spec/bb_spec.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","lib","babel_bridge")
|
2
|
+
|
3
|
+
describe BabelBridge do
|
4
|
+
attr_accessor :parser
|
5
|
+
|
6
|
+
def new_parser(&block)
|
7
|
+
$parser_counter||=0
|
8
|
+
$parser_counter+=1
|
9
|
+
Object.const_set(klass_name="TestParser#{$parser_counter}",Class.new(BabelBridge::Parser,&block))
|
10
|
+
@parser=Object.const_get(klass_name).new
|
11
|
+
end
|
12
|
+
|
13
|
+
#options
|
14
|
+
# :parser
|
15
|
+
# :failure_ok
|
16
|
+
def test_parse(string,options={})
|
17
|
+
parser = options[:parser] || @parser
|
18
|
+
res = parser.parse(string)
|
19
|
+
if options[:should_fail_at]
|
20
|
+
res.should == nil
|
21
|
+
parser.failure_index.should == options[:should_fail_at]
|
22
|
+
elsif !options[:failure_ok]
|
23
|
+
puts parser.parser_failure_info :verbose => true unless res
|
24
|
+
res.should_not == nil
|
25
|
+
end
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
it "ignore_whitespace should work" do
|
30
|
+
new_parser do
|
31
|
+
ignore_whitespace
|
32
|
+
rule :foobar, "foo", "bar"
|
33
|
+
end
|
34
|
+
|
35
|
+
test_parse "foobar"
|
36
|
+
test_parse "foo bar"
|
37
|
+
test_parse "foo \t \r \f \n bar"
|
38
|
+
test_parse " foobar"
|
39
|
+
test_parse "foobar "
|
40
|
+
end
|
41
|
+
|
42
|
+
it "the failure_index should be the furthest point reached, even if we managed to successfully match less" do
|
43
|
+
new_parser do
|
44
|
+
ignore_whitespace
|
45
|
+
|
46
|
+
rule :statement, "while", :statement, "end"
|
47
|
+
rule :statement, "0"
|
48
|
+
rule :statement, /[_a-zA-Z][_a-zA-Z0-9]*/
|
49
|
+
end
|
50
|
+
|
51
|
+
res = test_parse "while 0 foo", :should_fail_at => 8
|
52
|
+
end
|
53
|
+
|
54
|
+
it "parsing twice when the first didn't match all input should, but the second just failed, shouldn't report 'did not match entire input'" do
|
55
|
+
new_parser do
|
56
|
+
rule :foo, "foo"
|
57
|
+
end
|
58
|
+
|
59
|
+
partial_match_string = "did not match entire input"
|
60
|
+
|
61
|
+
test_parse "foobar", :failure_ok => true
|
62
|
+
parser.parser_failure_info[partial_match_string].should == partial_match_string
|
63
|
+
test_parse "bar", :failure_ok => true
|
64
|
+
parser.parser_failure_info[partial_match_string].should == nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it "include_whitespace should work" do
|
68
|
+
new_parser do
|
69
|
+
ignore_whitespace
|
70
|
+
|
71
|
+
rule :pair, :statement, :end_statement, :statement
|
72
|
+
rule :end_statement, include_whitespace(/([\t ]*[\n;])+/)
|
73
|
+
rule :statement, "0"
|
74
|
+
end
|
75
|
+
|
76
|
+
test_parse "0;0"
|
77
|
+
test_parse "0\n0"
|
78
|
+
test_parse "0 ; 0"
|
79
|
+
test_parse "0 ; ; 0"
|
80
|
+
test_parse "0 \n 0"
|
81
|
+
test_parse "0 \n \n 0"
|
82
|
+
test_parse "0 \n ; \n 0"
|
83
|
+
test_parse "0 ; \n ;0"
|
84
|
+
test_parse "0 0", :should_fail_at => 1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "include_whitespace should work even with EmptyNodes" do
|
88
|
+
new_parser do
|
89
|
+
ignore_whitespace
|
90
|
+
|
91
|
+
rule :pair, :statement, :end_statement, :statement
|
92
|
+
rule :end_statement, include_whitespace(/([\t ]*[\n;])+/)
|
93
|
+
rule :statement, "0", :one?, :one?, :one?
|
94
|
+
rule :one, "1"
|
95
|
+
end
|
96
|
+
|
97
|
+
test_parse "0;0"
|
98
|
+
test_parse "01;0"
|
99
|
+
test_parse "0\n0"
|
100
|
+
test_parse "01\n0"
|
101
|
+
test_parse "011\n0"
|
102
|
+
test_parse "0111\n0"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "include_whitespace should work with many" do
|
106
|
+
new_parser do
|
107
|
+
ignore_whitespace
|
108
|
+
rule :statements, many(:statement,:end_statement)
|
109
|
+
rule :end_statement, include_whitespace(/([\t ]*[;\n])+/)
|
110
|
+
rule :statement, "0"
|
111
|
+
end
|
112
|
+
|
113
|
+
test_parse "0"
|
114
|
+
test_parse "0\n0"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "custom ignore_whitespace should work" do
|
118
|
+
new_parser do
|
119
|
+
ignore_whitespace /[_\s]*/
|
120
|
+
|
121
|
+
rule :foobar, "foo", "bar"
|
122
|
+
end
|
123
|
+
|
124
|
+
test_parse "foobar"
|
125
|
+
test_parse "foo_bar"
|
126
|
+
test_parse "foo_ bar"
|
127
|
+
test_parse "foo _ bar"
|
128
|
+
test_parse "foo bar"
|
129
|
+
test_parse "foo-bar", :should_fail_at => 3
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should work to have many many parsing" do
|
133
|
+
new_parser do
|
134
|
+
rule :top, many(:bottom,";")
|
135
|
+
rule :bottom, many("0",",")
|
136
|
+
end
|
137
|
+
|
138
|
+
test_parse "0"
|
139
|
+
test_parse "0;0"
|
140
|
+
test_parse "0,0"
|
141
|
+
test_parse "0,0;0"
|
142
|
+
test_parse "0;0,0"
|
143
|
+
test_parse "0,0,0;0;0,0,0"
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should work to have many many parsing with whitespace tricks" do
|
147
|
+
new_parser do
|
148
|
+
ignore_whitespace
|
149
|
+
rule :statements, many(:statement,:end_statement)
|
150
|
+
rule :end_statement, include_whitespace(/([\t ]*[;\n])+/)
|
151
|
+
rule :statement, :bin_op
|
152
|
+
binary_operators_rule :bin_op, :int, ["**", [:/, :*], [:+, "-"]], :right_operators => ["**"]
|
153
|
+
rule :int, /\d+/
|
154
|
+
end
|
155
|
+
|
156
|
+
test_parse "0"
|
157
|
+
test_parse <<ENDCODE
|
158
|
+
3+4
|
159
|
+
9-2
|
160
|
+
4
|
161
|
+
ENDCODE
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
data/test/test_bb.rb
CHANGED
@@ -292,7 +292,8 @@ class BBTests < TestHelper
|
|
292
292
|
if src[offset..-1].index(/\A[A-Z]+/)==0
|
293
293
|
endpattern=$~.to_s
|
294
294
|
if i=src.index(endpattern,offset+endpattern.length)
|
295
|
-
|
295
|
+
range = offset..(i+endpattern.length)
|
296
|
+
BabelBridge::TerminalNode.new(parent_node,range,"endpattern")
|
296
297
|
end
|
297
298
|
end
|
298
299
|
end}
|
@@ -322,20 +323,6 @@ class BBTests < TestHelper
|
|
322
323
|
assert_equal ["foo","foo"], parser.parse("foo foo").foo.collect {|f| f.text}
|
323
324
|
end
|
324
325
|
|
325
|
-
def test_poly_post_delimiter
|
326
|
-
parser=new_parser do
|
327
|
-
rule :foo, many?("foo",/ +/,true).as(:foo), match("end").as(:end)
|
328
|
-
end
|
329
|
-
|
330
|
-
assert_equal nil,parser.parse("end").foo
|
331
|
-
assert_equal "end",parser.parse("end").end.to_s
|
332
|
-
assert_equal nil,parser.parse(" end")
|
333
|
-
assert_equal nil,parser.parse("foofoo end")
|
334
|
-
assert_equal ["foo"], parser.parse("foo end").foo.collect {|f| f.text}
|
335
|
-
assert_equal ["foo","foo"], parser.parse("foo foo end").foo.collect {|f| f.text}
|
336
|
-
assert_equal 5, parser.parse("foo foo foo foo foo end").foo.length
|
337
|
-
end
|
338
|
-
|
339
326
|
def test_poly_optional_delimiter
|
340
327
|
parser=new_parser do
|
341
328
|
rule :foo, many(";",match?(/ +/))
|
@@ -395,11 +382,11 @@ class BBTests < TestHelper
|
|
395
382
|
def test_binary_operator_rule
|
396
383
|
parser=new_parser do
|
397
384
|
binary_operators_rule :bin_op, :int, ["**", [:/, :*], [:+, "-"]], :right_operators => ["**"] do
|
398
|
-
def evaluate
|
385
|
+
def evaluate
|
399
386
|
"(#{left.evaluate}#{operator}#{right.evaluate})"
|
400
387
|
end
|
401
388
|
end
|
402
|
-
|
389
|
+
|
403
390
|
rule :int, /[-]?[0-9]+/ do
|
404
391
|
def evaluate; to_s; end
|
405
392
|
end
|
@@ -413,6 +400,14 @@ class BBTests < TestHelper
|
|
413
400
|
assert_equal "(5**(6**7))", parser.parse("5**6**7").evaluate
|
414
401
|
end
|
415
402
|
|
403
|
+
def test_dont_parser
|
404
|
+
parser = new_parser do
|
405
|
+
rule :option, dont.match(/msg.*/), /.*/
|
406
|
+
end
|
407
|
+
assert_nil parser.parse "msg test"
|
408
|
+
assert parser.parse "non test"
|
409
|
+
end
|
410
|
+
|
416
411
|
def test_line_col
|
417
412
|
assert_equal [1,1], "".line_col(0)
|
418
413
|
assert_equal [1,1], " ".line_col(0)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: babel_bridge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,10 +10,45 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
date: 2010-11-28 00:00:00.000000000 Z
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: ! 'Babel Bridge is an object oriented parser generator for parsing expression
|
47
|
+
grammars (PEG).
|
48
|
+
|
49
|
+
Generate memoizing packrat parsers 100% in Ruby code with a simple embedded DSL.
|
50
|
+
|
51
|
+
'
|
17
52
|
email: shanebdavis@gmail.com
|
18
53
|
executables: []
|
19
54
|
extensions: []
|
@@ -23,11 +58,13 @@ files:
|
|
23
58
|
- babel_bridge.gemspec
|
24
59
|
- test/test_bb.rb
|
25
60
|
- test/test_helper.rb
|
61
|
+
- spec/bb_spec.rb
|
26
62
|
- lib/babel_bridge.rb
|
27
63
|
- lib/nodes/empty_node.rb
|
28
64
|
- lib/nodes/many_node.rb
|
29
65
|
- lib/nodes/node.rb
|
30
66
|
- lib/nodes/non_terminal_node.rb
|
67
|
+
- lib/nodes/rule_node.rb
|
31
68
|
- lib/nodes/terminal_node.rb
|
32
69
|
- lib/nodes.rb
|
33
70
|
- lib/parser.rb
|
@@ -43,6 +80,7 @@ files:
|
|
43
80
|
- examples/test.rb
|
44
81
|
- examples/turing/examples.turing
|
45
82
|
- examples/turing/notes.rb
|
83
|
+
- examples/turing/turing++.rb
|
46
84
|
- examples/turing/turing.rb
|
47
85
|
- examples/turing/turing00.rb
|
48
86
|
- examples/turing/turing01.rb
|
@@ -83,3 +121,4 @@ signing_key:
|
|
83
121
|
specification_version: 3
|
84
122
|
summary: A Ruby-based parser-generator based on Parsing Expression Grammars.
|
85
123
|
test_files: []
|
124
|
+
has_rdoc: false
|