babel_bridge 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/babel_bridge.gemspec CHANGED
@@ -1,20 +1,23 @@
1
1
  require File.join(File.dirname(__FILE__),"lib/babel_bridge.rb")
2
2
 
3
- $gemspec = Gem::Specification.new do |s|
4
- s.name = "babel_bridge"
5
- s.version = BabelBridge::VERSION
6
- s.author = "Shane Brinkman-Davis"
7
- s.date = "2010-11-28"
8
- s.email = "shanebdavis@gmail.com"
9
- s.homepage = "http://babel-bridge.rubyforge.org"
10
- s.platform = Gem::Platform::RUBY
11
- s.rubyforge_project = "babel-bridge"
12
- s.summary = "A Ruby-based parser-generator based on Parsing Expression Grammars."
13
- s.description = <<DESCRIPTION
14
- Babel Bridge is an object oriented parser generator for parsing expression grammars (PEG).
3
+ $gemspec = Gem::Specification.new do |gem|
4
+ gem.name = "babel_bridge"
5
+ gem.version = BabelBridge::VERSION
6
+ gem.author = "Shane Brinkman-Davis"
7
+ gem.date = "2010-11-28"
8
+ gem.email = "shanebdavis@gmail.com"
9
+ gem.homepage = "http://babel-bridge.rubyforge.org"
10
+ gem.platform = Gem::Platform::RUBY
11
+ gem.rubyforge_project = "babel-bridge"
12
+ gem.summary = "A Ruby-based parser-generator based on Parsing Expression Grammars."
13
+ gem.description = <<DESCRIPTION
14
+ Babel Bridge is an object oriented parser generator for parsing expression grammars (PEG).
15
15
  Generate memoizing packrat parsers 100% in Ruby code with a simple embedded DSL.
16
16
  DESCRIPTION
17
-
18
- s.files = ["LICENSE", "README", "Rakefile", "babel_bridge.gemspec", "{test,lib,doc,examples}/**/*"].map{|p| Dir[p]}.flatten
19
- s.has_rdoc = false
17
+
18
+ gem.files = ["LICENSE", "README", "Rakefile", "babel_bridge.gemspec", "{test,spec,lib,doc,examples}/**/*"].map{|p| Dir[p]}.flatten
19
+ gem.has_rdoc = false
20
+
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'rspec'
20
23
  end
