--- /dev/null
+#!/usr/bin/env ruby
+
+# TODO:
+#
+# * Add unary operators for integers
+# * Add type checking to operators on constants
+# * Add optimization of constant operators
+# * Add increment/decrement optimization
+# * Implement function calls
+# * Implement string constants
+# * Implement if statement
+# * Implement tail call optimization?
+
+require 'stringio'
+require 'strscan'
+
+
+##
+# Intermediate Representation Module
+##
+
+module IR
+ Func = Struct.new(:loc, :type, :args, :locals, :body)
+ Return = Struct.new(:loc, :type, :value)
+ Assert = Struct.new(:loc, :type, :value)
+ Const = Struct.new(:loc, :type, :value)
+ Var = Struct.new(:loc, :type, :name)
+ Def = Struct.new(:loc, :type, :name, :value)
+ Set = Struct.new(:loc, :type, :name, :value)
+ Op = Struct.new(:loc, :type, :op, :left, :right)
+ Call = Struct.new(:loc, :type, :func, :args)
+ If = Struct.new(:loc, :type, :cond, :then, :else)
+end
+
+
+##
+# Data Type Representation Module
+##
+
+module Value
+ # Forms: :bool, :int, :float, :void, :array, :record, :string, :proc
+ Type = Struct.new(:form, :fields, :base, :size)
+ Field = Struct.new(:type, :offset)
+
+ # Base type definitions
+ Void = Type.new(:void, nil, nil, 0)
+ Bool = Type.new(:bool, nil, nil, 1)
+ Int = Type.new(:int, nil, nil, 8)
+ Float = Type.new(:float, nil, nil, 8)
+ String = Type.new(:string, nil, nil, 8)
+end
+
+
+##
+# Lexical Analyzer Definition
+##
+
+class Lexer
+ Tok = Struct.new(:text, :file, :pos, :type)
+
+ attr_accessor :file
+ attr_accessor :data
+
+ def initialize(path = nil)
+ @file = (path || "<input>")
+ @text = (path ? File.read(path) : "")
+ @data = StringScanner.new(@text)
+ end
+
+ def parse_string(str)
+ @file = "<input>"
+ @text = str
+ @data = StringScanner.new(@text)
+ end
+
+ def linenum(pos = nil)
+ pos = [pos || @data.pos].flatten.last
+ @text[0..pos].count("\n") + 1
+ end
+
+ SPACE = /([ \t\v\n\r]+|#.*\n)/
+ IDENT = /[_a-zA-Z][_a-zA-Z0-9]*/
+ BRACES = /[\(\)\[\]\{\}\.]/
+ OPERATORS = /[:,<>*\/=+\-\$?!%&|]+/
+ INTEGER = /[0-9]+/
+ FLOATING = /[0-9]+\.[0-9]+/
+ STRING = /"(\\"|[^"])*"/
+ ID_TYPES = {
+ "true" => :bool,
+ "false" => :bool,
+ "if" => :if,
+ "else" => :else,
+ "def" => :def,
+ "set" => :set,
+ "return" => :return,
+ "assert" => :assert,
+ }
+
+ def next
+ while @data.skip(SPACE) do
+ end
+ if not @data.eos?
+ type = :eof
+ if @data.scan(IDENT)
+ type = (ID_TYPES[@data.matched] || :ident)
+ elsif @data.scan(FLOATING)
+ type = :float
+ elsif @data.scan(INTEGER)
+ type = :int
+ elsif @data.scan(STRING)
+ type = :string
+ elsif @data.scan(BRACES)
+ type = @data.matched
+ elsif @data.scan(OPERATORS)
+ type = @data.matched
+ end
+ tok = Tok.new(@data.matched, @file, @data.pos, type) if type
+ else
+ tok = Tok.new("EOF", @file, @data.pos, :eof)
+ end
+# pp tok
+ tok
+ end
+end
+
+
+##
+# Code Parsing Module
+##
+
+class Parser
+ attr_accessor :syms
+ attr_accessor :checker
+
+ def initialize(path = nil)
+ @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)
+ parse_file(path)
+ end
+
+ def parse_file(path)
+ @lex = Lexer.new(path)
+ @prev = nil
+ @next = nil
+ toplevel
+ end
+
+ def error(str, loc = nil)
+ file, pos = (loc ? [@lex.file, loc] : [@lex.file, (@next || @prev).pos])
+ $stderr.puts "#{file}:#{@lex.linenum(pos)}: #{str}"
+ exit 1
+ end
+
+ def peek()
+ @next = @lex.next if @next.nil?
+ @next
+ end
+
+ def matches(type)
+ (peek().type == type)
+ end
+
+ def matches_any(types)
+ types.any? {|type| peek().type == type }
+ end
+
+ def accept(type)
+ if (matches(type))
+ @prev = @next
+ @next = nil
+ true
+ else
+ false
+ end
+ end
+
+ def expect(type)
+ tok = peek()
+ if not accept(type)
+ error("expected '#{type}', received '#{tok.type}'")
+ end
+ tok
+ end
+
+ def consume()
+ expect(peek().type)
+ end
+
+ def eof?
+ (peek().type == :eof)
+ end
+
+ def location()
+ if @prev
+ [@prev.file, @prev.pos]
+ else
+ [@lex.file, 0]
+ end
+ end
+
+ OPERATORS = {
+ "+" => true,
+ "-" => true,
+ "*" => true,
+ "/" => true,
+ "%" => true,
+ "<" => true,
+ ">" => true,
+ "<=" => true,
+ ">=" => true,
+ "==" => true,
+ "!=" => true,
+ "&&" => true,
+ "||" => true,
+ }
+
+ def operator?
+ OPERATORS[peek().type]
+ end
+
+ def toplevel
+ imports
+ exports
+ while !matches(:eof)
+ mod_def = toplevel_defintion()
+ if mod_def.value.is_a? IR::Const
+ kind = :const
+ elsif mod_def.value.is_a? IR::Var
+ kind = :var
+ elsif mod_def.value.is_a? IR::Func
+ kind = :func
+# elsif mod_def.value.is_a? IR::Type
+# kind = :type
+ else
+ raise "invalid toplevel definition #{mod_def.value.class}"
+ end
+
+ syms.add_sym(
+ mod_def.name.name,
+ mod_def.name.loc,
+ kind,
+ mod_def.type,
+ mod_def.value
+ )
+ end
+ end
+
+ def imports
+ # TBD
+ end
+
+ def exports
+ # TBD
+ end
+
+ def toplevel_defintion
+ ident = identifier()
+ if matches("(") then
+ val = function_definition(ident)
+ elsif matches("=") then
+ val = constant_definition(ident)
+ else
+ error("#{ident.name} is not a valid toplevel definition")
+ end
+ IR::Def.new(ident.loc, nil, ident, val)
+ end
+
+ def function_definition(name)
+ args = function_arglist()
+ func = IR::Func.new(name.loc, :void, args, [], [])
+ defs = []
+# if accept(":")
+# func.type = expect(:ident).text.to_sym
+# end
+ expect("{")
+ while matches(:def)
+ local = variable_definition()
+ func.locals << local.name.name
+ defs << local;
+ end
+ stmts = statement_list(["}", :return])
+ stmts << return_statement
+ func.body = defs + stmts
+ expect("}");
+ func
+ end
+
+ def constant_definition(name)
+ error("constant definitions not yet supported")
+ end
+
+ def function_arglist()
+ args = []
+ expect("(")
+ while !matches(")")
+ id = identifier()
+# expect(":")
+# id.type = expect(:ident).text.to_sym
+ args << id
+ expect(",") if !matches(")")
+ end
+ expect(")")
+ args
+ end
+
+ def identifier()
+ name = expect(:ident)
+ IR::Var.new(name.pos, nil, name.text.to_sym)
+ end
+
+
+ ##
+ # Statements
+ ##
+ def statement_list(terminators)
+ stmts = []
+ while !matches_any(terminators)
+ stmts << statement
+ end
+ stmts
+ end
+
+ def statement()
+ if matches(:set)
+ variable_assignment()
+ elsif matches(:assert)
+ assert_statement()
+ elsif matches(:if)
+ if_statement()
+ else
+ expression()
+ end
+ end
+
+ def return_statement()
+ loc = location
+ expect(:return);
+ if !matches("}")
+ IR::Return.new(loc, nil, expression())
+ else
+ IR::Return.new(loc, nil, nil)
+ end
+ end
+
+ def variable_definition()
+ loc = location
+ expect(:def)
+ name = identifier()
+ expect ("=")
+ value = expression()
+ IR::Def.new(loc, nil, name, value)
+ end
+
+ def variable_assignment()
+ loc = location
+ expect(:set)
+ name = identifier()
+ expect ("=")
+ value = expression()
+ IR::Set.new(loc, nil, name, value)
+ end
+
+ def assert_statement()
+ loc = location
+ expect(:assert);
+ IR::Assert.new(loc, nil, expression())
+ end
+
+
+ def if_statement()
+ loc = location
+ expect(:if)
+ cond = expression()
+ then_branch = statement_block()
+ if accept(:else)
+ if matches(:if)
+ else_branch = [ if_statement() ]
+ else
+ else_branch = statement_block()
+ end
+ end
+ IR::If.new(loc, nil, cond, then_branch, else_branch)
+ end
+
+ def statement_block()
+ loc = location
+ stmts = []
+ expect("{")
+ while !matches("}")
+ stmts << statement()
+ end
+ expect("}")
+ return stmts
+ end
+
+
+ ##
+ # Expressions
+ ##
+
+ def expression()
+ left = logical_expr()
+ if matches_any(["&&", "||"])
+ loc = location
+ op = consume()
+ right = logical_expr()
+ left = make_binop(loc, op.text, left, right)
+ end
+ left
+ end
+
+
+ def logical_expr()
+ left = simple_expr()
+ if matches_any(["==", "!=", "<", "<=", ">", ">="])
+ loc = location
+ op = consume()
+ right = simple_expr()
+ left = make_binop(loc, op.text, left, right)
+ end
+ left
+ end
+
+ def simple_expr()
+ # Handle unary ops first
+ if matches_any(["+", "-", :not])
+ loc = location
+ op = consume()
+ left = term()
+ left = make_unop(location, op.text, left, nil)
+ else
+ left = term()
+ end
+
+ # now look for binary ops
+ while matches_any(["+", "-", :or])
+ loc = location
+ op = consume()
+ right = term()
+ left = make_binop(loc, op.text, left, right)
+ end
+ left
+ end
+
+ def term()
+ left = factor()
+ while matches_any(["*", "/", "%", :and])
+ loc = location
+ op = consume()
+ right = factor();
+ left = make_binop(loc, op.text, left, right)
+ end
+ left
+ end
+
+ def make_binop(loc, op, left, right)
+ node = IR::Op.new(loc, nil, op, left, right)
+# checker.infer(syms, node)
+ node
+ end
+
+ def make_unop(loc, op, value)
+ node = IR::Op.new(loc, nil, op, value, nil)
+# checker.infer(syms, node)
+ node
+ end
+
+ def factor()
+ if accept("(")
+ expr = expression()
+ expect(")")
+ else
+ expr = const_or_ident()
+ end
+
+ # Check for function call
+ if matches("(")
+ expr = func_call(expr)
+ end
+ expr
+ end
+
+ def func_call(expr)
+ call = IR::Call.new(location, nil, expr, [])
+ expect("(")
+ while !matches(")")
+ call.args << expression()
+ expect(",") if !matches(")")
+ end
+ expect(")")
+ call
+ end
+
+ def const_or_ident()
+ tok = consume()
+ if tok.type == :bool
+ IR::Const.new(tok.pos, Value::Bool, tok.text == "true")
+ elsif tok.type == :string
+ IR::Const.new(tok.pos, Value::String, tok.text)
+ elsif tok.type == :int
+ IR::Const.new(tok.pos, Value::Int, tok.text.to_i)
+ elsif tok.type == :float
+ IR::Const.new(tok.pos, Value::Float, tok.text.to_f)
+ elsif tok.type == :void
+ IR::Const.new(tok.pos, Value::Void, :void)
+ elsif tok.type == :ident
+ IR::Var.new(tok.pos, nil, tok.text.to_sym)
+ else
+ error("invalid constant #{tok}")
+ end
+ end
+end
+
+
+##
+# Symbol Table Definition
+##
+
+class SymTable
+ Symbol = Struct.new(:name, :loc, :kind, :type, :value)
+
+ def initialize()
+ @scopes = [{}, {}]
+ end
+
+ def builtin?(name)
+ (not local? name) and (not (@scopes[0] || {})[name].nil?)
+ end
+
+ def global?(name)
+ (not local? name) and (not (@scopes[1] || {})[name].nil?)
+ end
+
+ def parameter?(name)
+ (not local? name) and (not (@scopes[2] || {})[name].nil?)
+ end
+
+ def local?(name)
+ find_sym(@scopes[3..-1], name)
+ end
+
+ def find_sym(scopes, name)
+ scopes.reverse_each.detect do |scope|
+ return scope[name]
+ end
+ end
+
+ def open_scope()
+ @scopes.push({})
+ end
+
+ def close_scope()
+ @scopes.pop
+ end
+
+ def reset_scope()
+ @scopes = @scopes[0..1]
+ end
+
+ def [](name)
+ find_sym(@scopes, name)
+ end
+
+ def add_builtin(name, kind, type, value)
+ @scopes[0][name] =
+ Symbol.new(name, 0, kind, type, value)
+ end
+
+ def add_sym(name, loc, kind, type, value)
+ @scopes.last[name] =
+ Symbol.new(name, loc, kind, type, value)
+ end
+
+ def each(&block)
+ @scopes.last.each(&block)
+ end
+end
+
+
+##
+# Type Checking/Inference Module
+##
+
+class TypeChecker
+ BaseToTypes = {
+ :bool => Value::Bool,
+ :int => Value::Int,
+ :float => Value::Float,
+# :string => Value::String,
+ }
+
+ TypesToBase = {
+ Value::Bool => :bool,
+ Value::Int => :int,
+ Value::Float => :float,
+ }
+
+ UnaryOps = {
+ "+" => {
+ int: [:int, :int],
+ float: [:float, :float],
+ },
+ "-" => {
+ int: [:int, :int],
+ float: [:float, :float],
+ },
+ "!" => {
+ bool: [:bool, :bool]
+ },
+ }
+
+ BinaryOps = {
+ "+" => {
+ int: [:int, :int, :int],
+ float: [:float, :float, :float],
+ },
+ "-" => {
+ int: [:int, :int, :int],
+ float: [:float, :float, :float],
+ },
+ "*" => {
+ int: [:int, :int, :int],
+ float: [:float, :float, :float],
+ },
+ "/" => {
+ int: [:int, :int, :int],
+ float: [:float, :float, :float],
+ },
+ "%" => {
+ int: [:int, :int, :int],
+# Float modulo normally implemented as library function
+# float: [:float, :float, :float],
+ },
+ "<" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ },
+ ">" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ },
+ "<=" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ },
+ ">=" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ bool: [:bool, :bool, :bool],
+ },
+ "==" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ bool: [:bool, :bool, :bool],
+ string: [:string, :string, :bool],
+ },
+ "!=" => {
+ int: [:int, :int, :bool],
+ float: [:float, :float, :bool],
+ bool: [:bool, :bool, :bool],
+ string: [:string, :string, :bool],
+ },
+ "&&" => { bool: [:bool, :bool, :bool] },
+ "||" => { bool: [:bool, :bool, :bool] },
+ "<<" => { int: [:int, :int, :int] },
+ ">>" => { int: [:int, :int, :int] },
+ "&" => { int: [:int, :int, :int] },
+ "^" => { int: [:int, :int, :int] },
+ "|" => { int: [:int, :int, :int] },
+ }
+
+ def initialize(parser)
+ @parser = parser
+ end
+
+ def error(loc, msg)
+ @parser.error(msg, loc)
+ end
+
+ def check(env, expr, type)
+ if expr.is_a? IR::Const
+ check_const(env, expr, type)
+ elsif (expr.is_a? IR::Op)
+ check_op(env, expr, type)
+ elsif expr.is_a? IR::Var
+ check_var(env, expr, type)
+# elsif expr.is_a? IR::Let
+# check_let(env, expr, type)
+# elsif expr.is_a? IR::If
+# check_ifexpr(env, expr, type)
+# elsif expr.is_a? IR::Set
+# check_set(env, expr, type)
+# elsif expr.is_a? IR::Func
+# check_func(env, expr, type)
+# elsif expr.is_a? IR::Apply
+# check_apply(env, expr, type)
+ else
+ etype = infer(env, expr)
+ if type != etype
+ error(expr.loc, "expected #{type}, received #{etype}")
+ end
+ end
+ expr.type = type
+ end
+
+ def infer(env, expr)
+ if expr.is_a? IR::Func
+ infer_func(env, expr)
+ elsif expr.is_a? IR::Return
+ infer_return(env, expr)
+ elsif expr.is_a? IR::Const
+ infer_const(env, expr)
+ elsif expr.is_a? IR::Var
+ infer_var(env, expr)
+ elsif expr.is_a? IR::Def
+ infer_def(env, expr)
+ elsif expr.is_a? IR::Set
+ infer_set(env, expr)
+ elsif expr.is_a? IR::Op
+ infer_op(env, expr)
+ elsif expr.is_a? IR::Call
+ infer_call(env, expr)
+ elsif expr.is_a? IR::If
+ infer_if(env, expr)
+ else
+ pp expr
+ error(expr.loc, "unable to determine type of expression")
+ end
+ end
+
+ private
+
+ ##
+ # TYPE INFERENCE
+ ##
+
+ def infer_func(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def infer_return(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def infer_const(env, expr)
+ expr.type
+ end
+
+ def infer_var(env, expr)
+ if env[expr.name].nil?
+ error(expr.loc, "symbol '#{expr.name}' not defined")
+ end
+ expr.type = env[expr.name][:type]
+ end
+
+ def infer_def(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def infer_set(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def infer_op(env, expr)
+ # infer the operand type first
+ vtype = infer(env, expr.left)
+ if (expr.left and expr.right)
+ check_binary(env, expr, vtype)
+ else
+ check_unary(env, expr, vtype)
+ end
+ end
+
+ def infer_call(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def infer_if(env, expr)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ ##
+ # TYPE CHECKING
+ ##
+
+
+ def check_func(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_return(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_const(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+# expr, type.type
+ end
+
+ def check_var(env, expr, type)
+ etype = infer(env, expr)
+ if (etype.class == String)
+ expr.type = type
+ env.set_type(expr.name, type)
+ elsif expr.type != type
+ error(expr.loc, "expected #{type}, received #{etype}")
+ end
+ type
+ end
+
+ def check_def(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_set(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_op(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_call(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+ def check_if(env, expr, type)
+ raise "unimplemented"
+ error(expr.loc, "unimplemented")
+ end
+
+
+
+# def make_typevar()
+# @typevar ||= 0
+# var = "abcdefghijklmnopqrstuvwxyz"[@typevar]
+# @typevar += 1
+# var
+# end
+#
+# def var?(expr)
+# expr.class == IR::Var
+# end
+#
+# def untyped_global_func?(env, func, type)
+# type.nil? and
+# var?(func) and
+# env.global?(func.name) and
+# env[func.name][:value]
+# end
+#
+# def check_apply(env, expr, type)
+# # Handle global functions that haven't been typed yet but are
+# # being called. We pause to infer their type.
+# if untyped_global_func?(env, expr.func, type)
+# value = env[expr.func.name][:value]
+# env[expr.func.name][:value] = nil
+# infer(@parser.syms, value)
+# type = infer(env, expr.func)
+# end
+#
+# error(expr.loc, "object being applied is not a function (has type: #{type.to_s})") if not type.is_a? Array
+# error(expr.loc, "wrong number of arguments to function call") if (type.length - 1) != expr.args.length
+# type[0..-2].each_with_index do |t,i|
+# check(env, expr.args[i], t)
+# end
+# expr.type = type.last
+# end
+#
+# def check_ifexpr(env, expr, type)
+# check(env, expr.cond, :bool)
+# check(env, expr.then, type)
+# check(env, expr.else, type)
+# end
+#
+#
+# def verify(cond, loc, msg)
+# error(loc, msg) if not cond
+# end
+#
+# def check_func(env, expr, type)
+# env = env.clone
+# verify((type.length-1) == expr.args.length, expr.loc,
+# "incorrect number of arguments (#{expr.args.length}, expected #{(type.length-1)}")
+# expr.args.each_with_index do |a,i|
+# env.add_sym(a.name, a.loc, :arg, type[i])
+# end
+# check(env, expr.expr, type.last)
+# end
+#
+# def check_let(env, let, type)
+# env = env.clone
+# env.add_sym(let.var.name, let.var.loc, :var, let.var.type)
+# check(env, let.expr, type)
+# end
+
+#
+# def infer_let(env, let)
+# if let.body.nil?
+# infer_decl(env, let)
+# else
+# infer_let_expr(env, let)
+# end
+# end
+#
+# def infer_decl(env, let)
+# env = env.clone
+# # handle the binding
+# if let.var.type
+# check(env, let.expr, let.var.type)
+# else
+# let.var.type = infer(env, let.expr)
+# end
+# env.set_type(let.var.name, let.var.type)
+# env[let.var.name][:value] = nil
+# let.type = :void
+# end
+#
+# def infer_let_expr(env, let)
+# env = env.clone
+#
+# # handle the binding
+# if let.var.type
+# check(env, let.expr, let.var.type)
+# else
+# let.var.type = infer(env, let.expr)
+# end
+#
+# env.add_sym(let.var.name, let.var.loc, :var, let.var.type)
+# let.type = infer(env, let.body)
+# end
+#
+# def infer_ifexpr(env, expr)
+# check(env, expr, infer(env, expr.then))
+# end
+#
+# def infer_set(env, expr)
+# error(expr.loc, "infer_set unimplemented")
+# end
+#
+# def infer_func(env, expr)
+# env = env.clone
+# @typevar = 0
+# expr.args.each do |a|
+# a.type ||= make_typevar()
+# env.add_sym(a.name, a.loc, :arg, a.type)
+# end
+# infer(env, expr.expr)
+# type = (expr.args + [expr.expr]).map {|v| v.type }
+# type.unshift(:void) if type.length == 1
+#
+# # the body may have inferred an arg type, fix it up here
+# expr.args.each_with_index do |a,i|
+# a.type = env[a.name][:type]
+# type[i] = a.type
+# end
+# expr.type = type
+# end
+#
+# def infer_apply(env, expr)
+# if expr.func.is_a? String
+# expr.type = infer_opcall(env, expr)
+# else
+# type = infer(env, expr.func)
+# check_apply(env, expr, type)
+# end
+# end
+#
+# def assign_type(env, var, type)
+# if var.class == IR::Var and (var.type.nil? or var.type == String) then
+# var.type = type
+# env[var.name][:type] = type
+# end
+# end
+
+
+ def check_unary(env, expr, vtype)
+ optype = UnaryOps[expr.op][TypesToBase[vtype]]
+ error(expr.loc, "unknown unary operation '#{expr.op}' for operand type #{vtype}") if not optype
+ optype = optype.map{|v| BaseToTypes[v] }
+ check(env, expr.left, optype[0])
+ expr.type = optype[-1]
+ end
+
+ def check_binary(env, expr, vtype)
+ pp expr
+ optype = BinaryOps[expr.op][TypesToBase[vtype]]
+ error(expr.loc, "unknown binary operation '#{expr.op}' for operand type #{vtype}") if optype.nil?
+ optype = optype.map{|v| BaseToTypes[v] }
+ check(env, expr.left, optype[0])
+ check(env, expr.right, optype[1])
+ expr.type = optype[-1]
+ end
+end
+
+
+##
+# Code Generation Module
+##
+
+module Codegen
+ State = Struct.new(:label, :locals, :syms, :temp, :indent)
+
+ def self.init(syms)
+ State.new(0, [], syms, 0, 1)
+ end
+
+ def self.putln(state, msg)
+ puts (" " * state.indent) + msg
+ end
+
+ def self.mktemp(state)
+ name = "_t#{state.temp}"
+ state.temp += 1
+ name
+ end
+
+ def self.genlabel(state)
+ label = state.label
+ state.label = state.label + 1
+ end
+
+ def self.emit(state, v)
+ if v.is_a? IR::Return then
+ emit_return(state, v)
+ elsif v.is_a? IR::Const then
+ emit_const(state, v)
+ elsif v.is_a? IR::Op then
+ emit_binop(state, v)
+ elsif v.is_a? IR::Var then
+ emit_var(state, v)
+ elsif v.is_a? IR::Call then
+ emit_call(state, v)
+ elsif v.is_a? IR::Def then
+ emit_def(state, v)
+ elsif v.is_a? IR::Set then
+ emit_set(state, v)
+ elsif v.is_a? IR::Assert then
+ emit_assert(state, v)
+ elsif v.is_a? IR::If then
+ emit_if(state, v)
+ else
+ raise "code emitting of #{v.class} not implemented"
+ end
+ end
+
+ def self.emit_return(state, v)
+ temp = emit(state, v.value)
+ putln state, "return #{temp};"
+ end
+
+ def self.emit_const(state, v)
+ var = mktemp(state)
+ if v.value.is_a? Integer
+ putln state, "Value #{var} = MakeInt(#{v.value});"
+ elsif v.value.is_a? Float
+ putln state, "Value #{var} = MakeReal(#{v.value});"
+ elsif v.value == true || v.value == false
+ putln state, "Value #{var} = MakeBool(#{v.value});"
+ elsif v.value.is_a? String
+ putln state, "Value #{var} = MakeString(#{v.value});"
+ else
+ raise "code emitting constants of this type not implemented"
+ end
+ var
+ end
+
+ def self.emit_binop(state, v)
+ if v.op == "&&"
+ raise "logical and"
+ elsif v.op == "||"
+ raise "logical or"
+ else
+ lvar = emit(state, v.left)
+ rvar = emit(state, v.right)
+ result = mktemp(state);
+ case v.op
+ when "+"
+ putln state, "Value #{result} = OpAdd(#{lvar}, #{rvar});"
+ when "-"
+ putln state, "Value #{result} = OpSub(#{lvar}, #{rvar});"
+ when "*"
+ putln state, "Value #{result} = OpMul(#{lvar}, #{rvar});"
+ when "/"
+ putln state, "Value #{result} = OpDiv(#{lvar}, #{rvar});"
+ when "%"
+ putln state, "Value #{result} = OpMod(#{lvar}, #{rvar});"
+ when "<"
+ putln state, "Value #{result} = OpLt(#{lvar}, #{rvar});"
+ when "<="
+ putln state, "Value #{result} = OpLtEq(#{lvar}, #{rvar});"
+ when ">"
+ putln state, "Value #{result} = OpGt(#{lvar}, #{rvar});"
+ when ">="
+ putln state, "Value #{result} = OpGte(#{lvar}, #{rvar});"
+ when "=="
+ putln state, "Value #{result} = OpEq(#{lvar}, #{rvar});"
+ when "!="
+ putln state, "Value #{result} = OpNeq(#{lvar}, #{rvar});"
+
+ # when "&&"
+ # putln state, "Value #{result} = OpAnd(#{lvar}, #{rvar});"
+ #
+ # when "||"
+ # putln state, "Value #{result} = OpOr(#{lvar}, #{rvar});"
+
+ else
+ raise "not implemented"
+ end
+ end
+ result
+ end
+
+ def self.emit_var(state, v)
+ pp v
+ var = state.syms[v.name]
+ if var.nil?
+ raise "no such variable: #{v.name}"
+ end
+ var
+ end
+
+ def self.emit_call(state, v)
+ result = mktemp(state)
+ vars = v.args.reverse.map do |arg|
+ emit(state, arg)
+ end.join(", ")
+ putln state, "Value #{result} = #{v.func.name}(#{vars});"
+ 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};"
+ end
+
+ def self.emit_set(state, v)
+ 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};"
+ end
+
+ def self.emit_assert(state, v)
+ temp = emit(state, v.value)
+ putln state, "Assert(__FILE__, __LINE__, #{temp});"
+ 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, "}"
+ end
+
+ def self.emit_block(state, v)
+ return if v.nil?
+ state.indent += 1
+ v.each do |v|
+ emit(state, v);
+ end
+ state.indent -= 1
+ end
+end
+
+
+##
+# Emit the Code
+##
+
+$parser = Parser.new("cerise-c.m")
+$checker = TypeChecker.new($parser)
+state = Codegen.init($parser.syms)
+
+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