"set" => :set,
"return" => :return,
"assert" => :assert,
+ "module" => :module
}
def next
class Parser
attr_accessor :syms
attr_accessor :checker
+ attr_reader :path
+ attr_reader :module
def initialize(path = nil)
+ @path = path
@syms = SymTable.new
@checker = TypeChecker.new(self)
- syms.add_builtin(:void, 0, :type, :void)
- syms.add_builtin(:bool, 0, :type, :bool)
- syms.add_builtin(:int, 0, :type, :int)
- syms.add_builtin(:string, 0, :type, :string)
- syms.add_builtin(:float, 0, :type, :float)
+# syms.add_builtin(:void, 0, :type, :void)
+# syms.add_builtin(:bool, 0, :type, :bool)
+# syms.add_builtin(:int, 0, :type, :int)
+# syms.add_builtin(:string, 0, :type, :string)
+# syms.add_builtin(:float, 0, :type, :float)
+
+ syms.add_builtin(:Length, :func, [:any, :int], nil)
+ syms.add_builtin(:Error, :func, [:any, :void], nil)
+
parse_file(path)
end
end
def toplevel
+ expect :module
+ @module = expect(:ident).text.to_sym
imports
exports
while !matches(:eof)
end
def local?(name)
- find_sym(@scopes[3..-1], name)
+ find_sym((@scopes[3..-1] || []).flatten, name)
end
def find_sym(scopes, name)
scopes.reverse_each.detect do |scope|
- return scope[name]
+ return scope[name] if scope[name]
end
+ nil
end
def open_scope()
# Code Generation Module
##
-module Codegen
+class Codegen
State = Struct.new(:parser, :label, :locals, :syms, :temp, :indent)
- def self.init(parser)
- State.new(parser, 0, [], parser.syms, 0, 1)
- end
-
- def self.putln(state, msg)
- puts (" " * state.indent) + msg
- end
+ def initialize(parser)
+ @parser = parser
+ @label = 0
+ @locals = []
+ @syms = @parser.syms
+ @temp = 0
+ @indent = 1
+ @output = $stdout
+# @state = State.new(parser, 0, [], parser.syms, 0, 1)
+ end
+
+ def output(outpath)
+ @output = File.open(outpath, "wb")
+
+ puts "#include <runtime.h>\n\n"
+ @syms.each do |name, val|
+ args = val.value.args.map{|e| "Value #{e.name}" }.join(", ")
+ puts "Value #{symname(val)}(#{args});"
+ end
+ puts ""
+
+ @syms.each do |name, val|
+ args = val.value.args.map{|e| "Value #{e.name}" }.join(", ")
+ puts "Value #{symname(val)}(#{args}) {"
+ @syms.open_scope
+ @locals = val.value.locals
+ val.value.args.each_with_index do |name, idx|
+ @syms.add_sym(name.name, name.loc, :param, name.type, idx)
+ end
+ val.value.body.each do |stmnt|
+ emit(stmnt)
+ end
+ puts "}\n\n"
+ @syms.close_scope
+ end
- def self.mktemp(state)
- name = "_t#{state.temp}"
- state.temp += 1
- name
+ @output.close
end
- def self.genlabel(state)
- label = state.label
- state.label = state.label + 1
- end
+ private
- def self.emit(state, v)
+ def emit(v)
if v.is_a? IR::Return then
- emit_return(state, v)
+ emit_return(v)
elsif v.is_a? IR::Const then
- emit_const(state, v)
+ emit_const(v)
elsif v.is_a? IR::Op then
- emit_binop(state, v)
+ emit_binop(v)
elsif v.is_a? IR::Var then
- emit_var(state, v)
+ emit_var(v)
elsif v.is_a? IR::Call then
- emit_call(state, v)
+ emit_call(v)
elsif v.is_a? IR::Def then
- emit_def(state, v)
+ emit_def(v)
elsif v.is_a? IR::Set then
- emit_set(state, v)
+ emit_set(v)
elsif v.is_a? IR::Assert then
- emit_assert(state, v)
+ emit_assert(v)
elsif v.is_a? IR::If then
- emit_if(state, v)
+ emit_if(v)
else
raise "code emitting of #{v.class} not implemented"
end
end
- def self.emit_return(state, v)
- if v.value
- temp = emit(state, v.value)
- putln state, "return #{temp};"
- else
- putln state, "return MakeNil();"
- end
+ def emit_assert(v)
+ temp = emit(v.value)
+ linenum = @parser.linenum(v.loc[1])
+ putln "Assert(#{v.loc[0].inspect}, #{linenum}, #{temp});"
end
- def self.emit_const(state, v)
- var = mktemp(state)
+ def emit_const(v)
+ var = mktemp()
if v.value.is_a? Integer
- putln state, "Value #{var} = MakeInt(#{v.value});"
+ putln "Value #{var} = MakeInt(#{v.value});"
elsif v.value.is_a? Float
- putln state, "Value #{var} = MakeReal(#{v.value});"
+ putln "Value #{var} = MakeReal(#{v.value});"
elsif v.value == true || v.value == false
- putln state, "Value #{var} = MakeBool(#{v.value});"
+ putln "Value #{var} = MakeBool(#{v.value});"
elsif v.value.is_a? String
- putln state, "Value #{var} = MakeString(#{v.value});"
+ putln "Value #{var} = MakeString(#{v.value});"
elsif v.value.is_a? Array
- putln state, "Value #{var} = MakeArray(#{v.value.length});"
+ putln "Value #{var} = MakeArray(#{v.value.length});"
v.value.each_with_index do |e, i|
- val = emit(state, e)
- putln state, "Array_Set(#{var}, #{i}, #{val});"
-
+ val = emit(e)
+ putln "Array_Set(#{var}, #{i}, #{val});"
end
elsif v.value.is_a? Hash
- putln state, "Value #{var} = MakeDict(#{v.value.length});"
+ putln "Value #{var} = MakeDict(#{v.value.length});"
v.value.each do |k, v|
- val = emit(state, v)
- putln state, "Dict_Set(#{var}, MakeString(#{k.value}), #{val});"
+ val = emit(v)
+ putln "Dict_Set(#{var}, MakeString(#{k.value}), #{val});"
end
else
raise "code emitting constants of this type not implemented"
var
end
- def self.emit_binop(state, v)
+ def emit_return(v)
+ if v.value
+ temp = emit(v.value)
+ putln "return #{temp};"
+ else
+ putln "return MakeNil();"
+ end
+ end
+
+ def emit_binop(v)
if v.op == "&&"
- lvar = emit(state, v.left)
- result = mktemp(state);
- putln state, "Value #{result} = #{lvar};"
- putln state, "if (IsTrue(#{result})) {"
- state.indent += 1
- rvar = emit(state, v.right)
- putln state, "#{result} = #{rvar};"
- state.indent -= 1
- putln state, "} else {"
- putln state, " #{result} = MakeBool(false);"
- putln state, "}"
+ lvar = emit(v.left)
+ result = mktemp();
+ putln "Value #{result} = #{lvar};"
+ putln "if (IsTrue(#{result})) {"
+ @indent += 1
+ rvar = emit(v.right)
+ putln "#{result} = #{rvar};"
+ @indent -= 1
+ putln "} else {"
+ putln " #{result} = MakeBool(false);"
+ putln "}"
elsif v.op == "||"
- lvar = emit(state, v.left)
- result = mktemp(state);
- putln state, "Value #{result} = #{lvar};"
- putln state, "if (IsFalse(#{result})) {"
- state.indent += 1
- rvar = emit(state, v.right)
- putln state, "#{result} = #{rvar};"
- state.indent -= 1
- putln state, "}"
+ lvar = emit(v.left)
+ result = mktemp();
+ putln "Value #{result} = #{lvar};"
+ putln "if (IsFalse(#{result})) {"
+ @indent += 1
+ rvar = emit(v.right)
+ putln "#{result} = #{rvar};"
+ @indent -= 1
+ putln "}"
elsif v.op == "["
- lvar = emit(state, v.left)
- rvar = emit(state, v.right)
- result = mktemp(state);
- putln state, "Value #{result} = Object_Get(#{lvar}, #{rvar});"
+ lvar = emit(v.left)
+ rvar = emit(v.right)
+ result = mktemp();
+ putln "Value #{result} = Object_Get(#{lvar}, #{rvar});"
else
- lvar = emit(state, v.left)
- rvar = emit(state, v.right)
- result = mktemp(state);
+ lvar = emit(v.left)
+ rvar = emit(v.right)
+ result = mktemp();
case v.op
when "+"
- putln state, "Value #{result} = OpAdd(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpAdd(#{lvar}, #{rvar});"
when "-"
- putln state, "Value #{result} = OpSub(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpSub(#{lvar}, #{rvar});"
when "*"
- putln state, "Value #{result} = OpMul(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpMul(#{lvar}, #{rvar});"
when "/"
- putln state, "Value #{result} = OpDiv(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpDiv(#{lvar}, #{rvar});"
when "%"
- putln state, "Value #{result} = OpMod(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpMod(#{lvar}, #{rvar});"
when "<"
- putln state, "Value #{result} = OpLt(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpLt(#{lvar}, #{rvar});"
when "<="
- putln state, "Value #{result} = OpLtEq(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpLtEq(#{lvar}, #{rvar});"
when ">"
- putln state, "Value #{result} = OpGt(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpGt(#{lvar}, #{rvar});"
when ">="
- putln state, "Value #{result} = OpGtEq(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpGtEq(#{lvar}, #{rvar});"
when "=="
- putln state, "Value #{result} = OpEq(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpEq(#{lvar}, #{rvar});"
when "!="
- putln state, "Value #{result} = OpNeq(#{lvar}, #{rvar});"
+ putln "Value #{result} = OpNeq(#{lvar}, #{rvar});"
else
raise "not implemented"
end
result
end
- def self.emit_var(state, v)
- var = state.syms[v.name]
- if var.nil?
- raise "no such variable: #{v.name}"
- end
- var.name
- end
-
- def self.emit_call(state, v)
- result = mktemp(state)
+ def emit_call(v)
+ result = mktemp()
vars = v.args.reverse.map do |arg|
- emit(state, arg)
+ emit(arg)
end.join(", ")
- putln state, "Value #{result} = #{v.func.name}(#{vars});"
+ func = lookup_func(v.func)
+ if (func.is_a? SymTable::Symbol) then
+ error v, "symbol '#{func.name}' is not a function" if (func.kind != :func)
+ putln "Value #{result} = #{symname(func)}(#{vars});"
+ else
+ error v, "expression result is not a function"
+ end
+
result
end
- def self.emit_def(state, v)
- state.syms.add_sym(
- v.name.name, v.loc, :local, v.type, v.value)
- temp = emit(state, v.value)
- raise "invalid local definition: #{v.name.name}" if not state.locals.index(v.name.name)
- putln state, "Value #{v.name.name} = #{temp};"
+ def emit_def(v)
+ @syms.add_sym(v.name.name, v.loc, :local, v.type, v.value)
+ temp = emit(v.value)
+ error v, "invalid local definition: #{v.name.name}" if not @locals.index(v.name.name)
+ putln "Value #{v.name.name} = #{temp};"
end
- def self.emit_set(state, v)
+ def emit_set(v)
if v.name.is_a? IR::Op
- raise "not a valid array or hash access expression" if v.name.op != "["
- object = emit(state, v.name.left)
- key = emit(state, v.name.right)
- value = emit(state, v.value)
- putln state, "Object_Set(#{object}, #{key}, #{value});"
+ error v, "not a valid array or hash access expression" if v.name.op != "["
+ object = emit(v.name.left)
+ key = emit(v.name.right)
+ value = emit(v.value)
+ putln "Object_Set(#{object}, #{key}, #{value});"
else
- temp = emit(state, v.value)
- raise "invalid local definition: #{v.name.name}" if not state.locals.index(v.name.name)
- putln state, "#{v.name.name} = #{temp};"
+ temp = emit(v.value)
+ error v, "invalid local definition: #{v.name.name}" if not @locals.index(v.name.name)
+ putln "#{v.name.name} = #{temp};"
end
end
- def self.emit_assert(state, v)
- temp = emit(state, v.value)
- linenum = state.parser.linenum(v.loc[1])
- putln state, "Assert(#{v.loc[0].inspect}, #{linenum}, #{temp});"
+ def emit_var(v)
+ var = @syms[v.name]
+ if var.nil?
+ error v, "no such variable: #{v.name}"
+ end
+ var.name
end
- def self.emit_if(state, v)
- cond = emit(state, v.cond)
- putln state, "if (#{cond}) {"
- emit_block(state, v.then)
- putln state, "} else {"
- emit_block(state, v.else)
- putln state, "}"
+ def emit_if(v)
+ cond = emit(v.cond)
+ putln "if (ValueAsBool(#{cond})) {"
+ emit_block(v.then)
+ putln "} else {"
+ emit_block(v.else)
+ putln "}"
end
- def self.emit_block(state, v)
+ def emit_block(v)
return if v.nil?
- state.indent += 1
+ @indent += 1
v.each do |v|
- emit(state, v);
+ emit(v);
end
- state.indent -= 1
+ @indent -= 1
end
-end
-##
-# Add Builtin Functions
-##
+ def lookup_func(func)
+ if func.is_a? IR::Var then
+ value = @syms[func.name]
+ error func, "no such function: #{func.name}" if value.nil?
+ else
+ value = func
+ end
+ value
+ end
+ def error(expr, msg)
+ file = @parser.path
+ line = @parser.linenum(expr.loc)
+ $stderr.puts "#{file}:#{line}:error: #{msg}"
+ exit 1
+ end
+ def puts(msg)
+ @output.puts msg
+ end
-##
-# Parse and Analyze
-##
+ def putln(msg)
+ @output.puts (" " * @indent) + msg
+ end
-$parser = Parser.new("cerise-c.m")
-$checker = TypeChecker.new($parser)
-$state = Codegen.init($parser)
+ def mktemp()
+ name = "_t#{@temp}"
+ @temp += 1
+ name
+ end
+
+ def genlabel()
+ state.label = state.label + 1
+ end
+
+ def symname(sym)
+ if @syms.builtin? sym.name
+ "#{sym.name}"
+ else
+ "#{@parser.module}_#{sym.name}"
+ end
+ end
+end
##
-# Emit the Code
+# Parse and Analyze
##
-puts "#include <runtime.h>\n\n"
-$parser.syms.each do |name, val|
- args = val.value.args.map{|e| "Value #{e.name}" }.join(", ")
- puts "Value #{name}(#{args});"
-end
-puts ""
-$parser.syms.each do |name, val|
- args = val.value.args.map{|e| "Value #{e.name}" }.join(", ")
- puts "Value #{name}(#{args}) {"
- $parser.syms.open_scope
- $state.locals = val.value.locals
- val.value.args.each_with_index do |name, idx|
- $parser.syms.add_sym(
- name.name, name.loc, :param, name.type, idx)
- end
- val.value.body.each do |stmnt|
- Codegen.emit($state, stmnt)
- end
- puts "}\n\n"
- $parser.syms.close_scope
-end
+$parser = Parser.new("cerise-c.m")
+$checker = TypeChecker.new($parser)
+$codegen = Codegen.new($parser)
+$codegen.output("cerise-c.c")
+`gcc -c -I. -O1 -o cerise-c.o #{"cerise-c.c"}`