@@ -0,0 +1,154 @@
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
+ # DONE: turing.rb PLUS
7
+ # => functions
8
+ # => local variables
9
+ # => stack
10
+ # TODO: add variables and functions
11
+ # TODO: add closures
12
+ # TODO: add classes
13
+
14
+ class TuringParser < BabelBridge::Parser
15
+ ignore_whitespace
16
+ # TODO: add "whole_words" option to convert all literal matching patterns that are words into /word\b/
17
+
18
+ def store
19
+ @store||=[]
20
+ end
21
+
22
+ # the stack consists of an array of hashs
23
+ # Each entry in the stack is a call-frame with a hash of local variable names
24
+ def stack; @stack ||= [{}]; end
25
+
26
+ def current_stack_frame; stack[-1]; end
27
+ def globals; stack[0]; end
28
+
29
+ rule :statements, many(:statement,";"), match?(";") do
30
+ def evaluate
31
+ ret = nil
32
+ statement.each do |s|
33
+ ret = s.evaluate
34
+ end
35
+ ret
36
+ end
37
+ end
38
+
39
+ rule :statement, :function_definition
40
+
41
+ rule :function_definition, "def", :identifier, :parameter_list?, "do", :statements, "end" do
42
+ def evaluate
43
+ stack_frame = parser.current_stack_frame
44
+ stack_frame[identifier.to_sym] = self
45
+ 1 # return true
46
+ end
47
+
48
+ def parameter_names; @parameter_names||=parameter_list ? parameter_list.parameter_names : []; end
49
+
50
+ def evaluate_function(params)
51
+ params ||= []
52
+ locals = {}
53
+ raise "wrong number of parameters. #{identifier} expects #{parameter_names.length} but got #{params.length}" unless params.length == parameter_names.length
54
+ parameter_names.each_with_index do |name,index|
55
+ locals[name] = params[index]
56
+ end
57
+ parser.stack << locals
58
+ statements.evaluate.tap {parser.stack.pop}
59
+ end
60
+ end
61
+
62
+ rule :parameter_list, "(", many(:identifier, ","), ")" do
63
+ def parameter_names
64
+ @parameter_names ||= identifier.collect{|a|a.to_sym}
65
+ end
66
+ end
67
+
68
+ rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
69
+ def evaluate
70
+ if statement.evaluate
71
+ statements.evaluate
72
+ elsif else_clause
73
+ else_clause.evaluate
74
+ end
75
+ end
76
+ end
77
+ rule :else_clause, "else", :statements
78
+
79
+ rule :statement, "while", :statement, "do", :statements, "end" do
80
+ def evaluate
81
+ ret = nil
82
+ while statement.evaluate
83
+ ret = statements.evaluate
84
+ end
85
+ ret
86
+ end
87
+ end
88
+
89
+ binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
90
+ def evaluate
91
+ ret = left.evaluate.send operator, right.evaluate
92
+ case operator
93
+ when :<, :<=, :>, :>=, :== then ret ? 1 : nil
94
+ else ret
95
+ end
96
+ end
97
+ end
98
+
99
+ rule :operand, "(", :statement, ")"
100
+
101
+ rule :operand, "[", :statement, "]", "=", :statement do
102
+ def evaluate
103
+ parser.store[statement[0].evaluate] = statement[1].evaluate
104
+ end
105
+ end
106
+
107
+ rule :operand, "[", :statement, "]" do
108
+ def evaluate
109
+ parser.store[statement.evaluate]
110
+ end
111
+ end
112
+
113
+ rule :operand, "nil" do
114
+ def evaluate
115
+ nil
116
+ end
117
+ end
118
+
119
+ rule :operand, :identifier, "=", :statement do
120
+ def evaluate
121
+ stack_frame = parser.current_stack_frame
122
+ stack_frame[identifier.to_sym] = statement.evaluate
123
+ end
124
+ end
125
+
126
+ rule :operand, :identifier, :parameters? do
127
+ def evaluate
128
+ globals = parser.globals
129
+ stack_frame = parser.current_stack_frame
130
+ name = identifier.to_sym
131
+ raise "undefined variable: #{name.inspect}" unless globals.has_key?(name) || stack_frame.has_key?(name)
132
+ case value = stack_frame.has_key?(name) ? stack_frame[name] : globals[name]
133
+ when BabelBridge::Node then value.evaluate_function(parameters.evaluate)
134
+ else value
135
+ end
136
+ end
137
+ end
138
+
139
+ rule :parameters, "(", many(:statement,","), ")" do
140
+ def evaluate
141
+ statement.collect {|s|s.evaluate}
142
+ end
143
+ end
144
+
145
+ rule :identifier, /[_a-zA-Z][_a-zA-Z0-9]*/
146
+
147
+ rule :operand, /[-]?[0-9]+/ do
148
+ def evaluate
149
+ to_s.to_i
150
+ end
151
+ end
152
+ end
153
+
154
+ BabelBridge::Shell.new(TuringParser.new).start
@@ -1,44 +1,32 @@
1
- require File.join(File.dirname(__FILE__),"..","..","lib","babel_bridge")
1
+ require "babel_bridge"
2
2
 
3
3
  class TuringParser < BabelBridge::Parser
4
4
  ignore_whitespace
5
5
 
6
6
  def store
7
7
  @store||=[]
8
- end
8
+ end
9
9
 
10
10
  rule :statements, many(:statement,";"), match?(";") do
11
11
  def evaluate
12
12
  ret = nil
13
13
  statement.each do |s|
14
- puts "class is: "+s.class
15
14
  ret = s.evaluate
16
15
  end
17
16
  ret
18
17
  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
18
  end
31
19
 
32
20
  rule :statement, "if", :statement, "then", :statements, :else_clause?, "end" do
33
21
  def evaluate
34
- if statement[0].evaluate
35
- statement[1].evaluate
22
+ if matches[1].evaluate
23
+ matches[3].evaluate
36
24
  else
37
25
  else_clause.evaluate if else_clause
38
26
  end
39
27
  end
40
28
  end
41
- rule :else_clause, "else", :statement
29
+ rule :else_clause, "else", :statements
42
30
 
