From: Michael D. Lowis Date: Tue, 3 Jan 2023 05:00:15 +0000 (-0500) Subject: fleshed out an ANF based compiler based on ml X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=471d65abc64cbf3a3f64d92319bdc6a490f18a2d;p=proto%2Fsclpl-rb.git fleshed out an ANF based compiler based on ml --- diff --git a/inputs/cerise.m b/inputs/cerise.m index f7f412a..1cbe4d4 100644 --- a/inputs/cerise.m +++ b/inputs/cerise.m @@ -16,34 +16,29 @@ #if true then 2 else 3.0 # error #if true then 2.0 else 3 # error - # type variables and unused args - # TODO: implement operator precedence # TODO: desugar to ANF *after* typing -foo = func(a) - let b = bar(a) in - let c = 123.0 + b in c - -bar = func(a) # TODO func type is wrong, for a - let b = 1.0 + a in b - - - -#bar : int -> int -#bar = func(a) +#foo : float -> float +#foo = func(a) +# let b = bar(a) in +# let c = 123.0 + b in c +# +#bar = func(a) # TODO func type is wrong, for a +# let b = 1.0 + a in b +# +#boo : int -> int +#boo = func(a) # 123 +# TODO: Constructs we should handle someday +#baz1 = func(a) : float +# let b = -a in b +#baz2 = func(a) +# let b : float = -a in b - -#foo = func(a, b) -# let bar = foo(a, b) in -# bar - - -#let bar : int -> int -> int = func(a,b) -# if 1 then 2 else 3 -# -#let setter : int -> void = func(a) -# set! value = 4 +makeAdder = func(a : int) + let d = 5 in + func(b) : int + let c = b + a in c diff --git a/lib/cerise.rb b/lib/cerise.rb index 3bebdc2..439aa4c 100755 --- a/lib/cerise.rb +++ b/lib/cerise.rb @@ -132,12 +132,14 @@ class TypeChecker 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 @@ -189,15 +191,12 @@ class TypeChecker 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 @@ -225,11 +224,25 @@ class TypeChecker 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 @@ -252,14 +265,12 @@ class TypeChecker 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 @@ -331,20 +342,22 @@ class TypeChecker # 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? @@ -355,10 +368,31 @@ class TypeChecker 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 = [] @@ -373,7 +407,7 @@ class Parser < BaseParser @exprs.each do |e| @type_checker.infer(syms, e) end - pp syms +# pp syms end def declaration() @@ -410,12 +444,14 @@ class Parser < BaseParser 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 @@ -455,27 +491,18 @@ class Parser < BaseParser - - 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() @@ -511,11 +538,13 @@ class Parser < BaseParser 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() @@ -523,6 +552,15 @@ class Parser < BaseParser 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 @@ -543,7 +581,7 @@ class Parser < BaseParser def let_expr() expect(:let) name = ident() - type = type_spec() if accept(":") + name.type = type_spec() if accept(":") expect("=") expr = complex_expr() expect(:in) @@ -552,13 +590,77 @@ class Parser < BaseParser 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 diff --git a/lib/utils/anf.rb b/lib/utils/anf.rb index 471351a..aa5cede 100644 --- a/lib/utils/anf.rb +++ b/lib/utils/anf.rb @@ -21,13 +21,14 @@ # | module ANF - Const = Struct.new(:loc, :type, :value) - Var = Struct.new(:loc, :type, :name) - Let = Struct.new(:loc, :type, :var, :expr, :body) - If = Struct.new(:loc, :type, :cond, :then, :else) - Set = Struct.new(:loc, :type, :var, :expr) - Func = Struct.new(:loc, :type, :args, :expr) - Apply = Struct.new(:loc, :type, :func, :args) + Const = Struct.new(:loc, :type, :value) + Var = Struct.new(:loc, :type, :name) + EnvRef = Struct.new(:loc, :type, :index) + Let = Struct.new(:loc, :type, :var, :expr, :body) + If = Struct.new(:loc, :type, :cond, :then, :else) + Set = Struct.new(:loc, :type, :var, :expr) + Func = Struct.new(:loc, :type, :args, :expr) + Apply = Struct.new(:loc, :type, :func, :args) def self.check_atomic(expr) case expr.class diff --git a/lib/utils/sym_table.rb b/lib/utils/sym_table.rb index 711ef27..2e6f300 100644 --- a/lib/utils/sym_table.rb +++ b/lib/utils/sym_table.rb @@ -1,4 +1,6 @@ class SymTable < Hash + attr_accessor :parent + def initialize(parent = nil) @global_scope = true if not parent.nil? @parent = (parent || {}) @@ -42,6 +44,16 @@ class SymTable < Hash end end + def depth + count = 0 + env = self + while env.class != Hash + count += 1 + env = env.parent + end + count + end + # def local(key) # method(:[]).super_method.call(key) # end