diff --git a/src/php_ast/transformers/module_ast.rs b/src/php_ast/transformers/module_ast.rs index 7fcfed7..9de64b8 100644 --- a/src/php_ast/transformers/module_ast.rs +++ b/src/php_ast/transformers/module_ast.rs @@ -25,7 +25,7 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> { // TODO: This definitely needs refactoring let function_expr: &Expression = &*fc.function; match function_expr { - Expression::Identifier(id) if *id == "print" => { + Expression::Identifier(id) if id.value == "print" => { // transform to print() expression // no parameters supported diff --git a/src/semantic/checks/expression.rs b/src/semantic/checks/expression.rs new file mode 100644 index 0000000..04a89b0 --- /dev/null +++ b/src/semantic/checks/expression.rs @@ -0,0 +1,106 @@ +use crate::{ + error_handling::{semantic_error::SemanticError, MistiError}, + semantic::{impls::SemanticCheck, symbol_table::SymbolTable, types::{Type, Typed}}, + syntax::ast::Expression, +}; + +impl SemanticCheck for Expression<'_> { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { + match self { + Expression::FunctionCall(f) => { + let fun = &*f.function; + let arguments = &*f.arguments.arguments; + + let function_datatype = fun.get_type(scope)?; + match function_datatype { + Type::Function(parameters, _return_type) => { + // Check parameters length + if parameters.len() != arguments.len() { + return Err(MistiError::Semantic(SemanticError { + // TODO: fix + error_start: 0, + error_end: 1, + reason: format!( + "Expected {} arguments, found {}", + parameters.len(), + arguments.len(), + ), + })); + } + + // Check that each argument matches the required datatype + for i in 0..parameters.len() { + let parameter = ¶meters[i]; + let argument = &arguments[i]; + + let argument_datatype = argument.get_type(scope)?; + if !argument_datatype.is_value(parameter) { + // The argument and the parameter have diferent types + return Err(MistiError::Semantic(SemanticError { + // TODO: fix + error_start: 0, + error_end: 1, + reason: format!( + "Expected datatype {}, got {:?}", + parameter, argument + ), + })); + } + } + } + _ => { + return Err(MistiError::Semantic(SemanticError { + // TODO: fix + error_start: 0, + error_end: 1, + reason: format!( + "Expected a function type, got {:?}", + function_datatype + ), + })); + } + } + } + Expression::Int(_) => {} + Expression::Float(_) => {} + Expression::String(_) => {} + Expression::Boolean(_) => {} + _ => todo!("Check semantics for expression other than function call and primitive"), + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::{error_handling::MistiError, lexic::token::Token, semantic::{impls::SemanticCheck, symbol_table::SymbolTable}, syntax::ast::{functions::{ArgumentsList, FunctionCall}, Expression}}; + + #[test] + fn should_error() { + // 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 expr = Expression::FunctionCall(FunctionCall { + function: Box::new(expr_function), + arguments: Box::new(arguments), + }); + + let scope = SymbolTable::new(); + + let output = expr.check_semantics(&scope); + match output { + Ok(_) => panic!("Expected an error"), + Err(MistiError::Semantic(err)) => { + assert_eq!(err.reason, "Cannot find `print` in this scope."); + assert_eq!(err.error_start, 0); + assert_eq!(err.error_end, 5); + }, + Err(e) => panic!("Expected a Semantic error, got {:?}", e) + } + } +} diff --git a/src/semantic/checks/mod.rs b/src/semantic/checks/mod.rs index 02de27b..4bf25fd 100644 --- a/src/semantic/checks/mod.rs +++ b/src/semantic/checks/mod.rs @@ -1,3 +1,5 @@ pub mod binding; pub mod function_declaration; pub mod top_level_declaration; +pub mod expression; + diff --git a/src/semantic/checks/top_level_declaration.rs b/src/semantic/checks/top_level_declaration.rs index e649ac5..1d04f84 100644 --- a/src/semantic/checks/top_level_declaration.rs +++ b/src/semantic/checks/top_level_declaration.rs @@ -29,72 +29,3 @@ impl SemanticCheck for Statement<'_> { } } } - -// TODO: Move to its own file when it grows -impl SemanticCheck for Expression<'_> { - fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { - match self { - Expression::FunctionCall(f) => { - let fun = &*f.function; - let arguments = &*f.arguments.arguments; - - let function_datatype = fun.get_type(scope)?; - match function_datatype { - Type::Function(parameters, _return_type) => { - // Check parameters length - if parameters.len() != arguments.len() { - return Err(MistiError::Semantic(SemanticError { - // TODO: fix - error_start: 0, - error_end: 1, - reason: format!( - "Expected {} arguments, found {}", - parameters.len(), - arguments.len(), - ), - })); - } - - // Check that each argument matches the required datatype - for i in 0..parameters.len() { - let parameter = ¶meters[i]; - let argument = &arguments[i]; - - let argument_datatype = argument.get_type(scope)?; - if !argument_datatype.is_value(parameter) { - // The argument and the parameter have diferent types - return Err(MistiError::Semantic(SemanticError { - // TODO: fix - error_start: 0, - error_end: 1, - reason: format!( - "Expected datatype {}, got {:?}", - parameter, argument - ), - })); - } - } - } - _ => { - return Err(MistiError::Semantic(SemanticError { - // TODO: fix - error_start: 0, - error_end: 1, - reason: format!( - "Expected a function type, got {:?}", - function_datatype - ), - })); - } - } - } - Expression::Int(_) => {} - Expression::Float(_) => {} - Expression::String(_) => {} - Expression::Boolean(_) => {} - _ => todo!("Check semantics for expression other than function call and primitive"), - } - - Ok(()) - } -} diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index 1355541..a89a7a9 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -16,13 +16,13 @@ impl Typed for Expression<'_> { Expression::Boolean(_) => Ok(Type::Value("Bool".into())), Expression::Identifier(identifier) => { // Attempt to get the datatype of the identifier in the current scope - let datatype = match scope.get_type(identifier) { + let datatype = match scope.get_type(&identifier.value) { Some(x) => x, None => { return Err(MistiError::Semantic(SemanticError { - error_start: 0, - error_end: 1, - reason: format!("The identifier {} does not exist.", identifier), + error_start: identifier.position, + error_end: identifier.get_end_position(), + reason: format!("Cannot find `{}` in this scope.", identifier.value), })) } }; @@ -37,7 +37,7 @@ impl Typed for Expression<'_> { match &*f.function { Expression::Identifier(id) => { - match scope.get_type(id) { + match scope.get_type(&id.value) { Some(t) => Ok(t), None => Err(MistiError::Semantic(SemanticError { // TODO: Actually find the start and end position @@ -45,7 +45,7 @@ impl Typed for Expression<'_> { // just the string value error_start: 0, error_end: 1, - reason: format!("Type not found for symbol {}", id), + reason: format!("Type not found for symbol {}", id.value), })), } } @@ -114,3 +114,12 @@ impl Typed for Expression<'_> { } } } + + +#[cfg(test)] +mod tests { + #[test] + fn should_error() { + + } +} diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index a8a67a5..706e53e 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -64,7 +64,7 @@ pub enum Expression<'a> { Float(&'a String), String(&'a String), Boolean(bool), - Identifier(&'a String), + Identifier(&'a Token), FunctionCall(FunctionCall<'a>), UnaryOperator(&'a String, Box>), BinaryOperator(Box>, Box>, &'a String), diff --git a/src/syntax/parsers/expression/comparison.rs b/src/syntax/parsers/expression/comparison.rs index f1101e3..c59c224 100644 --- a/src/syntax/parsers/expression/comparison.rs +++ b/src/syntax/parsers/expression/comparison.rs @@ -62,8 +62,8 @@ mod tests { Expression::BinaryOperator(exp1, exp2, op) => { match (*exp1, *exp2) { (Expression::Identifier(id1), Expression::Identifier(id2)) => { - assert_eq!("a", id1); - assert_eq!("b", id2); + assert_eq!("a", id1.value); + assert_eq!("b", id2.value); } _ => panic!("Expected 2 identifiers"), } diff --git a/src/syntax/parsers/expression/equality.rs b/src/syntax/parsers/expression/equality.rs index 8c81f7d..3497d03 100644 --- a/src/syntax/parsers/expression/equality.rs +++ b/src/syntax/parsers/expression/equality.rs @@ -61,8 +61,8 @@ mod tests { Expression::BinaryOperator(exp1, exp2, op) => { match (*exp1, *exp2) { (Expression::Identifier(id1), Expression::Identifier(id2)) => { - assert_eq!("a", id1); - assert_eq!("b", id2); + assert_eq!("a", id1.value); + assert_eq!("b", id2.value); } _ => panic!("Expected 2 identifiers"), } diff --git a/src/syntax/parsers/expression/factor.rs b/src/syntax/parsers/expression/factor.rs index ca2f18d..589d9b4 100644 --- a/src/syntax/parsers/expression/factor.rs +++ b/src/syntax/parsers/expression/factor.rs @@ -65,8 +65,8 @@ mod tests { Expression::BinaryOperator(exp1, exp2, op) => { match (*exp1, *exp2) { (Expression::Identifier(id1), Expression::Identifier(id2)) => { - assert_eq!("a", id1); - assert_eq!("b", id2); + assert_eq!("a", id1.value); + assert_eq!("b", id2.value); } _ => panic!("Expected 2 identifiers"), } diff --git a/src/syntax/parsers/expression/primary.rs b/src/syntax/parsers/expression/primary.rs index 0063564..fb6c3d8 100644 --- a/src/syntax/parsers/expression/primary.rs +++ b/src/syntax/parsers/expression/primary.rs @@ -19,7 +19,7 @@ pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult { TokenType::Identifier if token.value == "true" || token.value == "false" => { Ok((Expression::Boolean(token.value == "true"), token_pos + 1)) } - TokenType::Identifier => Ok((Expression::Identifier(&token.value), token_pos + 1)), + TokenType::Identifier => Ok((Expression::Identifier(&token), token_pos + 1)), TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos), _ => Err(ParsingError::Unmatched), }, @@ -90,7 +90,7 @@ mod tests { match expression { Ok((Expression::Identifier(value), _)) => { - assert_eq!("someIdentifier", format!("{}", value)) + assert_eq!("someIdentifier", format!("{}", value.value)) } _ => panic!(), } @@ -103,7 +103,7 @@ mod tests { match expression { Ok((Expression::Identifier(value), _)) => { - assert_eq!("identifier", format!("{}", value)) + assert_eq!("identifier", format!("{}", value.value)) } _ => panic!(), } diff --git a/src/syntax/parsers/expression/term.rs b/src/syntax/parsers/expression/term.rs index c36fc22..e91a216 100644 --- a/src/syntax/parsers/expression/term.rs +++ b/src/syntax/parsers/expression/term.rs @@ -66,8 +66,8 @@ mod tests { Expression::BinaryOperator(exp1, exp2, op) => { match (*exp1, *exp2) { (Expression::Identifier(id1), Expression::Identifier(id2)) => { - assert_eq!("a", id1); - assert_eq!("b", id2); + assert_eq!("a", id1.value); + assert_eq!("b", id2.value); } _ => panic!("Expected 2 identifiers"), } diff --git a/src/syntax/parsers/expression/unary.rs b/src/syntax/parsers/expression/unary.rs index 90bf2b7..f496f2e 100644 --- a/src/syntax/parsers/expression/unary.rs +++ b/src/syntax/parsers/expression/unary.rs @@ -38,7 +38,7 @@ mod tests { match expression { Ok((Expression::Identifier(value), _)) => { - assert_eq!("identifier", format!("{}", value)) + assert_eq!("identifier", format!("{}", value.value)) } _ => panic!(), }