use std::rc::Rc; use std::collections::HashMap; use schala_lang::parsing; use schala_lang::builtin; pub struct TypeContext { bindings: HashMap, Type> } #[derive(Debug, PartialEq, Clone)] pub enum Type { Const(TConst), Func(Box, Box), Void } #[derive(Debug, PartialEq, Clone)] pub enum TConst { Unit, Int, Float, StringT, Bool, Custom(String), } impl parsing::TypeName { fn to_type(&self) -> TypeResult { use self::parsing::TypeSingletonName; use self::parsing::TypeName::*; use self::Type::*; use self::TConst::*; Ok(match self { &Tuple(_) => return Err(format!("Tuples not yet implemented")), &Singleton(ref name) => match name { &TypeSingletonName { ref name, .. } => match &name[..] { "Int" => Const(Int), "Float" => Const(Float), "Bool" => Const(Bool), "String" => Const(StringT), n => Const(Custom(n.to_string())) } } }) } } type TypeResult = Result; impl TypeContext { pub fn new() -> TypeContext { TypeContext { bindings: HashMap::new() } } pub fn type_check_ast(&mut self, ast: &parsing::AST) -> TypeResult { use self::Type::*; use self::TConst::*; let mut ret_type = Const(Unit); for statement in ast.0.iter() { ret_type = self.type_check_statement(statement)?; } Ok(ret_type) } fn type_check_statement(&mut self, statement: &parsing::Statement) -> TypeResult { println!("statement should be: {:?}", statement); use self::parsing::Statement::*; match statement { &ExpressionStatement(ref expr) => self.infer(expr), &Declaration(ref decl) => self.add_declaration(decl), } } fn add_declaration(&mut self, decl: &parsing::Declaration) -> TypeResult { use self::parsing::Declaration::*; use self::Type::*; match decl { &Binding { ref name, ref expr, .. } => { let ty = self.infer(expr)?; self.bindings.insert(name.clone(), ty); }, _ => return Err(format!("other formats not done")) } Ok(Void) } fn infer(&mut self, expr: &parsing::Expression) -> TypeResult { use self::parsing::Expression; match expr { &Expression(ref e, Some(ref anno)) => { let anno_ty = anno.to_type()?; let ty = self.infer_exprtype(&e)?; self.unify(ty, anno_ty) }, &Expression(ref e, None) => self.infer_exprtype(e) } } fn infer_exprtype(&mut self, expr: &parsing::ExpressionType) -> TypeResult { use self::parsing::ExpressionType::*; use self::Type::*; use self::TConst::*; match expr { &IntLiteral(_) => Ok(Const(Int)), &FloatLiteral(_) => Ok(Const(Float)), &StringLiteral(_) => Ok(Const(StringT)), &BoolLiteral(_) => Ok(Const(Bool)), &BinExp(ref op, ref lhs, ref rhs) => { /* remember there are both the haskell convention talk and the write you a haskell ways to do this! */ match self.infer_optype(op)? { Func(box t1, box Func(box t2, box t3)) => { let lhs_ty = self.infer(lhs)?; let rhs_ty = self.infer(rhs)?; self.unify(t1, lhs_ty)?; self.unify(t2, rhs_ty)?; Ok(t3) }, other => return Err(format!("{:?} is not a binary function type", other)) } }, /* PrefixExp(Operation, Box), TupleLiteral(Vec), Value(Rc, Vec<(Rc, Expression)>), Call { f: Box, arguments: Vec, }, Index { indexee: Box, indexers: Vec, }, IfExpression(Box, Vec, Option>), MatchExpression(Box, Vec), ForExpression */ _ => Err(format!("Type not yet implemented")) } } fn infer_optype(&mut self, _op: &builtin::BinOp) -> TypeResult { use self::Type::*; use self::TConst::*; //this is a shim; not all ops are binops from int -> int -> int Ok(Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int)))))) } fn unify(&mut self, t1: Type, t2: Type) -> TypeResult { use self::Type::*;// use self::TConst::*; match (t1, t2) { (Const(ref a), Const(ref b)) if a == b => Ok(Const(a.clone())), (a, b) => Err(format!("Types {:?} and {:?} don't unify", a, b)) } } }