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 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
+
@@ -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, /if\b/, :statement, /then\b/, :statement, :else_clause?, /end\b/ do
27
+ rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
21
28
  def evaluate
22
- if statement[0].evaluate
23
- statement[1].evaluate
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, /else\b/, :statement
36
+ rule :else_clause, "else", :statements
30
37
 
31
- rule :statement, /while\b/, :statement, /do\b/, :statements, /end\b/ do
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, [[:<, :<=, :>, :>=, :==], [:+, :-], [:/, :*]] do
46
+ binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
40
47
  def evaluate
41
48
  case operator
42
49
  when :<, :<=, :>, :>=, :==
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
2
+
3
+ class TuringParser < BabelBridge::Parser
4
+
5
+ end
6
+
7
+ BabelBridge::Shell.new(TuringParser.new).start
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
2
+
3
+ class TuringParser < BabelBridge::Parser
4
+
5
+ rule :int, /[-]?[0-9]+/
6
+ end
7
+
8
+ BabelBridge::Shell.new(TuringParser.new).start
@@ -0,0 +1,10 @@
1
+ require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
2
+
3
+ class TuringParser < BabelBridge::Parser
4
+
5
+ rule :add, :int, "+", :add
6
+ rule :add, :int
7
+ rule :int, /[-]?[0-9]+/
8
+ end
9
+
10
+ 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 + 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
@@ -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 (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
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 parser_failure_info
239
- return unless src
240
- bracketing_lines=5
241
- line,col=src.line_col(failure_index)
242
- ret=<<-ENDTXT
243
- Parsing error at line #{line} column #{col} offset #{failure_index}
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
- common_root=nil
256
- expecting_list.values.each do |e|
257
- node=e[:node]
258
- pl=node.parent_list
259
- if common_root
260
- common_root.each_index do |i|
261
- if pl[i]!=common_root[i]
262
- common_root=common_root[0..i-1]
263
- break
264
- end
265
- end
266
- else
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
- ret+=<<ENDTXT
262
+ else
263
+ common_root=pl
264
+ end
265
+ end
266
+ <<ENDTXT
271
267
 
272
- Successfully matched rules up to failure:
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].parent_list,common_root)
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
- ret
300
+
301
+ ret+expecting_output
283
302
  end
284
303
  end
285
304
  end
@@ -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
- ret = parser.parse line.strip
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 " => #{evaluate(ret).inspect}"
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
- lines=self[0..offset-1].split("\n")
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
- new_re=array.flatten.collect do |op|
9
- "("+case op
10
- when Regexp then op.source
11
- when String, Symbol then Regexp.escape(op.to_s)
12
- end+")"
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] = i
38
- when Regexp then @regexp_operator_precedence << [op,i]
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
@@ -1,3 +1,3 @@
1
1
  module BabelBridge
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
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, [[:+, "-"], [:/, :*], "**"], :right_operators => ["**"] do
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)", parser.parse("5**6").evaluate
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.0
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/turing/test.rb
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:
@@ -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