use ::ast::{self, WithLocation}; use super::environment; use super::types::{self, 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 venv = TypeEnvironment::new(None); let mut tenv = TypeEnvironment::new(None); tenv.add_binding("int".into(), Ty::Int); tenv.add_binding("string".into(), Ty::String); trans_exp(&mut venv, &mut tenv, &*program.0) } macro_rules! err { ($exp: expr, $err: expr) => { Err(WithLocation::new($err, $exp.start, $exp.end)) } } fn trans_ty<'a>( venv: &mut TypeEnvironment<'a>, tenv: &mut TypeEnvironment<'a>, ty: &WithLocation) -> Result { match ty.value { ast::Ty::Name(ref name) => { tenv.lookup(&name) .map(|v| v.clone()) .ok_or(WithLocation::new(TypeError::UnboundName, ty.start, ty.end)) } ast::Ty::Array(ref inner_ty) => { trans_ty(venv, tenv, &inner_ty) }, ast::Ty::Record(ref fields) => { let mut result = vec![]; for field in fields { result.push(types::RecordField::new( field.name.clone(), trans_ty(venv, tenv, &field.ty)?)); } Ok(Ty::Record(result)) }, } } fn trans_decl<'a>( venv: &mut TypeEnvironment<'a>, tenv: &mut TypeEnvironment<'a>, decl: &WithLocation) -> Result { match decl.declaration { ast::DeclarationBody::Fun { .. } => { err!(decl, TypeError::Unimplemented) } ast::DeclarationBody::Ty { ref ty } => { trans_ty(venv, tenv, ty) } ast::DeclarationBody::Var { ref ty, ref value } => { let actual_ty = trans_exp(venv, tenv, &value)?; if let &Some(ref ty) = ty { let decl_ty = trans_ty(venv, tenv, &ty)?; if decl_ty != actual_ty { return err!(ty, TypeError::Mismatch { expected: decl_ty, actual: actual_ty, }); } } Ok(actual_ty) } } } fn trans_exp<'a>( venv: &mut TypeEnvironment<'a>, tenv: &mut TypeEnvironment<'a>, exp: &WithLocation) -> Result { use ast::Expression::*; match &exp.value { &Let(ref decls, ref body) => { let mut new_venv = TypeEnvironment::new(Some(venv)); let mut new_tenv = TypeEnvironment::new(Some(tenv)); for decl in decls.iter() { let decl_ty = trans_decl(&mut new_venv, &mut new_tenv, decl)?; match decl.declaration { ast::DeclarationBody::Fun { .. } | ast::DeclarationBody::Var { .. } => { new_venv.add_binding(decl.name.clone(), decl_ty); } ast::DeclarationBody::Ty { .. } => { new_tenv.add_binding(decl.name.clone(), decl_ty); } } } trans_exp(&mut new_venv, &mut new_tenv, &*body) }, &UnaryOp(ref op, ref operand) => { use ast::UnaryOp::*; let operand_ty = trans_exp(venv, tenv, 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, tenv, left)?; let right_ty = trans_exp(venv, tenv, 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.clone()) } else { err!(exp, TypeError::UnboundName) } }, &Nil => { Err(WithLocation::new(TypeError::Unimplemented, exp.start, exp.end)) }, } }