Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
charliesome committed Apr 5, 2012
0 parents commit 5559ffc
Show file tree
Hide file tree
Showing 7 changed files with 488 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source :rubygems

gem "bundler"
gem "cast19", "~> 0.1.1"
9 changes: 9 additions & 0 deletions bin/d16cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env ruby
#require "bundler"
#Bundler.setup
require "d16cc"

src = File.read ARGV.first
compiler = D16CC::Compiler.new src
compiler.compile
puts compiler.asm
8 changes: 8 additions & 0 deletions lib/d16cc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require "cast19"

module D16CC
require "d16cc/types"
require "d16cc/scope"
require "d16cc/node_compiler"
require "d16cc/compiler"
end
101 changes: 101 additions & 0 deletions lib/d16cc/compiler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module D16CC
class Compiler
class Section
attr_reader :name, :lines

def initialize(name)
@name, @lines = name, []
end

def <<(line)
lines << line.to_s
end

def to_s
[":#{name}", *lines.map { |l| " #{l}" }].join "\n"
end
end

attr_reader :types, :source, :sections, :symbols

def initialize(source)
@source = source
@sections = Hash.new { |h,k| h[k] = Section.new(k) }
@symbols = {}
@types = {
"char" => Types::Int16,
"short" => Types::Int16,
"int" => Types::Int16,
"long" => Types::Int32,
}
@types.keys.each do |name|
@types["unsigned #{name}"] = @types[name].as_unsigned
end
end

def compile
@ast = C.parse source
@node_compiler = NodeCompiler.new self, @ast
@node_compiler.compile
end

def asm
[sections["main"], *sections.reject { |k,v| k == "main" }.values].map(&:to_s).join("\n\n")
end

def section
scope.section
end

def scope
@current_scope
end

def function
scope.function
end

def with_scope(fn)
@current_scope = Scope.new sections[fn.name], fn
yield
@current_scope = nil
end

def ast_type(node)
case node
when C::Int
case node.longness
when 1 then Types::Int32
when 0 then Types::Int16
when -1 then Types::Int16
else raise "wtf"
end
else
raise "can't find own type for #{node.class}!"
require "pry"
pry binding
end
end

def expression_type(node)
case node
when C::IntLiteral
type = (node.suffix && node.suffix.include?("l")) ? Types::Int32 : Types::Int16
type = type.as_unsigned if node.suffix and node.suffix.include? "u"
type
when C::Variable
if scope[node.name]
scope[node.name].type
elsif symbols[node.name]
symbols[node.name].type
else
@node_compiler.error! node, "Undefined identifier #{node.name}"
end
else
raise "can't find expression type in #{node.class}!"
require "pry"
pry binding
end
end
end
end
172 changes: 172 additions & 0 deletions lib/d16cc/node_compiler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
module D16CC
class NodeCompiler
attr_reader :compiler, :ast

def initialize(compiler, ast)
@compiler, @ast = compiler, ast
@node_stack = []
end

def compile
compile_node ast
end

def error!(node, message)
raise "At #{node.pos} - #{message}"
end

def warn!(node, message)
warn "At #{node.pos} - #{message}"
end

private
def node
@node_stack.last
end

def type_of(node)
node.class.name.split("::").last
end

def compile_node(node)
if respond_to? type_of(node), true
@node_stack.push node
send type_of(node)
@node_stack.pop
else
error! node, "Node #{type_of(node)} not implemented!"
end
end

def save_local(local, reg = "A")
compiler.section << "SET #{local_ref local}, #{reg}"
end

def load_local(local, reg = "A")
compiler.section << "SET #{reg}, #{local_ref local}"
end

def local_ref(local)
"[#{-local.offset % 65536}+Z]"
end

# node compilers:

def TranslationUnit
node.entities.each { |child| compile_node child }
end

def FunctionDef
if compiler.symbols[node.name]
error! node, "Redeclaration of '#{node.name}' as different type" unless compiler.symbols[node.name] =~ node.type
else
compiler.symbols[node.name] = node.type
end
compiler.with_scope node do
if node.type.params
arg_offset = -2
node.type.params.each do |param|
type = compiler.ast_type(param.type)
compiler.scope.set_local Scope::Local.new compiler.scope, param.name, type, arg_offset
arg_offset -= type.size
end
end
compiler.section << "SET PUSH, Z"
compiler.section << "SET Z, SP"
compile_node node.def
end
end

