babel_bridge 0.3.0 → 0.3.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/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