diff --git a/src/semantic/checks/expression.rs b/src/semantic/checks/expression.rs index 7fb4e5e..240e492 100644 --- a/src/semantic/checks/expression.rs +++ b/src/semantic/checks/expression.rs @@ -5,7 +5,7 @@ use crate::{ symbol_table::SymbolTable, types::{Type, Typed}, }, - syntax::ast::Expression, + syntax::ast::{Expression, Positionable}, }; impl SemanticCheck for Expression<'_> { @@ -20,12 +20,13 @@ impl SemanticCheck for Expression<'_> { Type::Function(parameters, _return_type) => { // Check parameters length if parameters.len() != arguments.len() { + let (error_start, error_end) = f.arguments.get_position(); + return Err(MistiError::Semantic(SemanticError { - // TODO: fix - error_start: 0, - error_end: 1, + error_start, + error_end, reason: format!( - "Expected {} arguments, found {}", + "Expected {} arguments, got {}", parameters.len(), arguments.len(), ), @@ -54,10 +55,10 @@ impl SemanticCheck for Expression<'_> { } } _ => { + let (error_start, error_end) = fun.get_position(); return Err(MistiError::Semantic(SemanticError { - // TODO: fix - error_start: 0, - error_end: 1, + error_start, + error_end, reason: format!( "Expected a function type, got {:?}", function_datatype @@ -94,7 +95,11 @@ mod tests { // source code: `print()` let expr_token = Token::new_identifier("print".into(), 0); let expr_function = Expression::Identifier(&expr_token); - let arguments = ArgumentsList { arguments: vec![] }; + let arguments = ArgumentsList { + arguments: vec![], + paren_open_pos: 5, + paren_close_pos: 7, + }; let expr = Expression::FunctionCall(FunctionCall { function: Box::new(expr_function), @@ -128,6 +133,8 @@ mod tests { let arg_1 = Expression::Int(&arg_t); let arguments = ArgumentsList { arguments: vec![arg_1], + paren_open_pos: 5, + paren_close_pos: 10, }; let expr = Expression::FunctionCall(FunctionCall { @@ -145,4 +152,72 @@ mod tests { Err(e) => panic!("Expected semantic error, got {:?}", e), } } + + #[test] + fn should_error_on_invalid_function_argument_count() { + // source code: `print()` + let mut scope = SymbolTable::new(); + populate(&mut scope); + + let expr_token = Token::new_identifier("print".into(), 0); + let expr_function = Expression::Identifier(&expr_token); + + let arguments = ArgumentsList { + arguments: vec![], + paren_open_pos: 5, + paren_close_pos: 7, + }; + + let expr = Expression::FunctionCall(FunctionCall { + function: Box::new(expr_function), + arguments: Box::new(arguments), + }); + + match expr.check_semantics(&scope) { + Ok(_) => panic!("Expected semantic error, got ok"), + Err(MistiError::Semantic(e)) => { + assert_eq!(e.reason, "Expected 1 arguments, got 0"); + assert_eq!(e.error_start, 5); + assert_eq!(e.error_end, 7); + } + Err(e) => panic!("Expected semantic error, got {:?}", e), + } + } + + #[test] + fn should_error_on_invalid_function_argument_2() { + // source code: `print(322, 644)` + let mut scope = SymbolTable::new(); + populate(&mut scope); + + let expr_token = Token::new_identifier("print".into(), 0); + let expr_function = Expression::Identifier(&expr_token); + + let arg_t = Token::new_int(String::from("322"), 6); + let arg_1 = Expression::Int(&arg_t); + + let arg_t_2 = Token::new_int(String::from("644"), 11); + let arg_2 = Expression::Int(&arg_t_2); + + let arguments = ArgumentsList { + arguments: vec![arg_1, arg_2], + paren_open_pos: 5, + paren_close_pos: 15, + }; + + let expr = Expression::FunctionCall(FunctionCall { + function: Box::new(expr_function), + arguments: Box::new(arguments), + }); + + match expr.check_semantics(&scope) { + Ok(_) => panic!("Expected semantic error, got ok"), + Err(MistiError::Semantic(e)) => { + assert_eq!(e.reason, "Expected 1 arguments, got 2"); + assert_eq!(e.error_start, 5); + assert_eq!(e.error_end, 15); + } + Err(e) => panic!("Expected semantic error, got {:?}", e), + } + } } diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index 69a029e..f9652c6 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -185,7 +185,11 @@ mod tests { let id_token = Token::new_identifier("print".into(), 0); let fn_expr = Expression::Identifier(&id_token); - let args = ArgumentsList { arguments: vec![] }; + let args = ArgumentsList { + arguments: vec![], + paren_open_pos: 5, + paren_close_pos: 7, + }; let fn_call = Expression::FunctionCall(FunctionCall { function: Box::new(fn_expr), @@ -208,7 +212,11 @@ mod tests { let id_token = Token::new_identifier("print".into(), 0); let fn_expr = Expression::Identifier(&id_token); - let args = ArgumentsList { arguments: vec![] }; + let args = ArgumentsList { + arguments: vec![], + paren_open_pos: 5, + paren_close_pos: 7, + }; let fn_call = Expression::FunctionCall(FunctionCall { function: Box::new(fn_expr), @@ -233,7 +241,11 @@ mod tests { let id_token = Token::new_identifier("print".into(), 0); let fn_expr = Expression::Identifier(&id_token); - let args = ArgumentsList { arguments: vec![] }; + let args = ArgumentsList { + arguments: vec![], + paren_open_pos: 5, + paren_close_pos: 7, + }; let fn_call = Expression::FunctionCall(FunctionCall { function: Box::new(fn_expr), diff --git a/src/syntax/ast/functions.rs b/src/syntax/ast/functions.rs index 9eaf229..bfd9c66 100644 --- a/src/syntax/ast/functions.rs +++ b/src/syntax/ast/functions.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Positionable}; #[derive(Debug)] pub struct FunctionCall<'a> { @@ -9,4 +9,13 @@ pub struct FunctionCall<'a> { #[derive(Debug)] pub struct ArgumentsList<'a> { pub arguments: Vec>, + pub paren_open_pos: usize, + /// This is after the paren is closed + pub paren_close_pos: usize, +} + +impl Positionable for ArgumentsList<'_> { + fn get_position(&self) -> (usize, usize) { + (self.paren_open_pos, self.paren_close_pos) + } } diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index 1bdc765..40dc241 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -6,6 +6,12 @@ use var_binding::VariableBinding; pub mod functions; pub mod var_binding; +/// Trait that allows nodes to inform +/// on where they start and end on the source code +pub trait Positionable { + fn get_position(&self) -> (usize, usize); +} + /// The AST for a whole THP file #[derive(Debug)] pub struct ModuleAST<'a> { @@ -70,19 +76,19 @@ pub enum Expression<'a> { BinaryOperator(Box>, Box>, &'a String), } -impl Expression<'_> { +impl Positionable for Expression<'_> { /// Returns the absolute start and end position /// of this expression - pub fn get_position(&self) -> (usize, usize) { + fn get_position(&self) -> (usize, usize) { match self { Expression::Identifier(id) => (id.position, id.get_end_position()), Expression::Int(id) => (id.position, id.get_end_position()), Expression::Float(id) => (id.position, id.get_end_position()), Expression::String(id) => (id.position, id.get_end_position()), Expression::Boolean(id) => (id.position, id.get_end_position()), - Expression::FunctionCall(_) => todo!(), - Expression::UnaryOperator(_, _) => todo!(), - Expression::BinaryOperator(_, _, _) => todo!(), + Expression::FunctionCall(_) => (0, 1), + Expression::UnaryOperator(_, _) => (0, 1), + Expression::BinaryOperator(_, _, _) => (0, 1), } } } diff --git a/src/syntax/functions/arguments_list.rs b/src/syntax/functions/arguments_list.rs index 28d051a..692e21e 100644 --- a/src/syntax/functions/arguments_list.rs +++ b/src/syntax/functions/arguments_list.rs @@ -53,7 +53,7 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParsingResult (t, next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), @@ -74,7 +74,14 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParsingResult