Permalink
Browse files

Initial stab at generator, working good. Got function call and assign…

… working.
  • Loading branch information...
1 parent 47de9b4 commit 8309001aa9fa59b20c4e38dd1f6ac133f27aeab6 @macournoyer committed Oct 21, 2008
Showing with 133 additions and 49 deletions.
  1. +9 −0 lib/orange.rb
  2. +69 −2 lib/orange/generator.rb
  3. +15 −7 lib/orange/grammar.tt
  4. +40 −40 lib/orange/nodes.rb
View
@@ -10,3 +10,12 @@
else
Treetop.load File.dirname(__FILE__) + "/orange/grammar.tt"
end
+
+g = Orange::Generator.new
+OrangeParser.new.parse(<<-EOS).compile(g).to_s
+ x = "ohaie"
+ printf(x)
+EOS
+puts g.inspect
+g.optimize
+g.run
View
@@ -1,11 +1,78 @@
-require "llvmruby"
+require "rubygems"
+require "llvm"
module Orange
class Generator
include LLVM
+ PCHAR = Type.pointer(Type::Int8Ty)
+ INT = Type::Int32Ty
+
def initialize
- @module = Module.new("orange")
+ @module = LLVM::Module.new("orange")
+
+ @locals = {}
+
+ define_external_functions
+
+ @main = @module.get_or_insert_function("main", Type.function(INT, [INT, Type.pointer(PCHAR)]))
+ @entry_block = @main.create_block.builder
+ end
+
+ def new_string(value)
+ @entry_block.create_global_string_ptr(value)
+ end
+
+ def call(func, *args)
+ f = @module.get_function(func)
+ @entry_block.call(f, *args)
+ end
+
+ def assign(name, value)
+ # HACK assume *char for now
+ ptr = @entry_block.alloca(PCHAR, 0)
+ @entry_block.store(value, ptr)
+ @locals[name] = ptr
+ end
+
+ def load(name)
+ @entry_block.load(@locals[name])
+ end
+
+ def finish
+ @entry_block.return(0.llvm)
end
+
+ def optimize
+ PassManager.new.run(@module)
+ end
+
+ def run
+ ExecutionEngine.get(@module)
+ ExecutionEngine.run_function(@main, 0.llvm, 0.llvm)
+ end
+
+ def to_file(file)
+ @module.write_bitcode(file)
+ end
+
+ def inspect
+ @module.inspect
+ end
+
+ private
+ def define_external_functions
+ @module.external_function("printf", Type.function(INT, [PCHAR], true))
+ end
end
+end
+
+if __FILE__ == $PROGRAM_NAME
+ g = Orange::Generator.new
+ str = g.new_string("ohaie\n")
+ g.assign("x", str)
+ g.call("printf", g.load("x"))
+ g.finish
+ puts g.inspect
+ g.run.inspect
end
View
@@ -30,15 +30,23 @@ grammar Orange
end
rule def
- "def" space var arglist space newline
+ "def" space func:var arglist space newline
expressions:(expression newline)* space
- "end"
+ "end" <Def> {
+ def expressions
+ super.elements.map { |e| e.expression }
+ end
+ }
end
rule if
- "if" space expression space newline
+ "if" space condition:expression space newline
expressions:(expression newline)* space
- "end"
+ "end" <If> {
+ def expressions
+ super.elements.map { |e| e.expression }
+ end
+ }
end
rule cmp
@@ -68,23 +76,23 @@ grammar Orange
end
rule var
- !keyword [a-z] [\w]*
+ !keyword [a-z] [\w]* <Var>
end
rule keyword
"def" / "end"
end
rule number
- [0-9]+ {
+ [0-9]+ <Number> {
def value
text_value.to_i
end
}
end
rule string
- '"' (!'"' . / '\"')* '"' {
+ '"' (!'"' . / '\"')* '"' <String> {
def value
text_value.gsub('"', '')
end
View
@@ -9,63 +9,63 @@ def codegen(context)
end
Node = Treetop::Runtime::SyntaxNode
- class Context
- attr_reader :locals
-
- def initialize
- @out = []
- @locals = {}
- end
-
- def <<(o)
- @out << o
- end
-
- def to_s
- @out.compact.join("\n")
- end
- end
-
class Script < Node
def compile(generator)
- context = Context.new
- expressions.each do |exp|
- exp.codegen(context)
- end
- context
+ expressions.each { |e| e.codegen(generator) }
+ generator.finish
end
end
class Expression < Node
- def codegen(context)
- statements.each { |s| s.codegen(context) }
+ def codegen(g)
+ statements.map { |s| s.codegen(g) }
end
end
class Assign < Node
- def codegen(context)
- context.locals[var.value] = true
- expression.codegen(context)
- puts "setlocal: #{var.value} = #{expression.value}"
+ def codegen(g)
+ g.assign var.value, expression.codegen(g).last
end
end
class Call < Node
- def codegen(context)
- if receiver.nil? && context.locals[message.value]
- puts "getlocal: #{message.value}"
- else
- puts "call: #{receiver ? receiver.value : 'self'}.#{message.value}(#{arglist.args.map { |a| a.value }.join(", ")})"
- arglist.codegen(context) if arglist.is_a?(Block)
- arglist.block.codegen(context) if arglist.respond_to?(:block)
- end
+ def codegen(g)
+ arg_values = arglist.args.map { |arg| arg.codegen(g) }
+ g.call(func.value, *arg_values)
end
end
- class Block < Node
- def codegen(context)
- puts "in block: (#{arglist.args.map { |a| a.value }.join(", ") if arglist})"
- expressions.each { |exp| exp.codegen(context) }
+ class Def < Node
+ def codegen(g)
+ puts "def: #{func.value}"
+ expressions.each { |e| e.codegen(g) }
+ puts "end"
+ end
+ end
+
+ class If < Node
+ def codegen(g)
+ puts "if: #{condition.value}"
+ expressions.each { |e| e.codegen(g) }
+ puts "end"
+ end
+ end
+
+ class Var < Node
+ def codegen(g)
+ g.load(value)
+ end
+ end
+
+ class String < Node
+ def codegen(g)
+ g.new_string(value)
+ end
+ end
+
+ class Number < Node
+ def codegen(g)
+ value.llvm
end
end
end

0 comments on commit 8309001

Please sign in to comment.