43
31
  rule :statement, "while", :statement, "do", :statements, "end" do
44
32
  def evaluate
@@ -46,7 +34,19 @@ class TuringParser < BabelBridge::Parser
46
34
  statements.evaluate
47
35
  end
48
36
  end
49
- end
37
+ end
38
+
39
+ binary_operators_rule :statement, :operand, [[:/, :*], [:+, :-], [:<, :<=, :>, :>=, :==]] do
40
+ def evaluate
41
+ res = left.evaluate.send operator, right.evaluate
42
+ case operator
43
+ when :<, :<=, :>, :>=, :==
44
+ res ? 1 : nil
45
+ else
46
+ res
47
+ end
48
+ end
49
+ end
50
50
 
51
51
  rule :operand, "[", :statement, "]", "=", :statement do
52
52
  def evaluate
@@ -61,7 +61,7 @@ class TuringParser < BabelBridge::Parser
61
61
  end
62
62
 
63
63
  rule :operand, "(", :statement, ")"
64
- rule :operand, /-?[0-9]+/ do
64
+ rule :operand, /[-]?[0-9]+/ do
65
65
  def evaluate
66
66
  to_s.to_i
67
67
  end
data/lib/nodes.rb CHANGED
@@ -3,7 +3,8 @@
3
3
  empty_node
4
4
  terminal_node
5
5
  non_terminal_node
6
+ rule_node
6
7
  many_node
7
8
  }.each do |file|
8
9
  require File.join(File.dirname(__FILE__),"nodes",file)
9
- end
10
+ end
@@ -13,5 +13,15 @@ class EmptyNode < Node
13
13
  def inspect(options={})
14
14
  "EmptyNode" unless options[:simple]
15
15
  end
16
+
17
+ # EmptyNodes should always match at the beginning of the whitespace range
18
+ def node_init(parent_or_parser)
19
+ super
20
+ self.offset = preceding_whitespace_range.first
21
+ self.preceding_whitespace_range = match_range
22
+ end
23
+
24
+ def matches; [self]; end
25
+
26
+ end
16
27
  end
17
- end
@@ -5,60 +5,49 @@ http://babel-bridge.rubyforge.org/
5
5
  =end
6
6
 
7
7
  module BabelBridge
8
- # generated by a :poly PatternElement
9
- # Not subclassed
10
- class ManyNode < Node
11
- attr_accessor :matches,:delimiter_matches
12
- def initialize(parent)
13
- node_init(parent)
14
- self.matches=[]
15
- self.delimiter_matches=[]
16
- end
17
-
18
- def match_length; self.next-offset end
8
+ # generated by a :poly PatternElement
9
+ # Not subclassed
10
+ class ManyNode < NonTerminalNode
19
11
 
20
- def next
21
- if m=matches[-1]
22
- m_next=m.next
23
- if d=delimiter_matches[-1]
24
- d_next=d.next
25
- m_next > d_next ? m_next : d_next
26
- else
27
- m_next
28
- end
29
- else
30
- parent.next
31
- end
32
- end
12
+ def delimiter_matches
13
+ @delimiter_matches||=[]
14
+ end
33
15
 
