diff options
author | David Li <li.davidm96@gmail.com> | 2017-11-06 21:12:56 -0500 |
---|---|---|
committer | David Li <li.davidm96@gmail.com> | 2017-11-06 21:12:56 -0500 |
commit | a90fdb63e8adb09dafcc050344c443d3b73838bc (patch) | |
tree | af62150d119aad693d20c97d8a503cdd6ff34c4c | |
parent | b1ef87bc10b662d6e37f929fa86db7a20b44e7dd (diff) |
Parse and typecheck function calls
-rw-r--r-- | src/ast.rs | 1 | ||||
-rw-r--r-- | src/semantic/translate.rs | 49 | ||||
-rw-r--r-- | src/semantic/types.rs | 1 | ||||
-rw-r--r-- | src/taiga.lalrpop | 17 |
4 files changed, 65 insertions, 3 deletions
@@ -85,6 +85,7 @@ pub enum Expression { Number(u64), String(String), Name(String), + Call(String, Vec<WithLocation<Expression>>), Nil, } diff --git a/src/semantic/translate.rs b/src/semantic/translate.rs index 617c832..a5a9557 100644 --- a/src/semantic/translate.rs +++ b/src/semantic/translate.rs @@ -10,6 +10,10 @@ use super::types::{self, Ty}; #[derive(Debug)] pub enum TypeError { Unimplemented, + LengthMismatch { + expected: usize, + actual: usize, + }, Mismatch { expected: Ty, actual: Ty, @@ -71,9 +75,11 @@ fn trans_decl<'a>( let mut new_venv = TypeEnvironment::new(Some(venv)); let mut new_tenv = TypeEnvironment::new(Some(tenv)); + let mut arg_types = vec![]; for param in params.iter() { - let decl_ty = trans_ty(&mut new_venv, &mut new_tenv, ¶m.ty)?; - new_venv.add_binding(param.name.value.clone(), decl_ty); + let arg_ty = trans_ty(&mut new_venv, &mut new_tenv, ¶m.ty)?; + arg_types.push(arg_ty.clone()); + new_venv.add_binding(param.name.value.clone(), arg_ty); } let body_ty = trans_exp(&mut new_venv, &mut new_tenv, &*body)?; @@ -87,7 +93,7 @@ fn trans_decl<'a>( } } - Ok(body_ty) + Ok(Ty::Function(arg_types, Box::new(body_ty))) } ast::DeclarationBody::Ty { ref ty } => { trans_ty(venv, tenv, ty) @@ -131,6 +137,43 @@ fn trans_exp<'a>( } trans_exp(&mut new_venv, &mut new_tenv, &*body) }, + &Call(ref name, ref args) => { + let mut arg_types = vec![]; + for arg in args { + arg_types.push(trans_exp(venv, tenv, arg)?); + } + + let fun_ty = venv.lookup(name) + .ok_or(WithLocation::new(TypeError::UnboundName, + exp.start, exp.end))?; + match fun_ty { + &Ty::Function(ref expected_args, ref result) => { + if expected_args.len() != arg_types.len() { + return err!(exp, TypeError::LengthMismatch { + expected: expected_args.len(), + actual: arg_types.len(), + }); + } + for (i, (provided_ty, expected_ty)) in + arg_types.iter().zip(expected_args).enumerate() { + if provided_ty != expected_ty { + return err!(args[i], TypeError::Mismatch { + expected: expected_ty.clone(), + actual: provided_ty.clone(), + }) + } + } + Ok((**result).clone()) + }, + otherwise => { + err!(exp, TypeError::Mismatch { + // TODO: better way to handle this + expected: Ty::Function(arg_types, Box::new(Ty::Nil)), + actual: otherwise.clone(), + }) + } + } + }, &UnaryOp(ref op, ref operand) => { use ast::UnaryOp::*; let operand_ty = trans_exp(venv, tenv, operand)?; diff --git a/src/semantic/types.rs b/src/semantic/types.rs index 25d9fb5..928d8c9 100644 --- a/src/semantic/types.rs +++ b/src/semantic/types.rs @@ -10,6 +10,7 @@ pub enum Ty { Nil, Unit, Record(Vec<RecordField>), + Function(Vec<Ty>, Box<Ty>), } #[derive(Clone,Debug,Eq,PartialEq)] diff --git a/src/taiga.lalrpop b/src/taiga.lalrpop index 40830c6..9aa2f38 100644 --- a/src/taiga.lalrpop +++ b/src/taiga.lalrpop @@ -136,10 +136,27 @@ ExpressionSign: Box<ast::WithLocation<ast::Expression>> = { <ExpressionBase> => <>, }; +CallArgsList: Vec<WithLocation<ast::Expression>> = { + <exp: Expression> "," <rest: CallArgsList> => { + let mut rest = rest; + rest.push(*exp); + rest + }, + <exp: Expression> "," => vec![*exp], + <exp: Expression> => vec![*exp], +}; + +CallArgs: Vec<WithLocation<ast::Expression>> = { + "(" <v: CallArgsList> ")" => v, + "(" ")" => vec![], +}; + ExpressionBase: Box<ast::WithLocation<ast::Expression>> = { Num => Box::new(<>.map(|v| ast::Expression::Number(v))), String => Box::new(<>.map(|v| ast::Expression::String(v))), Name => Box::new(<>.map(|v| ast::Expression::Name(v))), + <l: @L> <func: Name> <args: CallArgs> <r: @R> => + Box::new(WithLocation::new(ast::Expression::Call(func.value, args), l, r)), Spanned<r"nil"> => Box::new(<>.map(|v| ast::Expression::Nil)), <l: @L> "let" <d: DeclarationsList> "in" <e: Expression> "end" <r: @R> => Box::new(WithLocation::new(ast::Expression::Let(d, e), l, r)), |