From 9773d40f5c66ff88a0b046aee4058eb24784360b Mon Sep 17 00:00:00 2001 From: Mike Lowis Date: Fri, 18 Oct 2024 12:55:14 -0400 Subject: [PATCH] type checker fleshed out more. test file can compile now. --- cerise-c.m | 5 +++ lib/parser.rb | 15 ++++++--- lib/type_checker.rb | 76 ++++++++++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/cerise-c.m b/cerise-c.m index 40081a6..9727244 100644 --- a/cerise-c.m +++ b/cerise-c.m @@ -182,6 +182,11 @@ TestIfBlocks(){ } } +AddTwoNums(a : int, b : int) +{ + return a + b +} + Main() { TestLiterals() diff --git a/lib/parser.rb b/lib/parser.rb index 2ab631c..6188d99 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -168,9 +168,9 @@ class Parser args = function_arglist() func = IR::Func.new(name.loc, :void, args, [], []) defs = [] -# if accept(":") -# func.type = expect(:ident).text.to_sym -# end + if accept(":") + func.type = type_specifier() + end expect("{") while matches(:def) local = variable_definition() @@ -193,8 +193,9 @@ class Parser expect("(") while !matches(")") id = identifier() -# expect(":") -# id.type = expect(:ident).text.to_sym + if accept(":") + id.type = type_specifier() + end args << id expect(",") if !matches(")") end @@ -207,6 +208,10 @@ class Parser IR::Var.new(name.pos, nil, name.text.to_sym) end + def type_specifier() + expect(:ident).text.to_sym + end + ## # Statements diff --git a/lib/type_checker.rb b/lib/type_checker.rb index a05df20..3915cdd 100644 --- a/lib/type_checker.rb +++ b/lib/type_checker.rb @@ -94,7 +94,7 @@ class TypeChecker @parser = parser env = parser.syms parser.symbols.each do |sym, data| - type = infer(env, env[sym].value) + env[sym].type = infer(env, env[sym].value) end end @@ -160,31 +160,32 @@ class TypeChecker ## def infer_func(env, expr) - env = env.clone @typevar = 0 + env.open_scope() expr.args.each do |a| a.type ||= make_typevar() - env.add_sym(a.name, a.loc, :arg, a.type) + env.add_sym(a.name, a.loc, :arg, a.type, a) end + env.open_scope() expr.body.each do |expr| infer(env, expr) end - -# infer(env, expr.body) type = (expr.args + [expr.body.last]).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 + + env.close_scope() + env.close_scope() expr.type = type end def infer_return(env, expr) - if expr.type + if expr.value expr.type = infer(env, expr.value) else expr.type = :void @@ -219,7 +220,6 @@ class TypeChecker else raise "could not infer const" end - pp "TYPED", expr expr.type end @@ -267,9 +267,13 @@ class TypeChecker def infer_if(env, expr) check(env, expr.cond, :bool) expr.type = :void + env.open_scope() expr.then.each {|e| infer(env, e) } + env.close_scope() + env.open_scope() (expr.else || []).each {|e| infer(env, e) } - end + env.close_scope() + end def infer_assert(env, expr) check(env, expr.value, :bool) @@ -301,7 +305,7 @@ class TypeChecker etype = infer(env, expr) if (etype.class == String) expr.type = type - env.set_type(expr.name, type) + env[expr.name].type = type elsif expr.type != type error(expr.loc, "expected #{type}, received #{etype}") end @@ -317,7 +321,18 @@ class TypeChecker end def check_call(env, expr, type) - pp expr + # 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) + puts "\nUNTYPED FUNC\n\n" + value = env[expr.func.name][:value] + env[expr.func.name][:value] = nil + infer(@parser.syms, value) + env[expr.func.name][:value] = value + env[expr.func.name][:type] = value.type + 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| @@ -333,24 +348,24 @@ class TypeChecker -# 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 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. @@ -468,8 +483,6 @@ class TypeChecker else error(expr.loc, "don't know how to index into type: #{left_type}") end -# pp left_type -# raise "array access" else optype = BinaryOps[expr.op][vtype] error(expr.loc, "unknown binary operation '#{expr.op}' for operand type #{vtype}") if optype.nil? @@ -477,7 +490,6 @@ class TypeChecker check(env, expr.right, optype[1]) expr.type = optype.last end - pp expr expr.type end end -- 2.54.0