34
- def inspect_helper(list,options)
35
- simple=options[:simple]
36
- ret=list.collect {|a|a.inspect(options)}.compact
37
- ret= if ret.length==0 then simple ? nil : "[]"
38
- elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
39
- else
40
- ret = ret.collect {|a| " "+a.gsub("\n","\n ")}
41
- (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
42
- end
43
- ret
44
- end
16
+ def separate_delimiter_matches
17
+ count = 0
18
+ @matches, @delimiter_matches = @matches.partition {|el| count+=1;(count%2==1)}
19
+ @delimiter_matches = @delimiter_matches[0..@matches.length-2] #delimiter_matches should be exactly 1 shorter
20
+ update_match_length
21
+ end
45
22
 
46
- def inspect(options={})
47
- if options[:simple]
48
- c=[]
49
- matches.each_with_index {|n,i| c<<n;c<<delimiter_matches[i]}
50
- c=c.compact
51
- inspect_helper(c,options)
52
- else
53
- ret=inspect_helper(matches,options)
54
- ret+=" delimiters="+inspect_helper(delimiter_matches,options) if delimiter_matches.length>0
55
- ret
56
- end
23
+ def inspect_helper(list,options)
24
+ simple=options[:simple]
25
+ ret=list.collect {|a|a.inspect(options)}.compact
26
+ ret= if ret.length==0 then simple ? nil : "[]"
27
+ elsif ret.length==1 && !ret[0]["\n"] then (simple ? ret[0] : "[#{ret[0]}]")
28
+ else
29
+ ret = ret.collect {|a| " "+a.gsub("\n","\n ")}
30
+ (simple ? ret : ["[",ret,"]"]).flatten.join("\n") #.gsub("\n","\n ")
57
31
  end
32
+ ret
33
+ end
58
34
 
59
- def method_missing(method_name, *args) #method_name is a symbol
60
- self.map {|match| match.send(method_name,*args)}
35
+ def inspect(options={})
36
+ if options[:simple]
37
+ c=[]
38
+ matches.each_with_index {|n,i| c<<n;c<<delimiter_matches[i]}
39
+ c=c.compact
40
+ inspect_helper(c,options)
41
+ else
42
+ ret=inspect_helper(matches,options)
43
+ ret+=" delimiters="+inspect_helper(delimiter_matches,options) if delimiter_matches.length>0
44
+ ret
61
45
  end
46
+ end
62
47
 
48
+ def method_missing(method_name, *args) #method_name is a symbol
49
+ self.map {|match| match.send(method_name,*args)}
63
50
  end
51
+
52
+ end
64
53
  end
data/lib/nodes/node.rb CHANGED
@@ -7,12 +7,33 @@ end
7
7
 
8
8
  # base class for all parse-tree nodes
9
9
  class Node
10
- attr_accessor :src,:offset,:match_length,:parent,:parser
10
+ attr_accessor :src,:offset,:match_length,:parent,:parser,:preceding_whitespace_range
11
+
12
+ def whitespace_regexp
13
+ parser.whitespace_regexp
14
+ end
15
+
16
+ # the index of the first character after the match
17
+ def offset_after_match
18
+ offset + match_length
19
+ end
20
+
21
+ def match_range
22
+ offset..(offset+match_length-1)
23
+ end
24
+
25
+ def trailing_whitespace_range
26
+ parser.white_space_range offset_after_match
27
+ end
11
28
 
12
29
  def to_s
13
30
  text
14
31
  end
15
32
 
33
+ def to_sym
34
+ to_s.to_sym
35
+ end
36
+
16
37
  def node_init(parent_or_parser)
17
38
  self.match_length=0
18
39
  case parent_or_parser
@@ -24,6 +45,7 @@ class Node
24
45
  self.parent=parent_or_parser
25
46
  self.parser=parent.parser
26
47
  self.offset=parent.next
48
+ self.preceding_whitespace_range=parent.trailing_whitespace_range
27
49
  self.src=parent.src
28
50
  raise "parent node does not have parser set" unless parser
29
51
  else
@@ -51,8 +73,8 @@ class Node
51
73
  #********************
52
74
  # info methods
53
75
  #********************
54
- def next; offset+match_length end # index of first character after match
55
- def text; src[offset,match_length] end # the substring in src matched
76
+ def next; trailing_whitespace_range.last+1 end # index of first character after match and any trailing whitespace
77
+ def text; src[match_range] end # the substring in src matched
56
78
 
57
79
  # length returns the number of sub-nodes
58
80
  def length
@@ -66,36 +88,8 @@ class Node
66
88
  def node_path
67
89
  "#{parent && (parent.node_path+' > ')}#{self.class}(#{offset})"
68
90
  end
69
-
70
- #*****************************
71
- # Array interface implementation
72
- #*****************************
73
- def matches # override this with function that returns array of matches to be used for Array indexing and iteration
74
- []
75
- end
76
-
77
- include Enumerable
78
- def length
79
- matches.length
80
- end
81
-
82
- def <<(node)
83
- matches<<node
84
- end
85
-
86
- def add_delimiter(node)
87
- delimiter_matches<<node
88
- end
89
-
90
- def [](i)
91
- matches[i]
92
- end
93
-
94
- def each(&block)
95
- matches.each(&block)
96
- end
97
91
  end
98
92
 
99
93
  class RootNode < Node
100
94
  end
101
- end
95
+ end