From e501f4bd104e19144822401d00c61edc50e61b45 Mon Sep 17 00:00:00 2001 From: greg Date: Sat, 23 Feb 2019 09:58:44 -0800 Subject: [PATCH] Various cleanup of comments, stringifying types --- schala-lang/language/src/typechecking.rs | 69 ++++++++++++++++-------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/schala-lang/language/src/typechecking.rs b/schala-lang/language/src/typechecking.rs index 7d17108..266dce1 100644 --- a/schala-lang/language/src/typechecking.rs +++ b/schala-lang/language/src/typechecking.rs @@ -1,8 +1,5 @@ use std::rc::Rc; -//THINGS TODO -// look at the haskell compiler, see where in its flow the typechecking happens -// -nope, ghc deliberately does typechecking before desugaring to core -// cf. a history of haskell, peyton-jones +use std::fmt::Write; use ena::unify::{UnifyKey, InPlaceUnificationTable, UnificationTable, EqUnifyValue}; @@ -10,9 +7,6 @@ use crate::ast::*; use crate::util::ScopeStack; use crate::builtin::{PrefixOp, BinOp}; -use std::collections::HashMap; -use std::hash::Hash; - #[derive(Debug, Clone, PartialEq)] pub struct TypeData { @@ -40,7 +34,7 @@ type InferResult = Result; pub struct TypeError { pub msg: String } impl TypeError { - fn new(msg: T) -> InferResult where T: Into { //TODO make these kinds of error-producing functions CoW-ready + fn new(msg: T) -> InferResult where T: Into { Err(TypeError { msg: msg.into() }) } } @@ -81,28 +75,56 @@ pub enum TypeConst { UserDefined } +impl TypeConst { + pub fn to_string(&self) -> String { + use self::TypeConst::*; + match self { + Unit => format!("()"), + Nat => format!("Nat"), + Int => format!("Int"), + Float => format!("Float"), + StringT => format!("String"), + Bool => format!("Bool"), + Ordering => format!("Ordering"), + _ => format!("UNKNOWN TYPE"), + } + } +} + impl EqUnifyValue for TypeConst { } macro_rules! ty { ($type_name:ident) => { Type::Const(TypeConst::$type_name) }; ($t1:ident -> $t2:ident) => { Type::Arrow { params: vec![ty!($t1)], ret: box ty!($t2) } }; ($t1:ident -> $t2:ident -> $t3:ident) => { Type::Arrow { params: vec![ty!($t1), ty!($t2)], ret: box ty!($t3) } }; + ($type_list:ident, $ret_type:ident) => { + Type::Arrow { + params: $type_list, + ret: box $ret_type, + } + } } //TODO find a better way to capture the to/from string logic impl Type { pub fn to_string(&self) -> String { use self::Type::*; - use self::TypeConst::*; match self { - Const(Unit) => format!("()"), - Const(Nat) => format!("Nat"), - Const(Int) => format!("Int"), - Const(Float) => format!("Float"), - Const(StringT) => format!("String"), - Const(Bool) => format!("Bool"), - Const(Ordering) => format!("Ordering"), - _ => format!("UNKNOWN TYPE"), + Const(c) => c.to_string(), + Var(v) => format!("t_{}", v.0), + Arrow { params, box ref ret } => { + if params.len() == 0 { + format!("-> {}", ret.to_string()) + } else { + let mut buf = String::new(); + for p in params.iter() { + write!(buf, "{} -> ", p.to_string()); + } + write!(buf, "{}", ret.to_string()); + buf + } + }, + Compound { .. } => format!("") } } @@ -228,6 +250,9 @@ impl<'a> TypeContext<'a> { } + /// `typecheck` is the entry into the type-inference system, accepting an AST as an argument + /// Following the example of GHC, the compiler deliberately does typechecking before de-sugaring + /// the AST to ReducedAST pub fn typecheck(&mut self, ast: &AST) -> Result { let mut returned_type = Type::Const(TypeConst::Unit); for statement in ast.0.iter() { @@ -334,11 +359,12 @@ impl<'a> TypeContext<'a> { } }).collect(); let argument_types = argument_types?; + let ret_type = match type_anno.as_ref() { + Some(anno) => self.get_type_from_name(anno)?, + None => Type::Var(self.fresh_type_variable()) + }; - println!("ARGUMENT TYPES: {:?}", argument_types); - - //TODO finish this - Ok(ty!(UserDefined)) + Ok(ty!(argument_types, ret_type)) } fn call(&mut self, f: &Expression, args: &Vec>) -> InferResult { @@ -379,7 +405,6 @@ impl<'a> TypeContext<'a> { fn unify(&mut self, t1: Type, t2: Type) -> InferResult { use self::Type::*; - use std::collections::hash_map::Entry; match (t1, t2) { (Const(ref c1), Const(ref c2)) if c1 == c2 => Ok(Const(c1.clone())), //choice of c1 is arbitrary I *think*