diff --git a/Cargo.toml b/Cargo.toml index 389c203..9dec5dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "semantic-analyzer" -version = "0.2.3" +version = "0.2.4" authors = ["Evgeny Ukhanov "] description = "Semantic analyzer library for compilers written in Rust for semantic analysis of programming languages AST" keywords = ["compiler", "semantic-analisis", "semantic-alalyzer", "compiler-design", "semantic"] diff --git a/src/ast.rs b/src/ast.rs index 7e3aee3..81f5da3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -642,7 +642,8 @@ pub struct IfStatement<'a> { impl GetLocation for IfStatement<'_> { fn location(&self) -> CodeLocation { - todo!() + // TODO + CodeLocation::new(1, 0) } } diff --git a/src/semantic.rs b/src/semantic.rs index 7ba0747..eb900bf 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -238,7 +238,7 @@ impl State { self.global.context.constant(const_val); } - /// Function declaration analyze. Add it to Global State/ + /// Function declaration analyze. Add it to Global State/M pub fn function_declaration(&mut self, data: &ast::FunctionStatement<'_>) { if self.global.functions.contains_key(&data.name().into()) { self.add_error(error::StateErrorResult::new( @@ -309,8 +309,16 @@ impl State { ast::BodyStatement::Expression(expression) | ast::BodyStatement::Return(expression) => { let expr_result = self.expression(expression, &body_state); + let expr: Expression = expression.clone().into(); + // Check is return statement previously called + if return_is_called { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ReturnAlreadyCalled, + expr.to_string(), + expression.location(), + )); + } if let Some(res) = expr_result { - let expr: Expression = expression.clone().into(); // Check expression type and do not exist from flow self.check_type_exists(&res.expr_type, &expr, &expression.clone()); let fn_ty: Type = data.result_type.clone().into(); @@ -466,7 +474,6 @@ impl State { )); return; } - function_state .borrow_mut() .context @@ -591,10 +598,13 @@ impl State { /// # If-condition body /// Analyze body for ant if condition: /// - if, else, if-else + /// NOTE: label_end - is always already exists pub fn if_condition_body( &mut self, body: &[ast::IfBodyStatement<'_>], if_body_state: &Rc>, + label_end: &LabelName, + label_loop: Option<(&LabelName, &LabelName)>, ) { for body in body.iter() { match body { @@ -608,7 +618,12 @@ impl State { self.function_call(fn_call, if_body_state); } ast::IfBodyStatement::If(if_condition) => { - self.if_condition(if_condition, if_body_state, None, None); + self.if_condition( + if_condition, + if_body_state, + Some(label_end.clone()), + label_loop, + ); } ast::IfBodyStatement::Loop(loop_statement) => { self.loop_statement(loop_statement, if_body_state); @@ -634,6 +649,7 @@ impl State { &mut self, body: &[ast::IfLoopBodyStatement<'_>], if_body_state: &Rc>, + label_if_end: &LabelName, label_loop_start: &LabelName, label_loop_end: &LabelName, ) { @@ -652,7 +668,7 @@ impl State { self.if_condition( if_condition, if_body_state, - None, + Some(label_if_end.clone()), Some((label_loop_start, label_loop_end)), ); } @@ -767,7 +783,7 @@ impl State { label_loop: Option<(&LabelName, &LabelName)>, ) { // It can't contain `else` and `if-else` on the same time - if data.else_if_statement.is_some() && data.else_if_statement.is_some() { + if data.else_statement.is_some() && data.else_if_statement.is_some() { let stm = data.else_if_statement.clone().unwrap(); self.add_error(error::StateErrorResult::new( error::StateErrorKind::IfElseDuplicated, @@ -784,7 +800,6 @@ impl State { function_body_state .borrow_mut() .set_child(if_body_state.clone()); - // Get labels name for if-begin, and if-end let label_if_begin = if_body_state .borrow_mut() @@ -792,16 +807,18 @@ impl State { let label_if_else = if_body_state .borrow_mut() .get_and_set_next_label(&"if_else".to_string().into()); - // Set if-end label from previous context, if exist - let label_if_end = label_end.map_or_else( + // Set if-end label from previous context + let label_if_end = label_end.clone().map_or_else( || { if_body_state .borrow_mut() - .get_and_set_next_label(&"if_begin".to_string().into()) + .get_and_set_next_label(&"if_end".to_string().into()) }, |label| label, ); - let is_else = data.else_if_statement.is_some() || data.else_if_statement.is_some(); + // To set if-end as single return point check is it previously set + let is_set_label_if_end = label_end.clone().is_some(); + let is_else = data.else_statement.is_some() || data.else_if_statement.is_some(); // Analyse if-conditions self.if_condition_calculation( @@ -820,15 +837,25 @@ impl State { match &data.body { ast::IfBodyStatements::If(body) => { // Analyze if-statement body - self.if_condition_body(body, &if_body_state); + self.if_condition_body(body, &if_body_state, &label_if_end, label_loop); } ast::IfBodyStatements::Loop(body) => { - let (label_loop_start, label_loop_end) = label_loop.expect("label should be set"); + // It's special case for the Loop, when `label_loop` should always be set. + // If it doesn't set, it's unexpected behavior and program algorithm error + let (label_loop_start, label_loop_end) = + label_loop.expect("loop label should be set"); // Analyze if-loop-statement body - self.if_condition_loop_body(body, &if_body_state, label_loop_start, label_loop_end); + self.if_condition_loop_body( + body, + &if_body_state, + &label_if_end, + label_loop_start, + label_loop_end, + ); } } - // Codegen for jump to if-end statement -return to program flow + // Codegen for jump to if-end statement - return to program flow + // TODO: issue #9 if_body_state .borrow_mut() .context @@ -838,19 +865,26 @@ impl State { if is_else { // Set if-else label if_body_state.borrow_mut().context.set_label(label_if_else); - // if-else has own state, different from if-state - let if_else_body_state = Rc::new(RefCell::new(BlockState::new(Some( - function_body_state.clone(), - )))); - function_body_state - .borrow_mut() - .set_child(if_else_body_state.clone()); + // Analyse if-else body: data.else_statement if let Some(else_body) = &data.else_statement { + // if-else has own state, different from if-state + let if_else_body_state = Rc::new(RefCell::new(BlockState::new(Some( + function_body_state.clone(), + )))); + function_body_state + .borrow_mut() + .set_child(if_else_body_state.clone()); + match else_body { ast::IfBodyStatements::If(body) => { // Analyze if-statement body - self.if_condition_body(body, &if_else_body_state); + self.if_condition_body( + body, + &if_else_body_state, + &label_if_end, + label_loop, + ); } ast::IfBodyStatements::Loop(body) => { let (label_loop_start, label_loop_end) = @@ -859,6 +893,7 @@ impl State { self.if_condition_loop_body( body, &if_else_body_state, + &label_if_end, label_loop_start, label_loop_end, ); @@ -866,12 +901,14 @@ impl State { } // Codegen for jump to if-end statement -return to program flow + // TODO: issue #9 if_body_state .borrow_mut() .context .jump_to(label_if_end.clone()); } else if let Some(else_if_statement) = &data.else_if_statement { // Analyse else-if statement + // Set `label_if_end` to indicate single if-end point self.if_condition( else_if_statement, function_body_state, @@ -881,8 +918,10 @@ impl State { } } - // End label for all if statement - if_body_state.borrow_mut().context.set_label(label_if_end); + // End label for all if statement, should be set only once + if !is_set_label_if_end { + if_body_state.borrow_mut().context.set_label(label_if_end); + } } /// # Loop @@ -974,6 +1013,12 @@ impl State { } } + // Because it's loop jump to loop begin + loop_body_state + .borrow_mut() + .context + .jump_to(label_loop_begin.clone()); + // Loop ending loop_body_state .borrow_mut() diff --git a/src/types/block_state.rs b/src/types/block_state.rs index 5dcb648..39d30af 100644 --- a/src/types/block_state.rs +++ b/src/types/block_state.rs @@ -34,7 +34,6 @@ pub struct BlockState { pub labels: HashSet, /// Last register for unique register representation pub last_register_number: u64, - /// Manual return from other states pub manual_return: bool, /// Parent state diff --git a/src/types/error.rs b/src/types/error.rs index 83d6f0f..fb8149e 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -21,6 +21,7 @@ pub enum StateErrorKind { FunctionNotFound, FunctionParameterTypeWrong, ReturnNotFound, + ReturnAlreadyCalled, IfElseDuplicated, TypeNotFound, WrongReturnType, diff --git a/src/types/semantic.rs b/src/types/semantic.rs index 94c1971..9121ddd 100644 --- a/src/types/semantic.rs +++ b/src/types/semantic.rs @@ -93,7 +93,7 @@ impl SemanticStack { /// Push Context to the stack as expression function return data pub fn expression_function_return(&mut self, expr_result: ExpressionResult) { - self.push(SemanticStackContext::ExpressionFunctionReturnWithLabel { expr_result }); + self.push(SemanticStackContext::ExpressionFunctionReturn { expr_result }); } /// Push Context to the stack as `expression function return with label` data diff --git a/tests/lbinding_tests.rs b/tests/binding_tests.rs similarity index 85% rename from tests/lbinding_tests.rs rename to tests/binding_tests.rs index 3a9888f..4decb15 100644 --- a/tests/lbinding_tests.rs +++ b/tests/binding_tests.rs @@ -159,10 +159,36 @@ fn binding_value_found() { block_state.borrow().values.get(&("x".into())).unwrap(), &val ); + let new_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(100)), + operation: None, + }; let binding = ast::Binding { name: ValueName::new(Ident::new("x")), - value: Box::new(expr), + value: Box::new(new_expr), }; t.state.binding(&binding, &block_state); assert!(t.is_empty_error()); + let state = block_state.borrow().context.clone().get(); + assert_eq!(state.len(), 2); + assert_eq!( + state[0], + SemanticStackContext::LetBinding { + let_decl: val.clone(), + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(30)) + }, + } + ); + assert_eq!( + state[1], + SemanticStackContext::Binding { + val: val.clone(), + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(100)) + }, + } + ); } diff --git a/tests/function_call_tests.rs b/tests/function_call_tests.rs index e7ccffb..2c6beca 100644 --- a/tests/function_call_tests.rs +++ b/tests/function_call_tests.rs @@ -1,6 +1,6 @@ use crate::utils::SemanticTest; use semantic_analyzer::ast; -use semantic_analyzer::ast::{CodeLocation, GetLocation, Ident}; +use semantic_analyzer::ast::{CodeLocation, GetLocation, GetName, Ident}; use semantic_analyzer::types::block_state::BlockState; use semantic_analyzer::types::error::StateErrorKind; use semantic_analyzer::types::types::{PrimitiveTypes, Type}; @@ -19,6 +19,7 @@ fn func_call_transform() { }; let fn_call_into: FunctionCall = fn_call.clone().into(); assert_eq!(fn_call.location(), CodeLocation::new(1, 0)); + assert_eq!(fn_call.name(), "fn1"); assert_eq!(fn_call.name.to_string(), "fn1"); assert_eq!(fn_call_into.to_string(), "fn1"); assert!(fn_call_into.parameters.is_empty()); diff --git a/tests/if_tests.rs b/tests/if_tests.rs new file mode 100644 index 0000000..5d8a64d --- /dev/null +++ b/tests/if_tests.rs @@ -0,0 +1,1386 @@ +use crate::utils::SemanticTest; +use semantic_analyzer::ast; +use semantic_analyzer::ast::{CodeLocation, GetLocation, Ident}; +use semantic_analyzer::types::block_state::BlockState; +use semantic_analyzer::types::condition::{ + Condition, ExpressionCondition, ExpressionLogicCondition, IfBodyStatement, IfBodyStatements, + IfCondition, IfLoopBodyStatement, IfStatement, LogicCondition, +}; +use semantic_analyzer::types::error::StateErrorKind; +use semantic_analyzer::types::expression::{Expression, ExpressionResult, ExpressionResultValue}; +use semantic_analyzer::types::semantic::SemanticStackContext; +use semantic_analyzer::types::types::{PrimitiveTypes, Type}; +use semantic_analyzer::types::{Function, LabelName, PrimitiveValue, Value}; +use std::cell::RefCell; +use std::rc::Rc; + +mod utils; + +#[test] +fn if_single_transform() { + let if_condition_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::F32(1.2)), + operation: None, + }; + let if_condition = ast::IfCondition::Single(if_condition_expr.clone()); + let let_binding = ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: false, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let binding = ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let fn_call = ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + }; + let if_statement2 = ast::IfStatement { + condition: if_condition.clone(), + body: ast::IfBodyStatements::If(vec![]), + else_statement: None, + else_if_statement: None, + }; + let loop_statement = ast::LoopBodyStatement::Break; + let return_statement = if_condition_expr.clone(); + let if_body = ast::IfBodyStatements::If(vec![ + ast::IfBodyStatement::LetBinding(let_binding.clone()), + ast::IfBodyStatement::Binding(binding.clone()), + ast::IfBodyStatement::FunctionCall(fn_call.clone()), + ast::IfBodyStatement::If(if_statement2.clone()), + ast::IfBodyStatement::Loop(vec![loop_statement.clone()]), + ast::IfBodyStatement::Return(return_statement.clone()), + ]); + let mut if_statement1 = ast::IfStatement { + condition: if_condition.clone(), + body: if_body.clone(), + else_statement: None, + else_if_statement: None, + }; + if_statement1.else_statement = Some(if_body.clone()); + if_statement1.else_if_statement = Some(Box::new(if_statement1.clone())); + let if_statement1_into: IfStatement = if_statement1.clone().into(); + assert_eq!( + if_statement1_into.condition, + IfCondition::Single(if_condition_expr.clone().into()) + ); + + let if_let_binding_into = IfBodyStatement::LetBinding(let_binding.into()); + let if_binding_into = IfBodyStatement::Binding(binding.into()); + let if_fn_call_into = IfBodyStatement::FunctionCall(fn_call.into()); + let if_if_statement2_into = IfBodyStatement::If(if_statement2.into()); + let loop_statement_into = IfBodyStatement::Loop(vec![loop_statement.into()]); + let return_statement_into = IfBodyStatement::Return(return_statement.into()); + assert_eq!( + if_statement1_into.body, + IfBodyStatements::If(vec![ + if_let_binding_into.clone(), + if_binding_into.clone(), + if_fn_call_into.clone(), + if_if_statement2_into.clone(), + loop_statement_into.clone(), + return_statement_into.clone() + ]) + ); + assert_eq!( + if_statement1_into.clone().else_statement.unwrap(), + IfBodyStatements::If(vec![ + if_let_binding_into, + if_binding_into, + if_fn_call_into, + if_if_statement2_into, + loop_statement_into, + return_statement_into + ]) + ); + let if_compare1 = *if_statement1_into.clone().else_if_statement.unwrap(); + let if_compare2 = IfStatement { + else_if_statement: None, + ..if_statement1_into.clone() + }; + assert_eq!(if_compare1, if_compare2); + assert_eq!(if_statement1.location(), CodeLocation::new(1, 0)); + // For grcov + format!("{if_statement1:?}"); +} + +#[test] +fn if_logic_transform() { + let if_condition_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::F32(1.2)), + operation: None, + }; + let if_condition_expr_into: Expression = if_condition_expr.clone().into(); + let expr_cond1 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::Great, + right: if_condition_expr.clone(), + }; + let expr_cond1_into: ExpressionCondition = expr_cond1.into(); + assert_eq!(expr_cond1_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond1_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond1_into.condition, Condition::Great); + + let expr_cond2 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::Less, + right: if_condition_expr.clone(), + }; + let expr_cond2_into: ExpressionCondition = expr_cond2.into(); + assert_eq!(expr_cond2_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond2_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond2_into.condition, Condition::Less); + + let expr_cond3 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::Eq, + right: if_condition_expr.clone(), + }; + let expr_cond3_into: ExpressionCondition = expr_cond3.into(); + assert_eq!(expr_cond3_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond3_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond3_into.condition, Condition::Eq); + + let expr_cond4 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::GreatEq, + right: if_condition_expr.clone(), + }; + let expr_cond4_into: ExpressionCondition = expr_cond4.into(); + assert_eq!(expr_cond4_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond4_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond4_into.condition, Condition::GreatEq); + + let expr_cond5 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::LessEq, + right: if_condition_expr.clone(), + }; + let expr_cond5_into: ExpressionCondition = expr_cond5.into(); + assert_eq!(expr_cond5_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond5_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond5_into.condition, Condition::LessEq); + + let expr_cond6 = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::NotEq, + right: if_condition_expr.clone(), + }; + let expr_cond6_into: ExpressionCondition = expr_cond6.into(); + assert_eq!(expr_cond6_into.left, if_condition_expr_into.clone()); + assert_eq!(expr_cond6_into.right, if_condition_expr_into.clone()); + assert_eq!(expr_cond6_into.condition, Condition::NotEq); + + let logic_cond_and = ast::LogicCondition::And; + let logic_cond_and_into: LogicCondition = logic_cond_and.into(); + assert_eq!(logic_cond_and_into, LogicCondition::And); + + let logic_cond_and = ast::LogicCondition::Or; + let logic_cond_and_into: LogicCondition = logic_cond_and.into(); + assert_eq!(logic_cond_and_into, LogicCondition::Or); + + let let_binding = ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: false, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let binding = ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let fn_call = ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + }; + + let if_statement2 = ast::IfStatement { + condition: ast::IfCondition::Single(if_condition_expr.clone()), + body: ast::IfBodyStatements::If(vec![]), + else_statement: None, + else_if_statement: None, + }; + let loop_statement = ast::LoopBodyStatement::Break; + let return_statement = if_condition_expr.clone(); + let if_loop_body = ast::IfBodyStatements::Loop(vec![ + ast::IfLoopBodyStatement::LetBinding(let_binding.clone()), + ast::IfLoopBodyStatement::Binding(binding.clone()), + ast::IfLoopBodyStatement::FunctionCall(fn_call.clone()), + ast::IfLoopBodyStatement::If(if_statement2.clone()), + ast::IfLoopBodyStatement::Loop(vec![loop_statement.clone()]), + ast::IfLoopBodyStatement::Return(return_statement.clone()), + ast::IfLoopBodyStatement::Break, + ast::IfLoopBodyStatement::Continue, + ]); + + let expr_cond = ast::ExpressionCondition { + left: if_condition_expr.clone(), + condition: ast::Condition::Eq, + right: if_condition_expr.clone(), + }; + let mut expr_logic_cond = ast::ExpressionLogicCondition { + left: expr_cond.clone(), + right: None, + }; + expr_logic_cond.right = Some((ast::LogicCondition::And, Box::new(expr_logic_cond.clone()))); + let if_logic_condition = ast::IfCondition::Logic(expr_logic_cond.clone()); + + let if_statement3 = ast::IfStatement { + condition: if_logic_condition.clone(), + body: if_loop_body, + else_statement: None, + else_if_statement: None, + }; + // For grcov + format!("{if_statement3:?}"); + let expr_logic_cond_into: ExpressionLogicCondition = expr_logic_cond.into(); + assert_eq!( + expr_logic_cond_into.left, + ExpressionCondition { + left: if_condition_expr_into.clone(), + condition: Condition::Eq, + right: if_condition_expr_into, + } + ); + let if_statement3_into: IfStatement = if_statement3.clone().into(); + assert_eq!( + if_statement3_into.condition, + IfCondition::Logic(expr_logic_cond_into) + ); + let if_let_binding_into = IfLoopBodyStatement::LetBinding(let_binding.into()); + let if_binding_into = IfLoopBodyStatement::Binding(binding.into()); + let if_fn_call_into = IfLoopBodyStatement::FunctionCall(fn_call.into()); + let if_statement2_into = IfLoopBodyStatement::If(if_statement2.into()); + let loop_statement_into = IfLoopBodyStatement::Loop(vec![loop_statement.into()]); + let return_statement_into = IfLoopBodyStatement::Return(return_statement.into()); + assert_eq!( + if_statement3_into.body, + IfBodyStatements::Loop(vec![ + if_let_binding_into, + if_binding_into, + if_fn_call_into, + if_statement2_into, + loop_statement_into, + return_statement_into, + IfLoopBodyStatement::Break, + IfLoopBodyStatement::Continue, + ]) + ); + assert!(if_statement3_into.else_if_statement.is_none()); + assert!(if_statement3_into.else_if_statement.is_none()); +} + +#[test] +fn check_if_and_else_if_statement_duplicate() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }; + + let if_else_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr.clone()), + body: ast::IfBodyStatements::If(vec![]), + else_statement: None, + else_if_statement: None, + }; + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![]), + else_statement: Some(ast::IfBodyStatements::If(vec![])), + else_if_statement: Some(Box::new(if_else_stmt)), + }; + t.state.if_condition(&if_stmt, &block_state, None, None); + t.check_errors_len(1); + t.check_error(StateErrorKind::IfElseDuplicated); +} + +#[test] +fn if_condition_calculation_simple() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + // Simple without operations + let condition1 = ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(3)), + operation: None, + }); + let label_if_begin: LabelName = String::from("if_begin").into(); + let label_if_else: LabelName = String::from("if_else").into(); + let label_if_end: LabelName = String::from("if_end").into(); + t.state.if_condition_calculation( + &condition1, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + + // Simple with else without operations + let condition2 = ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(3)), + operation: None, + }); + t.state.if_condition_calculation( + &condition2, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + true, + ); + + // Simple with wrong expression without operations + let condition3 = ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::ValueName(ast::ValueName::new(Ident::new("x"))), + operation: None, + }); + t.state.if_condition_calculation( + &condition3, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + true, + ); + + let ctx = block_state.borrow().context.clone().get(); + assert_eq!(ctx.len(), 2); + assert_eq!( + ctx[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8,), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(3,),), + }, + label_if_begin: label_if_begin.clone(), + label_if_end, + } + ); + assert_eq!( + ctx[1], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8,), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(3,),), + }, + label_if_begin, + label_if_end: label_if_else, + } + ); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::ValueNotFound)); +} + +#[test] +fn if_condition_calculation_logic() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let left_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(3)), + operation: None, + }; + let right_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(6)), + operation: None, + }; + let label_if_begin: LabelName = String::from("if_begin").into(); + let label_if_else: LabelName = String::from("if_else").into(); + let label_if_end: LabelName = String::from("if_end").into(); + + // Logic: left == right + let condition1 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }); + t.state.if_condition_calculation( + &condition1, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + + // Logic else: left == right + let condition2 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }); + t.state.if_condition_calculation( + &condition2, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + true, + ); + + // Logic condition: left == right + let condition3 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: Some(( + ast::LogicCondition::Or, + Box::new(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }), + )), + }); + t.state.if_condition_calculation( + &condition3, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + + let ctx = block_state.borrow().context.clone().get(); + assert_eq!( + ctx[0], + SemanticStackContext::ConditionExpression { + left_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(3)) + }, + right_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(6)) + }, + condition: Condition::Eq + } + ); + assert_eq!( + ctx[1], + SemanticStackContext::IfConditionLogic { + label_if_begin: label_if_begin.clone(), + label_if_end + } + ); + assert_eq!(ctx[2], ctx[0]); + assert_eq!( + ctx[3], + SemanticStackContext::IfConditionLogic { + label_if_begin, + label_if_end: label_if_else + } + ); + assert_eq!(ctx[4], ctx[0]); + assert_eq!(ctx[5], ctx[0]); + assert_eq!( + ctx[6], + SemanticStackContext::LogicCondition { + logic_condition: LogicCondition::Or + } + ); + assert_eq!(ctx[7], ctx[1]); + assert_eq!(ctx.len(), 8); + assert!(t.is_empty_error()); +} + +#[test] +fn if_condition_when_left_expr_return_error() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + // Left expression return error - function not found + let left_expr = ast::Expression { + expression_value: ast::ExpressionValue::FunctionCall(ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + }), + operation: None, + }; + let right_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(6)), + operation: None, + }; + let label_if_begin: LabelName = String::from("if_begin").into(); + let label_if_else: LabelName = String::from("if_else").into(); + let label_if_end: LabelName = String::from("if_end").into(); + + let condition1 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }); + t.state.if_condition_calculation( + &condition1, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::FunctionNotFound)); +} + +#[test] +fn if_condition_left_expr_and_right_expr_different_type() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + // Left expression return error - function not found + let left_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I16(10)), + operation: None, + }; + let right_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(6)), + operation: None, + }; + let label_if_begin: LabelName = String::from("if_begin").into(); + let label_if_else: LabelName = String::from("if_else").into(); + let label_if_end: LabelName = String::from("if_end").into(); + + let condition1 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }); + t.state.if_condition_calculation( + &condition1, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::ConditionExpressionWrongType)); +} + +#[test] +fn if_condition_primitive_type_only_check() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let type_decl = ast::StructTypes { + name: Ident::new("type1"), + attributes: vec![], + }; + t.state.types(&type_decl.clone()); + + let fn_name = ast::FunctionName::new(Ident::new("fn1")); + let fn_statement = ast::FunctionStatement { + name: fn_name.clone(), + parameters: vec![], + result_type: ast::Type::Struct(type_decl), + body: vec![], + }; + t.state.function_declaration(&fn_statement); + + // Left expression return error - function not found + let left_expr = ast::Expression { + expression_value: ast::ExpressionValue::FunctionCall(ast::FunctionCall { + name: fn_name.clone(), + parameters: vec![], + }), + operation: None, + }; + let right_expr = ast::Expression { + expression_value: ast::ExpressionValue::FunctionCall(ast::FunctionCall { + name: fn_name, + parameters: vec![], + }), + operation: None, + }; + let label_if_begin: LabelName = String::from("if_begin").into(); + let label_if_else: LabelName = String::from("if_else").into(); + let label_if_end: LabelName = String::from("if_end").into(); + + let condition1 = ast::IfCondition::Logic(ast::ExpressionLogicCondition { + left: ast::ExpressionCondition { + left: left_expr.clone(), + condition: ast::Condition::Eq, + right: right_expr.clone(), + }, + right: None, + }); + t.state.if_condition_calculation( + &condition1, + &block_state, + &label_if_begin, + &label_if_else, + &label_if_end, + false, + ); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::ConditionExpressionNotSupported)); +} + +#[test] +fn else_if_statement() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + let if_else_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(2)), + operation: None, + }; + + let if_else_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_else_expr), + body: ast::IfBodyStatements::If(vec![ast::IfBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(30)), + operation: None, + })]), + else_statement: Some(ast::IfBodyStatements::Loop(vec![ + ast::IfLoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64( + 10, + )), + operation: None, + }), + ])), + else_if_statement: None, + }; + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![ast::IfBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(12)), + operation: None, + })]), + else_statement: None, + else_if_statement: Some(Box::new(if_else_stmt)), + }; + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + assert!(t.is_empty_error()); + + assert!(block_state.borrow().context.clone().get().is_empty()); + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().children.len(), 3); + + let ch_ctx1 = block_state.borrow().children[0].clone(); + assert!(ch_ctx1.borrow().parent.is_some()); + assert!(ch_ctx1.borrow().children.is_empty()); + + let ch_ctx2 = block_state.borrow().children[1].clone(); + assert!(ch_ctx2.borrow().parent.is_some()); + assert!(ch_ctx2.borrow().children.is_empty()); + + let ch_ctx3 = block_state.borrow().children[2].clone(); + assert!(ch_ctx3.borrow().parent.is_some()); + assert!(ch_ctx3.borrow().children.is_empty()); + + let ctx1 = ch_ctx1.borrow().context.clone().get(); + assert_eq!(ctx1.len(), 6); + assert_eq!( + ctx1[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(1)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_else").into() + } + ); + assert_eq!( + ctx1[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + ctx1[2], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(12)), + } + } + ); + assert_eq!( + ctx1[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + ctx1[4], + SemanticStackContext::SetLabel { + label: String::from("if_else").into() + } + ); + assert_eq!( + ctx1[5], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + let ctx2 = ch_ctx2.borrow().context.clone().get(); + assert_eq!(ctx2.len(), 6); + assert_eq!( + ctx2[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(2)), + }, + label_if_begin: String::from("if_begin.0").into(), + label_if_end: String::from("if_else.0").into() + } + ); + assert_eq!( + ctx2[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin.0").into() + } + ); + assert_eq!( + ctx2[2], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(30)), + } + } + ); + assert_eq!( + ctx2[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + ctx2[4], + SemanticStackContext::SetLabel { + label: String::from("if_else.0").into() + } + ); + assert_eq!( + ctx2[5], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + + let ctx3 = ch_ctx3.borrow().context.clone().get(); + assert_eq!(ctx3.len(), 1); + assert_eq!( + ctx3[0], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(10)), + } + } + ); +} + +#[test] +fn if_body_statements() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let fn2 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U16), + body: vec![ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U16(23)), + operation: None, + })], + }; + t.state.function_declaration(&fn2); + + let if_body_let_binding = ast::IfBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_binding = ast::IfBodyStatement::Binding(ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }); + let if_body_fn_call = ast::IfBodyStatement::FunctionCall(ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }); + let if_body_if = ast::IfBodyStatement::If(ast::IfStatement { + condition: ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + body: ast::IfBodyStatements::Loop(vec![ast::IfLoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]), + else_statement: None, + else_if_statement: None, + }); + let if_body_loop = ast::IfBodyStatement::Loop(vec![ast::LoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]); + let if_body_return = ast::IfBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![ + if_body_let_binding, + if_body_binding, + if_body_fn_call, + if_body_if, + if_body_loop, + if_body_return, + ]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + assert!(t.is_empty_error()); + + assert!(block_state.borrow().context.clone().get().is_empty()); + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().children.len(), 1); + + let ctx = block_state.borrow().children[0].clone(); + assert!(ctx.borrow().parent.is_some()); + assert_eq!(ctx.borrow().children.len(), 2); + + let stm_ctx = ctx.borrow().context.clone().get(); + assert_eq!(stm_ctx.len(), 8); + assert_eq!( + stm_ctx[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(1)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + stm_ctx[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + stm_ctx[2], + SemanticStackContext::LetBinding { + let_decl: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(false)), + }, + } + ); + assert_eq!( + stm_ctx[3], + SemanticStackContext::Binding { + val: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + } + ); + assert_eq!( + stm_ctx[4], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + stm_ctx[5], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + assert_eq!( + stm_ctx[6], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + stm_ctx[7], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + let ch_ctx1 = ctx.borrow().children[0].clone(); + assert!(ch_ctx1.borrow().parent.is_some()); + assert!(ch_ctx1.borrow().children.is_empty()); + + let ctx1 = ch_ctx1.borrow().context.clone().get(); + assert_eq!(ctx1.len(), 4); + assert_eq!( + ctx1[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + label_if_begin: String::from("if_begin.0").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + ctx1[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin.0").into() + } + ); + assert_eq!( + ctx1[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx1[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + + let ch_ctx2 = ctx.borrow().children[1].clone(); + assert!(ch_ctx2.borrow().parent.is_some()); + assert!(ch_ctx2.borrow().children.is_empty()); + + let ctx2 = ch_ctx2.borrow().context.clone().get(); + assert_eq!(ctx2.len(), 5); + assert_eq!( + ctx2[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + + assert_eq!( + ctx2[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + ctx2[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx2[3], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + ctx2[4], + SemanticStackContext::SetLabel { + label: String::from("loop_end").into() + } + ); +} + +#[test] +fn if_loop_body_statements() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(1)), + operation: None, + }; + + let fn2 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U16), + body: vec![ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U16(23)), + operation: None, + })], + }; + t.state.function_declaration(&fn2); + let fn3 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn3")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::I16), + body: vec![ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I16(32)), + operation: None, + })], + }; + t.state.function_declaration(&fn3); + + let if_body_let_binding = ast::IfLoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let if_body_binding = ast::IfLoopBodyStatement::Binding(ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }); + let if_body_fn_call = ast::IfLoopBodyStatement::FunctionCall(ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }); + let if_body_if = ast::IfLoopBodyStatement::If(ast::IfStatement { + condition: ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + body: ast::IfBodyStatements::Loop(vec![ast::IfLoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]), + else_statement: None, + else_if_statement: None, + }); + let if_body_loop = ast::IfLoopBodyStatement::Loop(vec![ast::LoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn3")), + parameters: vec![], + }, + )]); + let if_body_return = ast::IfLoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let if_body_break = ast::IfLoopBodyStatement::Break; + let if_body_continue = ast::IfLoopBodyStatement::Continue; + + let label_loop_begin: LabelName = String::from("loop_begin").into(); + let label_loop_end: LabelName = String::from("loop_end").into(); + + let if_stmt = ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::Loop(vec![ + if_body_let_binding, + if_body_binding, + if_body_fn_call, + if_body_if, + if_body_loop, + if_body_break, + if_body_continue, + if_body_return, + ]), + else_statement: None, + else_if_statement: None, + }; + + t.state.if_condition( + &if_stmt, + &block_state, + None, + Some((&label_loop_begin, &label_loop_end)), + ); + assert!(t.is_empty_error()); + + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().context.clone().get().len(), 0); + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().children.len(), 1); + + let ctx = block_state.borrow().children[0].clone(); + assert!(ctx.borrow().parent.is_some()); + assert_eq!(ctx.borrow().children.len(), 2); + + let stm_ctx = ctx.borrow().context.clone().get(); + assert_eq!(stm_ctx.len(), 10); + assert_eq!( + stm_ctx[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U64), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(1)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + stm_ctx[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + stm_ctx[2], + SemanticStackContext::LetBinding { + let_decl: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(false)), + }, + } + ); + assert_eq!( + stm_ctx[3], + SemanticStackContext::Binding { + val: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + } + ); + assert_eq!( + stm_ctx[4], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + stm_ctx[5], + SemanticStackContext::JumpTo { + label: String::from("loop_end").into() + } + ); + assert_eq!( + stm_ctx[6], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[7], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + assert_eq!( + stm_ctx[8], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + stm_ctx[9], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + let ch_ctx1 = ctx.borrow().children[0].clone(); + assert!(ch_ctx1.borrow().parent.is_some()); + assert!(ch_ctx1.borrow().children.is_empty()); + + let ctx1 = ch_ctx1.borrow().context.clone().get(); + assert_eq!(ctx1.len(), 4); + assert_eq!( + ctx1[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + label_if_begin: String::from("if_begin.0").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + ctx1[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin.0").into() + } + ); + assert_eq!( + ctx1[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx1[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + + let ch_ctx2 = ctx.borrow().children[1].clone(); + assert!(ch_ctx2.borrow().parent.is_some()); + assert!(ch_ctx2.borrow().children.is_empty()); + + let ctx2 = ch_ctx2.borrow().context.clone().get(); + assert_eq!(ctx2.len(), 5); + assert_eq!( + ctx2[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + ctx2[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + ctx2[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn3").into(), + inner_type: Type::Primitive(PrimitiveTypes::I16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx2[3], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + ctx2[4], + SemanticStackContext::SetLabel { + label: String::from("loop_end").into() + } + ); +} diff --git a/tests/loop_tests.rs b/tests/loop_tests.rs new file mode 100644 index 0000000..5bfe69a --- /dev/null +++ b/tests/loop_tests.rs @@ -0,0 +1,351 @@ +use crate::utils::SemanticTest; +use semantic_analyzer::ast; +use semantic_analyzer::ast::Ident; +use semantic_analyzer::types::block_state::BlockState; +use semantic_analyzer::types::condition::LoopBodyStatement; +use semantic_analyzer::types::expression::{ExpressionResult, ExpressionResultValue}; +use semantic_analyzer::types::semantic::SemanticStackContext; +use semantic_analyzer::types::types::{PrimitiveTypes, Type}; +use semantic_analyzer::types::{Function, PrimitiveValue, Value}; +use std::cell::RefCell; +use std::rc::Rc; + +mod utils; + +#[test] +fn loop_transform() { + let let_binding = ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: false, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let binding = ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }; + let fn_call = ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + }; + let if_statement = ast::IfStatement { + condition: ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::F32(1.2)), + operation: None, + }), + body: ast::IfBodyStatements::If(vec![]), + else_statement: None, + else_if_statement: None, + }; + let loop_statement = ast::LoopBodyStatement::Break; + let return_statement = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::F32(1.2)), + operation: None, + }; + let loop_stmts = vec![ + ast::LoopBodyStatement::LetBinding(let_binding.clone()), + ast::LoopBodyStatement::Binding(binding.clone()), + ast::LoopBodyStatement::FunctionCall(fn_call.clone()), + ast::LoopBodyStatement::If(if_statement.clone()), + ast::LoopBodyStatement::Loop(vec![loop_statement.clone()]), + ast::LoopBodyStatement::Return(return_statement.clone()), + ast::LoopBodyStatement::Break, + ast::LoopBodyStatement::Continue, + ]; + // For grcov®® + format!("{loop_stmts:#?}"); + for loop_stmt in loop_stmts { + let loop_stmt_into: LoopBodyStatement = loop_stmt.into(); + match loop_stmt_into { + LoopBodyStatement::LetBinding(val) => assert_eq!(val, let_binding.clone().into()), + LoopBodyStatement::Binding(val) => assert_eq!(val, binding.clone().into()), + LoopBodyStatement::FunctionCall(val) => assert_eq!(val, fn_call.clone().into()), + LoopBodyStatement::If(val) => assert_eq!(val, if_statement.clone().into()), + LoopBodyStatement::Loop(val) => assert_eq!(val, vec![loop_statement.clone().into()]), + LoopBodyStatement::Return(val) => assert_eq!(val, return_statement.clone().into()), + LoopBodyStatement::Break => assert_eq!( + LoopBodyStatement::Break, + ast::LoopBodyStatement::Break.into() + ), + LoopBodyStatement::Continue => assert_eq!( + LoopBodyStatement::Continue, + ast::LoopBodyStatement::Continue.into() + ), + } + } +} + +#[test] +fn loop_statements() { + let block_state = Rc::new(RefCell::new(BlockState::new(None))); + let mut t = SemanticTest::new(); + + let fn2 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U16), + body: vec![ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U16(23)), + operation: None, + })], + }; + t.state.function_declaration(&fn2); + + let loop_body_let_binding = ast::LoopBodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let loop_body_binding = ast::LoopBodyStatement::Binding(ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }); + let loop_body_fn_call = ast::LoopBodyStatement::FunctionCall(ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }); + let loop_body_if = ast::LoopBodyStatement::If(ast::IfStatement { + condition: ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + body: ast::IfBodyStatements::Loop(vec![ast::IfLoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]), + else_statement: None, + else_if_statement: None, + }); + let loop_body_loop = ast::LoopBodyStatement::Loop(vec![ast::LoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]); + let loop_body_return = ast::LoopBodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let loop_body_break = ast::LoopBodyStatement::Break; + let loop_body_continue = ast::LoopBodyStatement::Continue; + + let loop_stmt = [ + loop_body_let_binding, + loop_body_binding, + loop_body_fn_call, + loop_body_if, + loop_body_loop, + loop_body_break, + loop_body_continue, + loop_body_return, + ]; + t.state.loop_statement(&loop_stmt, &block_state); + + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().context.clone().get().len(), 0); + assert!(block_state.borrow().parent.is_none()); + assert_eq!(block_state.borrow().children.len(), 1); + + let ctx = block_state.borrow().children[0].clone(); + assert!(ctx.borrow().parent.is_some()); + assert_eq!(ctx.borrow().children.len(), 2); + + let stm_ctx = ctx.borrow().context.clone().get(); + assert_eq!(stm_ctx.len(), 10); + assert_eq!( + stm_ctx[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[2], + SemanticStackContext::LetBinding { + let_decl: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(false)), + }, + } + ); + assert_eq!( + stm_ctx[3], + SemanticStackContext::Binding { + val: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + } + ); + assert_eq!( + stm_ctx[4], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + stm_ctx[5], + SemanticStackContext::JumpTo { + label: String::from("loop_end").into() + } + ); + assert_eq!( + stm_ctx[6], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[7], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + assert_eq!( + stm_ctx[8], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + stm_ctx[9], + SemanticStackContext::SetLabel { + label: String::from("loop_end").into() + } + ); + + let ch_ctx1 = ctx.borrow().children[0].clone(); + assert!(ch_ctx1.borrow().parent.is_some()); + assert!(ch_ctx1.borrow().children.is_empty()); + + let ctx1 = ch_ctx1.borrow().context.clone().get(); + assert_eq!(ctx1.len(), 5); + assert_eq!( + ctx1[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + ctx1[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + ctx1[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx1[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + ctx1[4], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + let ch_ctx2 = ctx.borrow().children[1].clone(); + assert!(ch_ctx2.borrow().parent.is_some()); + assert!(ch_ctx2.borrow().children.is_empty()); + + let ctx2 = ch_ctx2.borrow().context.clone().get(); + assert_eq!(ctx2.len(), 5); + assert_eq!( + ctx2[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin.0").into() + } + ); + assert_eq!( + ctx2[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin.0").into() + } + ); + assert_eq!( + ctx2[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + ctx2[3], + SemanticStackContext::JumpTo { + label: String::from("loop_begin.0").into() + } + ); + assert_eq!( + ctx2[4], + SemanticStackContext::SetLabel { + label: String::from("loop_end.0").into() + } + ); + + assert!(t.is_empty_error()); +} diff --git a/tests/main_tests.rs b/tests/main_tests.rs new file mode 100644 index 0000000..e420d8c --- /dev/null +++ b/tests/main_tests.rs @@ -0,0 +1,531 @@ +use crate::utils::SemanticTest; +use semantic_analyzer::ast::{self, GetName, Ident}; +use semantic_analyzer::types::error::StateErrorKind; +use semantic_analyzer::types::{ + expression::{ExpressionResult, ExpressionResultValue}, + semantic::SemanticStackContext, + types::{PrimitiveTypes, Type}, + Function, PrimitiveValue, Value, +}; + +mod utils; + +#[test] +fn main_run() { + let mut t = SemanticTest::new(); + let imports: ast::ImportPath = vec![ast::ImportName::new(Ident::new("import1"))]; + let import_stm = ast::MainStatement::Import(imports); + + let constant1 = ast::Constant { + name: ast::ConstantName::new(Ident::new("const1")), + constant_type: ast::Type::Primitive(ast::PrimitiveTypes::None), + constant_value: ast::ConstantExpression { + value: ast::ConstantValue::Constant(ast::ConstantName::new(Ident::new("const2"))), + operation: None, + }, + }; + let constant_stm = ast::MainStatement::Constant(constant1.clone()); + + let ty = ast::StructTypes { + name: Ident::new("StructType"), + attributes: vec![], + }; + let ty_stm = ast::MainStatement::Types(ty.clone()); + + let body_let_binding = ast::BodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("x")), + mutable: true, + value_type: None, + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool( + false, + )), + operation: None, + }), + }); + let body_binding = ast::BodyStatement::Binding(ast::Binding { + name: ast::ValueName::new(Ident::new("x")), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + }); + let body_fn_call = ast::BodyStatement::FunctionCall(ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }); + let body_if = ast::BodyStatement::If(ast::IfStatement { + condition: ast::IfCondition::Single(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }), + body: ast::IfBodyStatements::If(vec![ast::IfBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]), + else_statement: None, + else_if_statement: None, + }); + let body_loop = ast::BodyStatement::Loop(vec![ast::LoopBodyStatement::FunctionCall( + ast::FunctionCall { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + }, + )]); + let body_return = ast::BodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::Bool), + body: vec![ + body_let_binding, + body_binding, + body_fn_call, + body_if, + body_loop, + body_return.clone(), + ], + }; + let fn_stm = ast::MainStatement::Function(fn1.clone()); + + let body_expr_return = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U16(23)), + operation: None, + }); + let fn2 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn2")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U16), + body: vec![body_expr_return], + }; + let fn2_stm = ast::MainStatement::Function(fn2.clone()); + let main_stm: ast::Main = vec![import_stm, constant_stm, ty_stm, fn_stm, fn2_stm]; + // For grcov + format!("{main_stm:#?}"); + t.state.run(&main_stm); + assert!(t.is_empty_error()); + + assert_eq!( + t.state + .global + .constants + .get(&constant1.clone().name().into()) + .unwrap(), + &(constant1.clone().into()) + ); + assert_eq!( + t.state.global.types.get(&ty.clone().name().into()).unwrap(), + &Type::Struct(ty.clone().into()) + ); + let fn_state = t + .state + .global + .functions + .get(&fn1.clone().name().into()) + .unwrap(); + assert_eq!(fn_state.inner_name, fn1.name.clone().into()); + assert_eq!(fn_state.inner_type, fn1.result_type.clone().into()); + assert!(fn_state.parameters.is_empty()); + + // Function body context + assert_eq!(t.state.context.len(), 2); + let ctx1 = t.state.context[0].borrow(); + assert_eq!(ctx1.children.len(), 2); + assert!(ctx1.parent.is_none()); + let ctx2 = t.state.context[1].borrow(); + assert!(ctx2.children.is_empty()); + assert!(ctx2.parent.is_none()); + + let ch_ctx1 = ctx1.children[0].clone(); + assert!(ch_ctx1.borrow().parent.is_some()); + assert!(ch_ctx1.borrow().children.is_empty()); + + let ch_ctx2 = ctx1.children[1].clone(); + assert!(ch_ctx2.borrow().parent.is_some()); + assert!(ch_ctx2.borrow().children.is_empty()); + + // Semantic stack context for the block fn1 + let st_ctx1 = ctx1.context.clone().get(); + assert_eq!(st_ctx1.len(), 4); + assert_eq!( + st_ctx1[0], + SemanticStackContext::LetBinding { + let_decl: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(false)), + }, + } + ); + assert_eq!( + st_ctx1[1], + SemanticStackContext::Binding { + val: Value { + inner_name: "x.0".into(), + inner_type: Type::Primitive(PrimitiveTypes::Bool), + mutable: true, + alloca: false, + malloc: false + }, + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + } + ); + assert_eq!( + st_ctx1[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + + assert_eq!( + st_ctx1[3], + SemanticStackContext::ExpressionFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + + // Semantic stack context for the block fn2 + let st_ctx2 = ctx2.context.clone().get(); + assert_eq!(st_ctx2.len(), 1); + assert_eq!( + st_ctx2[0], + SemanticStackContext::ExpressionFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::U16), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U16(23)), + } + } + ); + + let st_ch_ctx1 = ch_ctx1.borrow().context.clone().get(); + assert_eq!(st_ch_ctx1.len(), 5); + assert_eq!( + st_ch_ctx1[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + st_ch_ctx1[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + st_ch_ctx1[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + st_ch_ctx1[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + st_ch_ctx1[4], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + let st_ch_ctx2 = ch_ctx2.borrow().context.clone().get(); + assert_eq!(st_ch_ctx2.len(), 5); + assert_eq!( + st_ch_ctx2[0], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + st_ch_ctx2[1], + SemanticStackContext::SetLabel { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + st_ch_ctx2[2], + SemanticStackContext::Call { + call: Function { + inner_name: String::from("fn2").into(), + inner_type: Type::Primitive(PrimitiveTypes::U16), + parameters: vec![], + }, + params: vec![], + } + ); + assert_eq!( + st_ch_ctx2[3], + SemanticStackContext::JumpTo { + label: String::from("loop_begin").into() + } + ); + assert_eq!( + st_ch_ctx2[4], + SemanticStackContext::SetLabel { + label: String::from("loop_end").into() + } + ); + + // Global semantic stack context + let st_global_context = t.state.global.context.get(); + assert_eq!(st_global_context.len(), 4); + assert_eq!( + st_global_context[0], + SemanticStackContext::Types { + type_decl: ty.into() + } + ); + assert_eq!( + st_global_context[1], + SemanticStackContext::Constant { + const_decl: constant1.into() + } + ); + assert_eq!( + st_global_context[2], + SemanticStackContext::FunctionDeclaration { + fn_decl: fn1.into() + } + ); + assert_eq!( + st_global_context[3], + SemanticStackContext::FunctionDeclaration { + fn_decl: fn2.into() + } + ); +} + +#[test] +fn double_return() { + let mut t = SemanticTest::new(); + let body_return = ast::BodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let body_expr = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::Bool), + body: vec![body_return, body_expr], + }; + let fn_stm = ast::MainStatement::Function(fn1); + let main_stm: ast::Main = vec![fn_stm]; + t.state.run(&main_stm); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::ReturnAlreadyCalled)); +} + +#[test] +fn wrong_return_type() { + let mut t = SemanticTest::new(); + let body_return = ast::BodyStatement::Return(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(10)), + operation: None, + }); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::Bool), + body: vec![body_return], + }; + let fn_stm = ast::MainStatement::Function(fn1); + let main_stm: ast::Main = vec![fn_stm]; + t.state.run(&main_stm); + assert!(t.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::WrongReturnType)); +} + +#[test] +fn expression_as_return() { + let mut t = SemanticTest::new(); + let body_expr = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::Bool), + body: vec![body_expr], + }; + let fn_stm = ast::MainStatement::Function(fn1.clone()); + let main_stm: ast::Main = vec![fn_stm]; + t.state.run(&main_stm); + assert!(t.is_empty_error()); + + // Function body context + assert_eq!(t.state.context.len(), 1); + let ctx = t.state.context[0].borrow(); + assert!(ctx.children.is_empty()); + assert!(ctx.parent.is_none()); + + // Semantic stack context for the block + let st_ctx = ctx.context.clone().get(); + assert_eq!(st_ctx.len(), 1); + assert_eq!( + st_ctx[0], + SemanticStackContext::ExpressionFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + } + } + ); + + // Global semantic stack context + let st_global_context = t.state.global.context.get(); + assert_eq!(st_global_context.len(), 1); + assert_eq!( + st_global_context[0], + SemanticStackContext::FunctionDeclaration { + fn_decl: fn1.into() + } + ); +} + +#[test] +fn if_return_from_function() { + let mut t = SemanticTest::new(); + let body_expr_return = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(5)), + operation: None, + }); + let if_expr_return = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::I8(10)), + operation: None, + }; + let if_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::Bool(true)), + operation: None, + }; + let body_if = ast::BodyStatement::If(ast::IfStatement { + condition: ast::IfCondition::Single(if_expr), + body: ast::IfBodyStatements::If(vec![ast::IfBodyStatement::Return(if_expr_return)]), + else_statement: None, + else_if_statement: None, + }); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::I8), + body: vec![body_if, body_expr_return], + }; + let fn_stm = ast::MainStatement::Function(fn1.clone()); + let main_stm: ast::Main = vec![fn_stm]; + t.state.run(&main_stm); + assert!(t.is_empty_error()); + + // Function body context + assert_eq!(t.state.context.len(), 1); + let ctx = t.state.context[0].borrow(); + assert_eq!(ctx.children.len(), 1); + assert!(ctx.parent.is_none()); + + // Children block context + let children_ctx = ctx.children[0].borrow(); + assert!(children_ctx.children.is_empty()); + assert!(children_ctx.parent.is_some()); + + // Children semantic stack context for the block + let st_children_ctx = children_ctx.context.clone().get(); + assert_eq!(st_children_ctx.len(), 5); + assert_eq!( + st_children_ctx[0], + SemanticStackContext::IfConditionExpression { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::Bool), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Bool(true)), + }, + label_if_begin: String::from("if_begin").into(), + label_if_end: String::from("if_end").into() + } + ); + assert_eq!( + st_children_ctx[1], + SemanticStackContext::SetLabel { + label: String::from("if_begin").into() + } + ); + assert_eq!( + st_children_ctx[2], + SemanticStackContext::JumpFunctionReturn { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(10)), + } + } + ); + assert_eq!( + st_children_ctx[3], + SemanticStackContext::JumpTo { + label: String::from("if_end").into() + } + ); + assert_eq!( + st_children_ctx[4], + SemanticStackContext::SetLabel { + label: String::from("if_end").into() + } + ); + + // Semantic stack context for the block + let st_ctx = ctx.context.clone().get(); + assert_eq!(st_ctx.len(), 1); + assert_eq!( + st_ctx[0], + SemanticStackContext::ExpressionFunctionReturnWithLabel { + expr_result: ExpressionResult { + expr_type: Type::Primitive(PrimitiveTypes::I8), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(5)), + } + } + ); + + // Global semantic stack context + let st_global_context = t.state.global.context.get(); + assert_eq!(st_global_context.len(), 1); + assert_eq!( + st_global_context[0], + SemanticStackContext::FunctionDeclaration { + fn_decl: fn1.into() + } + ); +}