def check(env, expr, type)
if (expr.is_a? ANF::If)
check_ifexpr(env, expr, type)
-# elsif (expr.is_a? ANF::Func)
-# check_func(env, expr, type)
+ elsif (expr.is_a? ANF::Func)
+ check_func(env, expr, type)
elsif (expr.is_a? ANF::Var)
check_var(env, expr, type)
elsif (expr.is_a? ANF::Let)
check_let(env, expr, type)
+# elsif (expr.is_a? ANF::Apply)
+# check_apply(env, expr, type)
else
etype = infer(env, expr)
if type != etype
def check_apply(env, expr, type)
# Handle global functions that haven't been typed yet but are
- # being called. We pause to infer them. This probably causes
- # a loop on recursive functions. Break that later.
+ # 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)
-
- pp "CHECK_APPLY", expr.func.name, type, expr.func, value
end
error(expr.loc, "object being applied is not a function (has type: #{type.to_s})") if not type.is_a? Array
type
end
-# def check_func(env, expr, type)
-# end
+ def verify(cond, loc, msg)
+ error(loc, msg) if not cond
+ end
-# def check_let(env, expr, type)
-# 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_const(env, expr)
expr.type
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
# use the operand type to pick op type and check it
if expr.args.length == 1
- optype = UnaryOps[expr.func][vtype]
- error(expr.loc, "unknown unary operation '#{expr.func}' for operand type #{vtype}") if not optype
- check_apply(env, expr, optype)
+ check_unary(env, expr, vtype)
+
elsif expr.args.length == 2
- infer_binary(env, expr, vtype)
+ check_binary(env, expr, vtype)
else
error(expr.loc, "too many operands for operator '#{expr.func}'")
end
end
- def infer_unary(env, expr, vtype)
+ def check_unary(env, expr, vtype)
+ optype = UnaryOps[expr.func][vtype]
+ error(expr.loc, "unknown unary operation '#{expr.func}' for operand type #{vtype}") if not optype
+ check_apply(env, expr, optype)
end
- def infer_binary(env, expr, vtype)
+ def check_binary(env, expr, vtype)
optype = BinaryOps[expr.func][vtype]
error(expr.loc, "unknown binary operation '#{expr.func}' for operand type #{vtype}") if optype.nil?
end
end
-
class Parser < BaseParser
attr_accessor :exprs
+ def initialize(path = nil)
+ super(nil)
+ syms.add_sym(:void, 0, :type, :void)
+ syms.add_sym(:bool, 0, :type, :bool)
+ syms.add_sym(:int, 0, :type, :int)
+ syms.add_sym(:string, 0, :type, :string)
+ syms.add_sym(:float, 0, :type, :float)
+ parse_file(path)
+ end
+
+ OPERATORS = {
+ "+" => true,
+ "-" => true,
+ "*" => true,
+ "/" => true,
+ "%" => true,
+ }
+
+ def operator?
+ OPERATORS[peek().type]
+ end
+
def toplevel
@type_checker = TypeChecker.new(self)
@exprs = []
@exprs.each do |e|
@type_checker.infer(syms, e)
end
- pp syms
+# pp syms
end
def declaration()
expr = if_expr()
elsif matches(:set)
expr = var_set()
+ elsif matches_any(["+","-", "!"])
+ expr = unop_call()
else
expr = atomic_expr()
if matches("(")
expr = func_call(expr)
elsif operator?
- expr = operator_call(expr)
+ expr = binop_call(expr)
end
end
expr
-
- OPERATORS = {
- "+" => true,
- "-" => true,
- "*" => true,
- "/" => true,
- "%" => true,
- }
-
- def operator?
- OPERATORS[peek().type]
+ def unop_call()
+ op = consume()
+ rhs = atomic_expr()
+ ANF::Apply.new(op.pos, nil, op.type, [rhs])
end
- def operator_call(expr)
+ def binop_call(expr)
op = consume()
rhs = atomic_expr()
ANF::Apply.new(expr.loc, nil, op.type, [expr, rhs])
end
-
-
def if_expr()
loc = expect(:if).pos
cond = atomic_expr()
expect("(")
while !matches(")")
args << ident()
+ args.last.type = type_spec() if accept(":")
expect(",") if not matches(")")
end
expect(")")
+ type = type_spec() if accept(":")
body = expression()
- ANF::Func.new(loc, nil, args, body)
+ ANF::Func.new(loc, type, args, body)
end
def ident()
ANF::Var.new(name.pos, nil, name.text.to_sym)
end
+ def typename()
+ name = expect(:ident)
+ sym = syms[name.text]
+ if sym && sym[:kind] != :type
+ error(name.pos, "#{name.text} is not a valid type")
+ end
+ ANF::Var.new(name.pos, nil, name.text.to_sym)
+ end
+
def constant()
tok = consume()
if tok.type == :bool
def let_expr()
expect(:let)
name = ident()
- type = type_spec() if accept(":")
+ name.type = type_spec() if accept(":")
expect("=")
expr = complex_expr()
expect(:in)
end
def type_spec()
- type = [ident().name]
+ type = [typename().name]
while accept("->")
- type << ident().name
+ type << typename().name
end
(type.length == 1 ? type[0] : type)
end
end
-parser = Parser.new("inputs/cerise.m")
-pp parser.syms
+
+$parser = Parser.new("inputs/cerise.m")
+
+def replace_var(env, expr)
+ if not env.defined? expr.name
+ env[:$free][expr.name] ||= env[:$free].length
+ expr = ANF::EnvRef.new(expr.loc, expr.type, env[:$free][expr.name])
+ end
+ expr
+end
+
+def patch_vars(env, expr, close_funcs = true)
+ if expr.is_a? ANF::Var
+ expr = replace_var(env, expr)
+ elsif expr.is_a? ANF::Let
+ expr.expr = patch_vars(env, expr.expr, close_funcs)
+ env.add_sym(expr.var.name, 0, :var, expr.var.type)
+ expr.body = patch_vars(env, expr.body, close_funcs)
+ elsif expr.is_a? ANF::If
+ expr.cond = patch_vars(env, expr.cond, close_funcs)
+ expr.then = patch_vars(env, expr.then, close_funcs)
+ expr.else = patch_vars(env, expr.else, close_funcs)
+ elsif expr.is_a? ANF::Set
+ expr.else = patch_vars(env, expr.else, close_funcs)
+ elsif expr.is_a? ANF::Func
+ expr = close_func(env, expr, close_funcs)
+ elsif expr.is_a? ANF::Apply
+ expr.func = patch_vars(env, expr.func, close_funcs)
+ expr.args = expr.args.map{|a| patch_vars(env, a, close_funcs) }
+ end
+ expr
+end
+
+$lifted = {}
+
+def close_func(env, func, close_funcs = true)
+ if close_funcs
+ func.type.unshift(:$ENV)
+ func.args.unshift(ANF::Var.new(0, :$ENV, :_env))
+ env = $parser.syms.clone
+ end
+ env[:$free] = {}
+ func.args.each do |a|
+ env.add_sym(a.name, 0, :var, a.type)
+ end
+ func.expr = patch_vars(env, func.expr, true)
+ if close_funcs
+ name = "__func#{$lifted.length}".to_sym
+ $lifted[name] = func
+ func = ANF::Var.new(0, func.type, name)
+ end
+ func
+end
+
+$parser.exprs.each do |e|
+ if e.is_a? ANF::Let and e.expr.is_a? ANF::Func
+ patch_vars($parser.syms.clone, e, false)
+ else
+ patch_vars($parser.syms.clone, e, true)
+ end
+ pp e
+end
+pp $lifted
+#pp parser.exprs[0]
+#pp parser.syms
+#pp parser.exprs