babel_bridge 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/babel_bridge.gemspec 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