babel_bridge 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/test.rb +31 -0
- data/examples/turing/examples.turing +33 -0
- data/examples/turing/notes.rb +111 -0
- data/examples/turing/turing.rb +13 -6
- data/examples/turing/turing00.rb +7 -0
- data/examples/turing/turing01.rb +8 -0
- data/examples/turing/turing02.rb +10 -0
- data/examples/turing/turing03.rb +19 -0
- data/examples/turing/turing04.rb +19 -0
- data/examples/turing/turing05.rb +18 -0
- data/examples/turing/turing06.rb +20 -0
- data/examples/turing/turing07.rb +21 -0
- data/examples/turing/turing08.rb +26 -0
- data/examples/turing/turing09.rb +37 -0
- data/examples/turing/turing10.rb +53 -0
- data/examples/turing/turing11.rb +63 -0
- data/examples/turing/turing12.rb +71 -0
- data/examples/turing/turing_demo.rb +71 -0
- data/lib/nodes/many_node.rb +3 -1
- data/lib/nodes/node.rb +7 -0
- data/lib/parser.rb +51 -32
- data/lib/pattern_element.rb +10 -1
- data/lib/rule_variant.rb +1 -7
- data/lib/shell.rb +13 -6
- data/lib/string.rb +4 -1
- data/lib/tools.rb +27 -8
- data/lib/version.rb +1 -1
- data/test/test_bb.rb +17 -2
- metadata +18 -2
- data/examples/turing/test.rb +0 -28
data/examples/test.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TestParser < BabelBridge::Parser
|
4
|
+
|
5
|
+
rule :expr, :bin_op do
|
6
|
+
def evaluate
|
7
|
+
output = bin_op.evaluate
|
8
|
+
input = to_s
|
9
|
+
puts "bbtest: #{output} = #{eval output}"
|
10
|
+
puts "ruby: #{input} = #{eval input}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
#rule :bin_op, :operand, "+", :operand do
|
15
|
+
#rule :bin_op, many(:operand,"+") do
|
16
|
+
#rule :bin_op, :operand, /([-+\/*])|(\*\*)/, :bin_op do
|
17
|
+
#rule :bin_op, many(:operand,/([-+\/*])|(\*\*)/) do
|
18
|
+
binary_operators_rule :bin_op, :operand, ["**", [:/, :*], [:+, "-"]], :right_operators => ["**"] do
|
19
|
+
def evaluate
|
20
|
+
"(#{left.evaluate}#{operator}#{right.evaluate})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
rule :operand, "(", :bin_op, ")"
|
25
|
+
|
26
|
+
rule :operand, /[-]?[0-9]+/ do
|
27
|
+
def evaluate; to_s; end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
BabelBridge::Shell.new(TestParser.new).start
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# this language doesn't support comments, should copy-paste the examples between
|
2
|
+
# the comments
|
3
|
+
|
4
|
+
# calculate 2^[0]
|
5
|
+
[0]=10;[1]=1;while [0]>0 do [0]=[0]-1;[1]=[1]*2 end;[1]
|
6
|
+
|
7
|
+
# return the [0]th number in the fibonancci sequence:
|
8
|
+
# [0] == fib number to output
|
9
|
+
# [1] == current fib number
|
10
|
+
# [2] == fib[[1]]
|
11
|
+
# [3] == fib[[1]-1]
|
12
|
+
# [4] == temp
|
13
|
+
[0]=5;[1]=2;[2]=[3]=1;while [1]<[0] do [4]=[2]+[3];[2]=[3];[3]=[4];[1]=[1]+1 end;[3]
|
14
|
+
|
15
|
+
# simulate a turing machine
|
16
|
+
# [0] == current state
|
17
|
+
# [1] == head position
|
18
|
+
# [2] == number of symbols this machine recognizes
|
19
|
+
# [3] == current state + read value offset
|
20
|
+
# [state*[2]*3+10] == write at the current head position(nil or any number)
|
21
|
+
# [state*[2]*3+11] == move head amount (-1, 0, 1)
|
22
|
+
# [state*[2]*3+12] == next-state (states starting from 0)
|
23
|
+
# [10000+] == tape - pick a large enough constant that the turing machine doesn't corrupt your states
|
24
|
+
# -1 is the stop-state
|
25
|
+
# Assumes you initalized your turing program for all your states.
|
26
|
+
[0]=-1;
|
27
|
+
[1]=10000;
|
28
|
+
while [0]>=0 do
|
29
|
+
[3] = 10 + ([0]*[2]+[[1]]) * 3;
|
30
|
+
[[1]] = [[3]];
|
31
|
+
[1] = [1] + [[3]+1];
|
32
|
+
[0] = [[3]+2];
|
33
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
turing0: #babel-bridge setup
|
2
|
+
turing1: #introducing rules -> show interactive shell in action
|
3
|
+
rule :int, /[-]?[0-9]+/
|
4
|
+
|
5
|
+
turing2: #the "add" rule (with 2 variants to support recursive matching)
|
6
|
+
rule :add, :int, "+", :add
|
7
|
+
rule :add, :int
|
8
|
+
|
9
|
+
turing3: #add evaluate methods
|
10
|
+
rule :add, :int, "+", :add do
|
11
|
+
def evaluate
|
12
|
+
int.evaluate + add.evaluate
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rule :add, :int
|
16
|
+
|
17
|
+
rule :int, /[-]?[0-9]+/ do
|
18
|
+
def evaluate
|
19
|
+
to_s.to_i
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
turing4: #multiple operators, but "3*10+2" or "2+10*3" breaks!
|
24
|
+
# add :operator rule, update evaluate
|
25
|
+
rule :add, :int, /[-+\/*]/, :add do
|
26
|
+
def evaluate
|
27
|
+
int.evaluate.send match[1].to_s.to_sym, add.evaluate
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
turing5: # binary_operators_rule
|
32
|
+
# replace all but the int rule with:
|
33
|
+
binary_operators_rule :statement, :int, [[:/, :*], [:+, :-]] do
|
34
|
+
def evaluate
|
35
|
+
left.evaluate.send operator, right.evaluate
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
turing6: # add parenthesis
|
40
|
+
# rename :int to :operand
|
41
|
+
rule :operand, "(", :statement, ")"
|
42
|
+
|
43
|
+
turing7: # show "5 + 2" fails -> ignore_whitespace
|
44
|
+
ignore_whitespace
|
45
|
+
|
46
|
+
turing8: # add comparators
|
47
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-],
|
48
|
+
[:<, :<=, :>, :>=, :==]] do
|
49
|
+
def evaluate
|
50
|
+
case operator
|
51
|
+
when :<, :<=, :>, :>=, :==
|
52
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
53
|
+
else
|
54
|
+
left.evaluate.send operator, right.evaluate
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
turing9: # add if
|
60
|
+
rule :statement, "if", :statement, "then", :statement, :else_clause?, "end" do
|
61
|
+
def evaluate
|
62
|
+
if matches[1].evaluate
|
63
|
+
matches[3].evaluate
|
64
|
+
else
|
65
|
+
else_clause.evaluate if else_clause
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rule :else_clause, "else", :statement
|
70
|
+
|
71
|
+
turing10: #add store
|
72
|
+
|
73
|
+
def store
|
74
|
+
@store||=[]
|
75
|
+
end
|
76
|
+
|
77
|
+
rule :operand, "[", :statement, "]", "=", :statement do
|
78
|
+
def evaluate
|
79
|
+
parser.store[statement[0].evaluate] = statement[1].evaluate
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
rule :operand, "[", :statement, "]" do
|
84
|
+
def evaluate
|
85
|
+
parser.store[statement.evaluate]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
turing11: #statements
|
90
|
+
# update "if" bodies to have statements
|
91
|
+
|
92
|
+
rule :statements, many(:statement,";"), match?(";") do
|
93
|
+
def evaluate
|
94
|
+
ret = nil
|
95
|
+
statement.each do |s|
|
96
|
+
ret = s.evaluate
|
97
|
+
end
|
98
|
+
ret
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
turing12: # while
|
103
|
+
|
104
|
+
rule :statement, "while", :statement, "do", :statements, "end" do
|
105
|
+
def evaluate
|
106
|
+
while statement.evaluate
|
107
|
+
statements.evaluate
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
data/examples/turing/turing.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
# A turing complete programming language
|
3
|
+
# Example program that computes the power of two of the value stored in the [0] register:
|
4
|
+
# => [0]=32;[1]=1;while [0]>0 do [1] = [1] * 2; [0] = [0]-1; end;[1]
|
5
|
+
|
6
|
+
# TODO: add variables and functions
|
2
7
|
|
3
8
|
class TuringParser < BabelBridge::Parser
|
4
9
|
ignore_whitespace
|
10
|
+
# TODO: add "whole_words" option to convert all literal matching patterns that are words into /word\b/
|
11
|
+
|
5
12
|
|
6
13
|
def store
|
7
14
|
@store||=[]
|
@@ -17,18 +24,18 @@ class TuringParser < BabelBridge::Parser
|
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
20
|
-
rule :statement,
|
27
|
+
rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
|
21
28
|
def evaluate
|
22
|
-
if statement
|
23
|
-
|
29
|
+
if statement.evaluate
|
30
|
+
statements.evaluate
|
24
31
|
else
|
25
32
|
else_clause.evaluate if else_clause
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
29
|
-
rule :else_clause,
|
36
|
+
rule :else_clause, "else", :statements
|
30
37
|
|
31
|
-
rule :statement,
|
38
|
+
rule :statement, "while", :statement, "do", :statements, "end" do
|
32
39
|
def evaluate
|
33
40
|
while statement.evaluate
|
34
41
|
statements.evaluate
|
@@ -36,7 +43,7 @@ class TuringParser < BabelBridge::Parser
|
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
39
|
-
binary_operators_rule :statement, :operand, [[
|
46
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
40
47
|
def evaluate
|
41
48
|
case operator
|
42
49
|
when :<, :<=, :>, :>=, :==
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
|
5
|
+
rule :add, :int, "+", :add do
|
6
|
+
def evaluate
|
7
|
+
int.evaluate + add.evaluate
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rule :add, :int
|
11
|
+
|
12
|
+
rule :int, /[-]?[0-9]+/ do
|
13
|
+
def evaluate
|
14
|
+
to_s.to_i
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
|
5
|
+
rule :add, :int, /[-+\/*]/, :add do
|
6
|
+
def evaluate
|
7
|
+
int.evaluate.send matches[1].to_s.to_sym, add.evaluate
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rule :add, :int
|
11
|
+
|
12
|
+
rule :int, /[-]?[0-9]+/ do
|
13
|
+
def evaluate
|
14
|
+
to_s.to_i
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
|
5
|
+
binary_operators_rule :statement, :int, [[:/, :*], [:+, :-]] do
|
6
|
+
def evaluate
|
7
|
+
left.evaluate.send operator, right.evaluate
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
rule :int, /[-]?[0-9]+/ do
|
12
|
+
def evaluate
|
13
|
+
to_s.to_i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
|
5
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-]] do
|
6
|
+
def evaluate
|
7
|
+
left.evaluate.send operator, right.evaluate
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
rule :operand, "(", :statement, ")"
|
12
|
+
|
13
|
+
rule :operand, /[-]?[0-9]+/ do
|
14
|
+
def evaluate
|
15
|
+
to_s.to_i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-]] do
|
7
|
+
def evaluate
|
8
|
+
left.evaluate.send operator, right.evaluate
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
rule :operand, "(", :statement, ")"
|
13
|
+
|
14
|
+
rule :operand, /[-]?[0-9]+/ do
|
15
|
+
def evaluate
|
16
|
+
to_s.to_i
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
7
|
+
def evaluate
|
8
|
+
case operator
|
9
|
+
when :<, :<=, :>, :>=, :==
|
10
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
11
|
+
else
|
12
|
+
left.evaluate.send operator, right.evaluate
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
rule :operand, "(", :statement, ")"
|
18
|
+
|
19
|
+
rule :operand, /[-]?[0-9]+/ do
|
20
|
+
def evaluate
|
21
|
+
to_s.to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
rule :statement, "if", :statement, "then", :statement, :else_clause?, "end" do
|
7
|
+
def evaluate
|
8
|
+
if matches[1].evaluate
|
9
|
+
matches[3].evaluate
|
10
|
+
else
|
11
|
+
else_clause.evaluate if else_clause
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rule :else_clause, "else", :statement
|
16
|
+
|
17
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
18
|
+
def evaluate
|
19
|
+
case operator
|
20
|
+
when :<, :<=, :>, :>=, :==
|
21
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
22
|
+
else
|
23
|
+
left.evaluate.send operator, right.evaluate
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
rule :operand, "(", :statement, ")"
|
29
|
+
|
30
|
+
rule :operand, /[-]?[0-9]+/ do
|
31
|
+
def evaluate
|
32
|
+
to_s.to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
def store
|
7
|
+
@store||=[]
|
8
|
+
end
|
9
|
+
|
10
|
+
rule :statement, "if", :statement, "then", :statement, :else_clause?, "end" do
|
11
|
+
def evaluate
|
12
|
+
if statement[0].evaluate
|
13
|
+
statement[1].evaluate
|
14
|
+
else
|
15
|
+
else_clause.evaluate if else_clause
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
rule :else_clause, "else", :statement
|
20
|
+
|
21
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
22
|
+
def evaluate
|
23
|
+
case operator
|
24
|
+
when :<, :<=, :>, :>=, :==
|
25
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
26
|
+
else
|
27
|
+
left.evaluate.send operator, right.evaluate
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
rule :operand, "(", :statement, ")"
|
33
|
+
|
34
|
+
rule :operand, "[", :statement, "]", "=", :statement do
|
35
|
+
def evaluate
|
36
|
+
parser.store[statement[0].evaluate] = statement[1].evaluate
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
rule :operand, "[", :statement, "]" do
|
41
|
+
def evaluate
|
42
|
+
parser.store[statement.evaluate]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
rule :operand, /[-]?[0-9]+/ do
|
47
|
+
def evaluate
|
48
|
+
to_s.to_i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
def store
|
7
|
+
@store||=[]
|
8
|
+
end
|
9
|
+
|
10
|
+
rule :statements, many(:statement,";"), match?(";") do
|
11
|
+
def evaluate
|
12
|
+
ret = nil
|
13
|
+
statement.each do |s|
|
14
|
+
ret = s.evaluate
|
15
|
+
end
|
16
|
+
ret
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
|
21
|
+
def evaluate
|
22
|
+
if statement.evaluate
|
23
|
+
statements.evaluate
|
24
|
+
else
|
25
|
+
else_clause.evaluate if else_clause
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rule :else_clause, "else", :statements
|
30
|
+
|
31
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
32
|
+
def evaluate
|
33
|
+
case operator
|
34
|
+
when :<, :<=, :>, :>=, :==
|
35
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
36
|
+
else
|
37
|
+
left.evaluate.send operator, right.evaluate
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
rule :operand, "(", :statement, ")"
|
43
|
+
|
44
|
+
rule :operand, "[", :statement, "]", "=", :statement do
|
45
|
+
def evaluate
|
46
|
+
parser.store[statement[0].evaluate] = statement[1].evaluate
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
rule :operand, "[", :statement, "]" do
|
51
|
+
def evaluate
|
52
|
+
parser.store[statement.evaluate]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
rule :operand, /[-]?[0-9]+/ do
|
57
|
+
def evaluate
|
58
|
+
to_s.to_i
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
def store
|
7
|
+
@store||=[]
|
8
|
+
end
|
9
|
+
|
10
|
+
rule :statements, many(:statement,";"), match?(";") do
|
11
|
+
def evaluate
|
12
|
+
ret = nil
|
13
|
+
statement.each do |s|
|
14
|
+
ret = s.evaluate
|
15
|
+
end
|
16
|
+
ret
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
|
21
|
+
def evaluate
|
22
|
+
if statement.evaluate
|
23
|
+
statements.evaluate
|
24
|
+
else
|
25
|
+
else_clause.evaluate if else_clause
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rule :else_clause, "else", :statements
|
30
|
+
|
31
|
+
rule :statement, "while", :statement, "do", :statements, "end" do
|
32
|
+
def evaluate
|
33
|
+
while statement.evaluate
|
34
|
+
statements.evaluate
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
40
|
+
def evaluate
|
41
|
+
case operator
|
42
|
+
when :<, :<=, :>, :>=, :==
|
43
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
44
|
+
else
|
45
|
+
left.evaluate.send operator, right.evaluate
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
rule :operand, "(", :statement, ")"
|
51
|
+
|
52
|
+
rule :operand, "[", :statement, "]", "=", :statement do
|
53
|
+
def evaluate
|
54
|
+
parser.store[statement[0].evaluate] = statement[1].evaluate
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
rule :operand, "[", :statement, "]" do
|
59
|
+
def evaluate
|
60
|
+
parser.store[statement.evaluate]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
rule :operand, /[-]?[0-9]+/ do
|
65
|
+
def evaluate
|
66
|
+
to_s.to_i
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
+
|
3
|
+
class TuringParser < BabelBridge::Parser
|
4
|
+
ignore_whitespace
|
5
|
+
|
6
|
+
def store
|
7
|
+
@store||=[]
|
8
|
+
end
|
9
|
+
|
10
|
+
rule :statements, many(:statement,";"), match?(";") do
|
11
|
+
def evaluate
|
12
|
+
ret = nil
|
13
|
+
statement.each do |s|
|
14
|
+
puts "class is: "+s.class
|
15
|
+
ret = s.evaluate
|
16
|
+
end
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
|
22
|
+
def evaluate
|
23
|
+
case operator
|
24
|
+
when :<, :<=, :>, :>=, :==
|
25
|
+
(left.evaluate.send operator, right.evaluate) ? 1 : nil
|
26
|
+
else
|
27
|
+
left.evaluate.send operator, right.evaluate
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
|
33
|
+
def evaluate
|
34
|
+
if statement[0].evaluate
|
35
|
+
statement[1].evaluate
|
36
|
+
else
|
37
|
+
else_clause.evaluate if else_clause
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rule :else_clause, "else", :statement
|
42
|
+
|
43
|
+
rule :statement, "while", :statement, "do", :statements, "end" do
|
44
|
+
def evaluate
|
45
|
+
while statement.evaluate
|
46
|
+
statements.evaluate
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
rule :operand, "[", :statement, "]", "=", :statement do
|
52
|
+
def evaluate
|
53
|
+
parser.store[statement[0].evaluate] = statement[1].evaluate
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
rule :operand, "[", :statement, "]" do
|
58
|
+
def evaluate
|
59
|
+
parser.store[statement.evaluate]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
rule :operand, "(", :statement, ")"
|
64
|
+
rule :operand, /-?[0-9]+/ do
|
65
|
+
def evaluate
|
66
|
+
to_s.to_i
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
BabelBridge::Shell.new(TuringParser.new).start
|
data/lib/nodes/many_node.rb
CHANGED
@@ -36,7 +36,9 @@ module BabelBridge
|
|
36
36
|
ret=list.collect {|a|a.inspect(options)}.compact
|
37
37
|
ret= if ret.length==0 then simple ? nil : "[]"
|
38
38
|
elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
|
39
|
-
else
|
39
|
+
else
|
40
|
+
ret = ret.collect {|a| " "+a.gsub("\n","\n ")}
|
41
|
+
(simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
|
40
42
|
end
|
41
43
|
ret
|
42
44
|
end
|
data/lib/nodes/node.rb
CHANGED
@@ -41,6 +41,13 @@ class Node
|
|
41
41
|
self
|
42
42
|
end
|
43
43
|
|
44
|
+
# Returns a human-readable representation of the parse tree
|
45
|
+
# options
|
46
|
+
# :simple => output a simplified representation of the parse tree
|
47
|
+
def inspect(options={})
|
48
|
+
"(TODO: def #{self.class}#inspect(options={}))"
|
49
|
+
end
|
50
|
+
|
44
51
|
#********************
|
45
52
|
# info methods
|
46
53
|
#********************
|
data/lib/parser.rb
CHANGED
@@ -160,6 +160,7 @@ class Parser
|
|
160
160
|
attr_accessor :expecting_list
|
161
161
|
attr_accessor :src
|
162
162
|
attr_accessor :parse_cache
|
163
|
+
attr_accessor :failed_parse # gets set if the entire input was not matched
|
163
164
|
|
164
165
|
def initialize
|
165
166
|
reset_parser_tracking
|
@@ -209,6 +210,7 @@ class Parser
|
|
209
210
|
if ret.next<src.length # parse only succeeds if the whole input is matched
|
210
211
|
@parsing_did_not_match_entire_input=true
|
211
212
|
@failure_index=ret.next
|
213
|
+
@failed_parse = ret
|
212
214
|
ret=nil
|
213
215
|
else
|
214
216
|
reset_parser_tracking
|
@@ -235,51 +237,68 @@ class Parser
|
|
235
237
|
node_list && node_list[common_root.length..-1].map{|p|"#{p.class}(#{p.offset})"}.join(" > ")
|
236
238
|
end
|
237
239
|
|
238
|
-
def
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
Source:
|
246
|
-
...
|
247
|
-
#{(failure_index==0 ? "" : src[0..(failure_index-1)]).last_lines(bracketing_lines)}<HERE>#{src[(failure_index)..-1].first_lines(bracketing_lines)}
|
248
|
-
...
|
249
|
-
ENDTXT
|
240
|
+
def nodes_interesting_parse_path(node)
|
241
|
+
path = node.parent_list
|
242
|
+
path << node
|
243
|
+
path.pop while path[-1] && !path[-1].kind_of?(NonTerminalNode)
|
244
|
+
path
|
245
|
+
end
|
250
246
|
|
251
|
-
if @parsing_did_not_match_entire_input
|
252
|
-
ret+="\nParser did not match entire input."
|
253
|
-
else
|
254
247
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
common_root=node.parent_list
|
248
|
+
def expecting_output
|
249
|
+
return "" if expecting_list.length==0
|
250
|
+
common_root=nil
|
251
|
+
expecting_list.values.each do |e|
|
252
|
+
node=e[:node]
|
253
|
+
pl=nodes_interesting_parse_path(node)
|
254
|
+
pl.pop # ignore the node itself
|
255
|
+
if common_root
|
256
|
+
common_root.each_index do |i|
|
257
|
+
if pl[i]!=common_root[i]
|
258
|
+
common_root=common_root[0..i-1]
|
259
|
+
break
|
268
260
|
end
|
269
261
|
end
|
270
|
-
|
262
|
+
else
|
263
|
+
common_root=pl
|
264
|
+
end
|
265
|
+
end
|
266
|
+
<<ENDTXT
|
271
267
|
|
272
|
-
|
268
|
+
Parse path at failure:
|
273
269
|
#{node_list_string(common_root)}
|
274
270
|
|
275
271
|
Expecting#{expecting_list.length>1 ? ' one of' : ''}:
|
276
272
|
#{expecting_list.values.collect do |a|
|
277
|
-
list=node_list_string(a[:node]
|
273
|
+
list=node_list_string(nodes_interesting_parse_path(a[:node]),common_root)
|
278
274
|
[list,"#{a[:pattern].inspect} (#{list})"]
|
279
275
|
end.sort.map{|i|i[1]}.join("\n ")}
|
280
276
|
ENDTXT
|
277
|
+
end
|
278
|
+
|
279
|
+
#option: :verbose => true
|
280
|
+
def parser_failure_info(options={})
|
281
|
+
return unless src
|
282
|
+
verbose = options[:verbose]
|
283
|
+
bracketing_lines=5
|
284
|
+
line,col=src.line_col(failure_index)
|
285
|
+
ret=<<-ENDTXT
|
286
|
+
Parsing error at line #{line} column #{col} offset #{failure_index}
|
287
|
+
|
288
|
+
Source:
|
289
|
+
...
|
290
|
+
#{(failure_index==0 ? "" : src[0..(failure_index-1)]).last_lines(bracketing_lines)}<HERE>#{src[(failure_index)..-1].first_lines(bracketing_lines)}
|
291
|
+
...
|
292
|
+
ENDTXT
|
293
|
+
|
294
|
+
if @parsing_did_not_match_entire_input
|
295
|
+
ret+="\nParser did not match entire input.\n"
|
296
|
+
if verbose
|
297
|
+
ret+="\nParsed:\n#{Tools::indent failed_parse.inspect}\n"
|
298
|
+
end
|
281
299
|
end
|
282
|
-
|
300
|
+
|
301
|
+
ret+expecting_output
|
283
302
|
end
|
284
303
|
end
|
285
304
|
end
|
data/lib/pattern_element.rb
CHANGED
@@ -32,6 +32,10 @@ class PatternElement
|
|
32
32
|
raise "pattern element cannot be both :dont and :optional" if negative && optional
|
33
33
|
end
|
34
34
|
|
35
|
+
def inspect
|
36
|
+
"<PatternElement rule_variant=#{rule_variant.variant_node_class} match=#{match.inspect}>"
|
37
|
+
end
|
38
|
+
|
35
39
|
def to_s
|
36
40
|
match.inspect
|
37
41
|
end
|
@@ -50,6 +54,11 @@ class PatternElement
|
|
50
54
|
# Could-match patterns (PEG: &element)
|
51
55
|
match.match_length=0 if match && could_match
|
52
56
|
|
57
|
+
if !match && terminal
|
58
|
+
# log failures on Terminal patterns for debug output if overall parse fails
|
59
|
+
parent_node.parser.log_parsing_failure(parent_node.next,:pattern=>self.match,:node=>parent_node)
|
60
|
+
end
|
61
|
+
|
53
62
|
# return match
|
54
63
|
match
|
55
64
|
end
|
@@ -139,7 +148,7 @@ class PatternElement
|
|
139
148
|
|
140
149
|
# generate delimiter_pattern_element
|
141
150
|
delimiter_pattern_element= hash[:delimiter] && PatternElement.new(hash[:delimiter],rule_variant)
|
142
|
-
|
151
|
+
|
143
152
|
# generate post_delimiter_element
|
144
153
|
post_delimiter_element=hash[:post_delimiter] && case hash[:post_delimiter]
|
145
154
|
when TrueClass then delimiter_pattern_element
|
data/lib/rule_variant.rb
CHANGED
@@ -25,13 +25,7 @@ class RuleVariant
|
|
25
25
|
match=pe.parse(node)
|
26
26
|
|
27
27
|
# if parse failed
|
28
|
-
if !match
|
29
|
-
if pe.terminal
|
30
|
-
# log failures on Terminal patterns for debug output if overall parse fails
|
31
|
-
node.parser.log_parsing_failure(node.next,:pattern=>pe.match,:node=>node)
|
32
|
-
end
|
33
|
-
return nil
|
34
|
-
end
|
28
|
+
return if !match
|
35
29
|
|
36
30
|
# parse succeeded, add to node and continue
|
37
31
|
node.add_match(match,pe.name)
|
data/lib/shell.rb
CHANGED
@@ -8,9 +8,14 @@ class Shell
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def evaluate(parse_tree_node)
|
11
|
-
parse_tree_node.evaluate
|
11
|
+
if parse_tree_node.respond_to? :evaluate
|
12
|
+
" => "+parse_tree_node.evaluate.inspect
|
13
|
+
else
|
14
|
+
"\nParse tree:\n "+
|
15
|
+
parse_tree_node.inspect.gsub("\n","\n ")+"\n\n"
|
16
|
+
end
|
12
17
|
rescue Exception => e
|
13
|
-
@stderr.puts "Error evaluating parse tree: #{e}\n "+e.backtrace.join("\n ")
|
18
|
+
@stderr.puts "Error evaluating parse tree: #{e}\n "+e.backtrace.join("\n ")+"\nParse Tree:\n"+parse_tree_node.inspect
|
14
19
|
end
|
15
20
|
|
16
21
|
# if block is provided, successful parsers are yield to block
|
@@ -19,16 +24,18 @@ class Shell
|
|
19
24
|
@stdout = options[:stdout] || $stdout
|
20
25
|
@stderr = options[:stdout] || @stdout
|
21
26
|
@stdin = options[:stdin] || $stdin
|
22
|
-
while line = @stdin == $stdin ? Readline.readline("> ", true) : @stdin.gets
|
23
|
-
|
27
|
+
while line = @stdin == $stdin ? Readline.readline("> ", true) : @stdin.gets
|
28
|
+
line.strip!
|
29
|
+
next if line.length==0
|
30
|
+
ret = parser.parse line
|
24
31
|
if ret
|
25
32
|
if block
|
26
33
|
yield ret
|
27
34
|
else
|
28
|
-
@stdout.puts
|
35
|
+
@stdout.puts evaluate(ret)
|
29
36
|
end
|
30
37
|
else
|
31
|
-
@stderr.puts parser.parser_failure_info
|
38
|
+
@stderr.puts parser.parser_failure_info :verbose => true
|
32
39
|
end
|
33
40
|
end
|
34
41
|
end
|
data/lib/string.rb
CHANGED
@@ -14,8 +14,11 @@ module BabelBridge
|
|
14
14
|
lines.length<=n ? self : lines[-n..-1].join("\n")
|
15
15
|
end
|
16
16
|
|
17
|
+
# return the line and column of a given offset into this string
|
18
|
+
# line and column are 1-based
|
17
19
|
def line_col(offset)
|
18
|
-
|
20
|
+
return 1,1 if length==0 || offset==0
|
21
|
+
lines=(self[0..offset-1]+" ").split("\n")
|
19
22
|
return lines.length, lines[-1].length
|
20
23
|
end
|
21
24
|
end
|
data/lib/tools.rb
CHANGED
@@ -2,15 +2,33 @@ module BabelBridge
|
|
2
2
|
class Tools
|
3
3
|
class << self
|
4
4
|
|
5
|
+
def indent(string,indent=" ")
|
6
|
+
indent + string.gsub("\n", "\n#{indent}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def symbols_to_strings(array)
|
10
|
+
array.collect {|op| op.kind_of?(Symbol) ? op.to_s : op}
|
11
|
+
end
|
12
|
+
|
13
|
+
def regexp_and_strings_to_regexpstrings(array)
|
14
|
+
array.collect {|op| op.kind_of?(Regexp) ? op.source : Regexp.escape(op)}
|
15
|
+
end
|
16
|
+
|
17
|
+
# sort strings first, regexp second
|
18
|
+
# sort strings by lenght, longest first
|
19
|
+
# will then match first to last
|
20
|
+
def sort_operator_patterns(array)
|
21
|
+
array.sort_by {|a| a.kind_of?(Regexp) ? 0 : -a.length}
|
22
|
+
end
|
23
|
+
|
5
24
|
# Takes an array of Strings and Regexp and generates a new Regexp
|
6
25
|
# that matches the or ("|") of all strings and Regexp
|
7
26
|
def array_to_or_regexp_string(array)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end.sort{|a|a.length}.join('|')
|
27
|
+
array = symbols_to_strings array.flatten
|
28
|
+
array = sort_operator_patterns array
|
29
|
+
array = regexp_and_strings_to_regexpstrings array
|
30
|
+
|
31
|
+
array.collect {|op| "(#{op})"}.join('|') #.tap {|a| puts "array_to_or_regexp_string(#{array.inspect}) -> /#{a}/"}
|
14
32
|
end
|
15
33
|
|
16
34
|
def array_to_anchored_or_regexp(array)
|
@@ -33,9 +51,10 @@ class BinaryOperatorProcessor
|
|
33
51
|
|
34
52
|
operator_precedence.each_with_index do |op_level,i|
|
35
53
|
(op_level.kind_of?(Array) ? op_level : [op_level]).each do |op|
|
54
|
+
precedence = operator_precedence.length - i
|
36
55
|
case op
|
37
|
-
when String, Symbol then @exact_operator_precedence[op.to_s] =
|
38
|
-
when Regexp then @regexp_operator_precedence << [op,
|
56
|
+
when String, Symbol then @exact_operator_precedence[op.to_s] = precedence
|
57
|
+
when Regexp then @regexp_operator_precedence << [op,precedence]
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
data/lib/version.rb
CHANGED
data/test/test_bb.rb
CHANGED
@@ -394,7 +394,7 @@ class BBTests < TestHelper
|
|
394
394
|
|
395
395
|
def test_binary_operator_rule
|
396
396
|
parser=new_parser do
|
397
|
-
binary_operators_rule :bin_op, :int, [
|
397
|
+
binary_operators_rule :bin_op, :int, ["**", [:/, :*], [:+, "-"]], :right_operators => ["**"] do
|
398
398
|
def evaluate
|
399
399
|
"(#{left.evaluate}#{operator}#{right.evaluate})"
|
400
400
|
end
|
@@ -408,11 +408,26 @@ class BBTests < TestHelper
|
|
408
408
|
assert_equal "((1+2)+3)", parser.parse("1+2+3").evaluate
|
409
409
|
assert_equal "(1+(2*3))", parser.parse("1+2*3").evaluate
|
410
410
|
assert_equal "((1*2)+3)", parser.parse("1*2+3").evaluate
|
411
|
-
assert_equal "(5**6)",
|
411
|
+
assert_equal "(5**6)", parser.parse("5**6").evaluate
|
412
412
|
assert_equal "((1-2)+((3*4)/(5**6)))", parser.parse("1-2+3*4/5**6").evaluate
|
413
413
|
assert_equal "(5**(6**7))", parser.parse("5**6**7").evaluate
|
414
414
|
end
|
415
415
|
|
416
|
+
def test_line_col
|
417
|
+
assert_equal [1,1], "".line_col(0)
|
418
|
+
assert_equal [1,1], " ".line_col(0)
|
419
|
+
assert_equal [1,1], "a\nbb\nccc".line_col(0)
|
420
|
+
assert_equal [1,2], "a\nbb\nccc".line_col(1)
|
421
|
+
assert_equal [2,1], "a\nbb\nccc".line_col(2)
|
422
|
+
assert_equal [2,2], "a\nbb\nccc".line_col(3)
|
423
|
+
assert_equal [2,3], "a\nbb\nccc".line_col(4)
|
424
|
+
assert_equal [3,1], "a\nbb\nccc".line_col(5)
|
425
|
+
assert_equal [3,2], "a\nbb\nccc".line_col(6)
|
426
|
+
assert_equal [3,3], "a\nbb\nccc".line_col(7)
|
427
|
+
assert_equal [3,4], "a\nbb\nccc".line_col(8)
|
428
|
+
assert_equal [3,4], "a\nbb\nccc".line_col(9)
|
429
|
+
end
|
430
|
+
|
416
431
|
def disabled_test_recursive_block
|
417
432
|
# PEG does have this problem, so this isn't really an error
|
418
433
|
# But maybe in the future we'll handle it better.
|
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.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -40,8 +40,24 @@ files:
|
|
40
40
|
- lib/version.rb
|
41
41
|
- examples/indention_grouping.rb
|
42
42
|
- examples/indention_grouping_test.txt
|
43
|
-
- examples/
|
43
|
+
- examples/test.rb
|
44
|
+
- examples/turing/examples.turing
|
45
|
+
- examples/turing/notes.rb
|
44
46
|
- examples/turing/turing.rb
|
47
|
+
- examples/turing/turing00.rb
|
48
|
+
- examples/turing/turing01.rb
|
49
|
+
- examples/turing/turing02.rb
|
50
|
+
- examples/turing/turing03.rb
|
51
|
+
- examples/turing/turing04.rb
|
52
|
+
- examples/turing/turing05.rb
|
53
|
+
- examples/turing/turing06.rb
|
54
|
+
- examples/turing/turing07.rb
|
55
|
+
- examples/turing/turing08.rb
|
56
|
+
- examples/turing/turing09.rb
|
57
|
+
- examples/turing/turing10.rb
|
58
|
+
- examples/turing/turing11.rb
|
59
|
+
- examples/turing/turing12.rb
|
60
|
+
- examples/turing/turing_demo.rb
|
45
61
|
homepage: http://babel-bridge.rubyforge.org
|
46
62
|
licenses: []
|
47
63
|
post_install_message:
|
data/examples/turing/test.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
|
2
|
-
|
3
|
-
class TestParser < BabelBridge::Parser
|
4
|
-
|
5
|
-
rule :expr, :bin_op do
|
6
|
-
def evaluate
|
7
|
-
output = bin_op.evaluate
|
8
|
-
input = to_s
|
9
|
-
puts "bbtest: #{output} = #{eval output}"
|
10
|
-
puts "ruby: #{input} = #{eval input}"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
#rule :bin_op, many(:int,/[-+\/*]/) do
|
15
|
-
binary_operators_rule :bin_op, :operand, [[:+, "-"], [:/, :*], "**"], :right_operators => ["**"] do
|
16
|
-
def evaluate
|
17
|
-
"(#{left.evaluate}#{operator}#{right.evaluate})"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
rule :operand, "(", :bin_op, ")"
|
22
|
-
|
23
|
-
rule :operand, /[-]?[0-9]+/ do
|
24
|
-
def evaluate; to_s; end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
BabelBridge::Shell.new(TestParser.new).start
|