def Block
node.stmts.each { |stmt| compile_node stmt }
end

def Return
return_type = compiler.ast_type(compiler.function.type.type)
if return_type.is_a? Types::Void
error! node, "Returning value from void function" if node.expr
else
error! node, "Returning no value from non-void function" unless node.expr
compile_node node.expr
end
# results of expressions are always left in A, which is our return register
compiler.section << "SET SP, Z"
compiler.section << "SET Z, POP"
compiler.section << "SET PC, POP"
end

def IntLiteral
type = compiler.expression_type(node)
error! node, "Integer literal too large. Maximum for this type is #{type.max_value}" if node.val > type.max_value
error! node, "Integer literal too small. Minimum for this type is #{type.min_value}" if node.val < type.min_value
compiler.section << "SET A, #{node.val}"
end

def Call
if node.expr.is_a? C::Variable and not compiler.scope[node.expr.name]
unless compiler.symbols[node.expr.name]
warn! node.expr, "implicit declaration of function '#{node.expr.name}'"
compiler.symbols[node.expr.name] = Types::Function.new Types::Int16, *([Types::Int16] * node.args.size)
end
end
# @TODO type checks on arguments
node.args.reverse_each do |arg|
compile_node arg
compiler.section << "SET PUSH, A"
end
if node.expr.is_a? C::Variable and not compiler.scope[node.expr.name]
compiler.section << "JSR #{node.expr.name}"
else
compile_node node.expr
compiler.section << "JSR [A]"
end
compiler.section << "ADD SP, #{node.args.size}"
end

def Variable
if local = compiler.scope[node.name]
load_local local
else
compiler.section << "SET A, [#{node.name}]"
end
end

def Add
type1 = compiler.expression_type(node.expr1)
type2 = compiler.expression_type(node.expr2)
unless type1.is_a? Types::Integral and type2.is_a? Types::Integral
error! node, "Addition can't be performed on these operand types"
end
if type1.size > 1 or type2.size > 1
# 32 bit addition
compile_node node.expr1
compiler.section << "SET B, 0" if type1.size == 1 # zero extend
compiler.scope.with_temp 2 do |a,b|
save_local a, "A"
save_local b, "B"
compile_node node.expr2
compiler.section << "SET B, 0" if type2.size == 1 # zero extend
compiler.section << "ADD B, #{local_ref b}"
compiler.section << "ADD A, #{local_ref a}"
compiler.section << "ADD B, O"
end
else
# 16 bit addition
compile_node node.expr1
compiler.scope.with_temp do |temp|
save_local temp, "A"
compile_node node.expr2
compiler.section << "ADD A, #{local_ref temp}"
end
end
end

def ExpressionStatement
if node.expr.is_a? C::Variable and node.expr.name == "__halt"
compiler.section << "SET PC, 0xFFF0" # crash it with an illegal opcode
else
compile_node node.expr
end
end
end
end
58 changes: 58 additions & 0 deletions lib/d16cc/scope.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module D16CC
class Scope
class Local
attr_reader :scope, :name, :type, :offset

def initialize(scope, name, type, offset)
@scope, @name, @type, @offset = scope, name, type, offset
end
end

attr_reader :section, :function, :size

def initialize(section, function)
@section = section
@function = function
@variables = {}
@temp = []
@size = 1
end

def acquire_temp
if @temp.any?
@temp.pop
else
local = Local.new self, "___temp_#{@size}", Types::Int32, @size
@size += 1
local
end
end

def release_temp(temp)
@temp << temp
end

def with_temp(count = 1)
temps = count.times.map { acquire_temp }
yield(*temps)
ensure
temps.each { |temp| release_temp temp }
end

def set_local(local)
if @variables[local.name]
raise "Can't redeclare #{local.name}"
end
@variables[local.name] = local
end

def declare(name, type)
set_local Local.new self, name, type, @size
@size += type.size
end

def [](name)
@variables[name]
end
end
end
Loading

0 comments on commit 5559ffc

Please sign in to comment.