Skip to content

Commit

Permalink
Initial stab at generator, working good. Got function call and assign…
Browse files Browse the repository at this point in the history
… working.
  • Loading branch information
macournoyer committed Oct 21, 2008
1 parent 47de9b4 commit 8309001
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 49 deletions.
9 changes: 9 additions & 0 deletions lib/orange.rb
Expand Up @@ -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
71 changes: 69 additions & 2 deletions lib/orange/generator.rb
@@ -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
22 changes: 15 additions & 7 deletions lib/orange/grammar.tt
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
80 changes: 40 additions & 40 deletions lib/orange/nodes.rb
Expand Up @@ -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.