use ::ast::{self, WithLocation}; use super::environment; use super::types::Ty; #[derive(Debug)] pub enum TypeError { Unimplemented, Mismatch { expected: Ty, actual: Ty, }, UnboundName, } type TypeEnvironment<'a> = environment::Environment<'a, String, Ty>; pub type Result = ::std::result::Result>; pub fn translate(program: &ast::Program) -> Result { let mut env = TypeEnvironment::new(None); trans_exp(&mut env, &*program.0) } macro_rules! err { ($exp: expr, $err: expr) => { Err(WithLocation::new($err, $exp.start, $exp.end)) } } fn trans_exp<'a>(venv: &mut TypeEnvironment<'a>, exp: &WithLocation) -> Result { use ast::Expression::*; match &exp.value { &Let(ref decls, ref body) => { let mut new_env = TypeEnvironment::new(None); for decl in decls.iter() { let decl_ty = trans_exp(venv, &*decl.value.value)?; if let Some(_) = decl.value.type_ { return err!(decl, TypeError::Unimplemented); } else { new_env.add_binding(decl.value.name.value.clone(), decl_ty); } } new_env.set_parent(venv); trans_exp(&mut new_env, &*body) }, &UnaryOp(ref op, ref operand) => { use ast::UnaryOp::*; let operand_ty = trans_exp(venv, operand)?; match op { &Neg | &Pos => { match operand_ty { Ty::Int => { Ok(Ty::Int) } other => { err!(operand, TypeError::Mismatch { expected: Ty::Int, actual: other, }) } } } &Not => { err!(exp, TypeError::Unimplemented) } } }, &BinOp(ref op, ref left, ref right) => { use ast::BinOp::*; let left_ty = trans_exp(venv, left)?; let right_ty = trans_exp(venv, right)?; match op { &Add => { match (left_ty, right_ty) { (Ty::Int, Ty::Int) => { Ok(Ty::Int) } (Ty::String, Ty::String) => { Ok(Ty::String) } (Ty::Int, other) => { err!(right, TypeError::Mismatch { expected: Ty::Int, actual: other, }) } (Ty::String, other) => { err!(right, TypeError::Mismatch { expected: Ty::String, actual: other, }) } _ => { err!(exp, TypeError::Unimplemented) } } } _ => { err!(exp, TypeError::Unimplemented) } } }, &Number(_) => Ok(Ty::Int), &String(_) => Ok(Ty::String), &Name(ref name) => { if let Some(ty) = venv.lookup(name) { Ok(*ty) } else { err!(exp, TypeError::UnboundName) } }, &Nil => { Err(WithLocation::new(TypeError::Unimplemented, exp.start, exp.end)) }, } }