diff --git a/Cargo.toml b/Cargo.toml index 8dcbbf8..f66e248 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "semantic-analyzer" -version = "0.3.1" +version = "0.3.2" 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"] @@ -13,5 +13,9 @@ repository = "https://github.com/mrLSD/semantic-analyzer-rs" [lib] doctest = false +#[lints.clippy] +#pedantic = "deny" +#nursery = "deny" + [dependencies] nom_locate = "4.2" diff --git a/src/ast.rs b/src/ast.rs index 81f5da3..5a8f3a9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -33,6 +33,7 @@ impl GetName for ImportName<'_> { } impl<'a> ImportName<'a> { + #[must_use] pub const fn new(ident: Ident<'a>) -> Self { Self(ident) } @@ -47,6 +48,7 @@ pub struct ConstantName<'a>(Ident<'a>); impl<'a> ConstantName<'a> { /// Init `ConstantName`, especially useful for testing + #[must_use] pub const fn new(name: Ident<'a>) -> Self { Self(name) } @@ -69,6 +71,7 @@ impl GetName for ConstantName<'_> { pub struct FunctionName<'a>(Ident<'a>); impl<'a> FunctionName<'a> { + #[must_use] pub const fn new(name: Ident<'a>) -> Self { Self(name) } @@ -92,6 +95,7 @@ impl<'a> ToString for FunctionName<'a> { pub struct ParameterName<'a>(Ident<'a>); impl<'a> ParameterName<'a> { + #[must_use] pub const fn new(name: Ident<'a>) -> Self { Self(name) } @@ -112,6 +116,7 @@ impl ToString for ParameterName<'_> { pub struct ValueName<'a>(Ident<'a>); impl<'a> ValueName<'a> { + #[must_use] pub const fn new(name: Ident<'a>) -> Self { Self(name) } @@ -138,16 +143,19 @@ impl CodeLocation { /// Initialize code location with: /// - `location` - line of source code /// - `offset` - position on the line of source code + #[must_use] pub const fn new(location: u32, offset: usize) -> Self { Self(location, offset) } /// Get location line in the source + #[must_use] pub const fn line(&self) -> u32 { self.0 } /// Get location position on the line in the source + #[must_use] pub const fn offset(&self) -> usize { self.1 } @@ -383,6 +391,7 @@ pub enum PrimitiveValue { } impl PrimitiveValue { + #[must_use] pub const fn get_type(&self) -> Type<'_> { match self { Self::U8(_) => Type::Primitive(PrimitiveTypes::U8), @@ -451,6 +460,8 @@ pub enum ExpressionOperations { } impl ExpressionOperations { + /// Get expression operation priority level + #[must_use] pub const fn priority(&self) -> u8 { match self { Self::Plus => 5, diff --git a/src/lib.rs b/src/lib.rs index f66e2dc..4886475 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(clippy::pedantic, clippy::nursery)] +#![allow(clippy::module_name_repetitions)] //! # Semantic Analyzer //! The semantic analyzer consists of the following basic elements: //! - AST is an abstract syntax tree that implements a predefined set of diff --git a/src/semantic.rs b/src/semantic.rs index 2310178..fb68513 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -19,7 +19,7 @@ use crate::types::semantic::{GlobalSemanticContext, SemanticContext, SemanticSta use crate::types::types::{Type, TypeName}; use crate::types::{ error, Binding, Constant, ConstantName, Function, FunctionCall, FunctionName, - FunctionStatement, LabelName, LetBinding, Value, + FunctionParameter, FunctionStatement, InnerValueName, LabelName, LetBinding, Value, }; use std::cell::RefCell; use std::collections::HashMap; @@ -74,6 +74,7 @@ impl Default for State { impl State { /// Init new `State` + #[must_use] pub fn new() -> Self { Self { global: GlobalState { @@ -129,7 +130,7 @@ impl State { // identification for using it in functions body. // First pass is Imports and Types - for main in data.iter() { + for main in data { match main { ast::MainStatement::Import(import) => self.import(import), ast::MainStatement::Types(types) => self.types(types), @@ -137,7 +138,7 @@ impl State { } } // Declaration pass for Constants and Functions - for main in data.iter() { + for main in data { match main { ast::MainStatement::Constant(constant) => self.constant(constant), ast::MainStatement::Function(function) => self.function_declaration(function), @@ -146,7 +147,7 @@ impl State { } // After getting all functions declarations, fetch only functions body - for main in data.iter() { + for main in data { if let ast::MainStatement::Function(function) = main { self.function_body(function); } @@ -182,7 +183,7 @@ impl State { /// If expression contains `Constant` check is constant exists. /// Values doesn't check as it's just `Primitive Values`. /// Also check all expression tree branches. - /// If ConstantValue doesn't exist add error to `Error State` and `return` false result. + /// If `ConstantValue` doesn't exist add error to `Error State` and `return` false result. pub fn check_constant_value_expression( &mut self, data: &Option<(ast::ExpressionOperations, Box>)>, @@ -207,7 +208,7 @@ impl State { } self.check_constant_value_expression(&child_data.operation) } - _ => true, + ast::ConstantValue::Value(_) => true, } } else { true @@ -278,6 +279,60 @@ impl State { .function_declaration(data.clone().into()); } + /// Init function parameters. + /// It's init function parameters as values, same as let-binding. + /// And add instructions to `SemanticStack`. + fn init_func_params( + &mut self, + function_state: &Rc>, + fn_params: &Vec>, + ) { + for fn_param in fn_params { + let func_param: FunctionParameter = fn_param.clone().into(); + let arg_name = func_param.clone().to_string(); + + // Find value in current state and parent states + let value = function_state + .borrow() + .get_value_name(&arg_name.clone().into()); + // Calculate `inner_name` as unique for current and all parent states + let inner_name: InnerValueName = if value.is_none() { + // if value not found in all states check and set + // `inner_value` from value name + // NOTE: value number not incremented + arg_name.clone().into() + } else { + // Function parameter name can't be with the same name. + // Produce error + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::FunctionArgumentNameDuplicated, + arg_name, + CodeLocation::new(1, 1), + )); + return; + }; + // Set value parameters + let value = Value { + inner_name: inner_name.clone(), + inner_type: func_param.parameter_type.clone(), + mutable: false, + alloca: false, + malloc: false, + }; + // Value inserted only to current state by Value name and Value data + function_state + .borrow_mut() + .values + .insert(arg_name.into(), value.clone()); + // Set `inner_name` to current state and all parent states + function_state + .borrow_mut() + .set_inner_value_name(&inner_name); + + function_state.borrow_mut().function_arg(value, func_param); + } + } + /// Function body analyze. /// It is basic execution entity for program flow. /// It's operate sub analyze for function elements. It's contain @@ -286,6 +341,8 @@ impl State { // Init empty function body state let body_state = Rc::new(RefCell::new(BlockState::new(None))); self.add_state_context(body_state.clone()); + // Init function parameters - add to SemanticStackContext + self.init_func_params(&body_state, &data.parameters); // Flag to indicate is function return called let mut return_is_called = false; // Fetch function elements and gather errors @@ -308,7 +365,7 @@ impl State { self.function_call(fn_call, &body_state); } ast::BodyStatement::If(if_condition) => { - self.if_condition(if_condition, &body_state, None, None); + self.if_condition(if_condition, &body_state, &None, None); } ast::BodyStatement::Loop(loop_statement) => { self.loop_statement(loop_statement, &body_state); @@ -455,9 +512,7 @@ impl State { let bind_data: Binding = data.clone().into(); // Find value in current state and parent states - let value = if let Some(val) = function_state.borrow().get_value_name(&bind_data.name) { - val - } else { + let Some(value) = function_state.borrow().get_value_name(&bind_data.name) else { self.add_error(error::StateErrorResult::new( error::StateErrorKind::ValueNotFound, bind_data.to_string(), @@ -569,16 +624,14 @@ impl State { )); return function_body_state.borrow().last_register_number; } - match left_res.expr_type { - Type::Primitive(_) => (), - _ => { - self.add_error(error::StateErrorResult::new( - error::StateErrorKind::ConditionExpressionNotSupported, - left_res.expr_type.to_string(), - data.left.left.location(), - )); - return function_body_state.borrow().last_register_number; - } + if let Type::Primitive(_) = left_res.expr_type { + } else { + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ConditionExpressionNotSupported, + left_res.expr_type.to_string(), + data.left.left.location(), + )); + return function_body_state.borrow().last_register_number; } // Increment register @@ -619,7 +672,7 @@ impl State { /// # If-condition body /// Analyze body for ant if condition: /// - if, else, if-else - /// NOTE: label_end - is always already exists + /// NOTE: `label_end` - is always already exists /// ## Return /// Return body statement "return" status pub fn if_condition_body( @@ -630,7 +683,7 @@ impl State { label_loop: Option<(&LabelName, &LabelName)>, ) -> bool { let mut return_is_called = false; - for body in body.iter() { + for body in body { if return_is_called { self.add_error(error::StateErrorResult::new( error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, @@ -652,7 +705,7 @@ impl State { self.if_condition( if_condition, if_body_state, - Some(label_end.clone()), + &Some(label_end.clone()), label_loop, ); } @@ -691,7 +744,7 @@ impl State { let mut return_is_called = false; let mut break_is_called = false; let mut continue_is_called = false; - for body in body.iter() { + for body in body { if return_is_called { self.add_error(error::StateErrorResult::new( error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, @@ -728,7 +781,7 @@ impl State { self.if_condition( if_condition, if_body_state, - Some(label_if_end.clone()), + &Some(label_if_end.clone()), Some((label_loop_start, label_loop_end)), ); } @@ -835,16 +888,20 @@ impl State { /// context, and main goal is to end all of if-condition nodes in /// the same flow with same `if-end` label. It's especially important /// for `else-if` condition. + /// + /// ## Panics + /// `label_loop` is must 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 pub fn if_condition( &mut self, data: &ast::IfStatement<'_>, function_body_state: &Rc>, - label_end: Option, + label_end: &Option, label_loop: Option<(&LabelName, &LabelName)>, ) { // It can't contain `else` and `if-else` on the same time - if data.else_statement.is_some() && data.else_if_statement.is_some() { - let stm = data.else_if_statement.clone().unwrap(); + if let (Some(_), Some(stm)) = (&data.else_statement, &data.else_if_statement) { self.add_error(error::StateErrorResult::new( error::StateErrorKind::IfElseDuplicated, String::from("if-condition"), @@ -877,7 +934,7 @@ impl State { |label| label, ); // 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_set_label_if_end = label_end.is_some(); let is_else = data.else_statement.is_some() || data.else_if_statement.is_some(); // Analyse if-conditions @@ -966,7 +1023,7 @@ impl State { self.if_condition( else_if_statement, function_body_state, - Some(label_if_end.clone()), + &Some(label_if_end.clone()), label_loop, ); } @@ -1017,7 +1074,7 @@ impl State { let mut return_is_called = false; let mut break_is_called = false; let mut continue_is_called = false; - for body in data.iter() { + for body in data { if return_is_called { self.add_error(error::StateErrorResult::new( error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated, @@ -1053,7 +1110,7 @@ impl State { ast::LoopBodyStatement::If(if_condition) => self.if_condition( if_condition, &loop_body_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ), ast::LoopBodyStatement::Loop(loop_statement) => { @@ -1270,7 +1327,7 @@ impl State { let last_register_number = body_state.borrow().last_register_number; body_state.borrow_mut().expression_struct_value( val.clone(), - attributes.clone().attr_index, + attributes.attr_index, last_register_number, ); @@ -1371,7 +1428,7 @@ impl State { ast::ExpressionValue::Expression(Box::new(ast::Expression { expression_value: data.expression_value, operation: Some(( - op.clone(), + op, Box::new(ast::Expression { expression_value: expr.expression_value, operation: None, diff --git a/src/types/block_state.rs b/src/types/block_state.rs index 4bb0502..1119344 100644 --- a/src/types/block_state.rs +++ b/src/types/block_state.rs @@ -2,7 +2,7 @@ //! Block state Semantic types. use super::semantic::SemanticStack; -use super::{Constant, Function, InnerValueName, LabelName, Value, ValueName}; +use super::{Constant, Function, FunctionParameter, InnerValueName, LabelName, Value, ValueName}; use crate::types::condition::{Condition, LogicCondition}; use crate::types::expression::{ExpressionOperations, ExpressionResult}; use crate::types::semantic::SemanticContext; @@ -49,6 +49,7 @@ pub struct BlockState { impl BlockState { /// Init block state with optional `parent` state + #[must_use] pub fn new(parent: Option>>) -> Self { // Get values from parent let (last_register_number, inner_values_name, labels, manual_return) = @@ -76,6 +77,7 @@ impl BlockState { } } + #[must_use] pub fn get_context(&self) -> SemanticStack { self.context.clone() } @@ -95,7 +97,7 @@ impl BlockState { } /// Get child Block state - pub fn set_child(&mut self, child: Rc>) { + pub fn set_child(&mut self, child: Rc>) { self.children.push(child); } @@ -108,6 +110,7 @@ impl BlockState { } /// Check is `inner_value_name` exist in current and parent states + #[must_use] pub fn is_inner_value_name_exist(&self, name: &InnerValueName) -> bool { if self.inner_values_name.contains(name) { return true; @@ -119,6 +122,7 @@ impl BlockState { /// Get `Value` by value name from current state. /// If not found on current state - recursively find in parent states. + #[must_use] pub fn get_value_name(&self, name: &ValueName) -> Option { if let Some(val) = self.values.get(name) { return Some(val.clone()); @@ -129,6 +133,7 @@ impl BlockState { } /// Check is label name exist in current and parent states + #[must_use] pub fn is_label_name_exist(&self, name: &LabelName) -> bool { if self.labels.contains(name) { return true; @@ -147,10 +152,11 @@ impl BlockState { } /// Set attribute counter - increment, if counter exist. + #[must_use] pub fn set_attr_counter(val: &str) -> String { let val_attr: Vec<&str> = val.split('.').collect(); if val_attr.len() == 2 { - let i: u64 = val_attr[1].parse().expect("expect integer"); + let i: u64 = val_attr[1].parse().unwrap_or_default(); format!("{}.{:?}", val_attr[0], i + 1) } else { format!("{}.0", val_attr[0]) @@ -179,6 +185,7 @@ impl BlockState { /// Get next `inner_value_name` by name counter for current and /// parent states. The `inner_value_name` should always be unique. + #[must_use] pub fn get_next_inner_name(&self, val: &InnerValueName) -> InnerValueName { // Increment inner value name counter for shadowed variable let name: InnerValueName = Self::set_attr_counter(&val.to_string()).into(); @@ -225,7 +232,7 @@ impl SemanticContext for BlockState { if let Some(parent) = &self.parent { parent .borrow_mut() - .expression_struct_value(expression, index, register_number) + .expression_struct_value(expression, index, register_number); } } @@ -394,4 +401,11 @@ impl SemanticContext for BlockState { .if_condition_logic(label_if_begin, label_if_end, result_register); } } + + fn function_arg(&mut self, value: Value, func_arg: FunctionParameter) { + self.context.function_arg(value.clone(), func_arg.clone()); + if let Some(parent) = &self.parent { + parent.borrow_mut().function_arg(value, func_arg); + } + } } diff --git a/src/types/error.rs b/src/types/error.rs index 50b87f6..aa996c4 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -31,6 +31,7 @@ pub enum StateErrorKind { ForbiddenCodeAfterReturnDeprecated, ForbiddenCodeAfterContinueDeprecated, ForbiddenCodeAfterBreakDeprecated, + FunctionArgumentNameDuplicated, } /// State error location. Useful to determine location of error @@ -49,6 +50,7 @@ pub struct StateErrorResult { } impl StateErrorResult { + #[must_use] pub const fn new(kind: StateErrorKind, value: String, location: CodeLocation) -> Self { Self { kind, @@ -59,8 +61,8 @@ impl StateErrorResult { } impl StateErrorResult { - #[allow(dead_code)] /// Get state trace data from error result as string + #[must_use] pub fn trace_state(&self) -> String { format!( "[{:?}] for value {:?} at: {:?}:{:?}", diff --git a/src/types/mod.rs b/src/types/mod.rs index 2573a65..79dc610 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -418,7 +418,7 @@ impl ToString for PrimitiveValue { Self::F64(val) => val.clone().to_string(), Self::Bool(val) => val.to_string(), Self::String(s) => s.clone(), - Self::Char(c) => format!("{}", c), + Self::Char(c) => format!("{c}"), Self::Ptr => "ptr".to_string(), Self::None => "None".to_string(), } diff --git a/src/types/semantic.rs b/src/types/semantic.rs index 91eea2e..e807890 100644 --- a/src/types/semantic.rs +++ b/src/types/semantic.rs @@ -5,7 +5,7 @@ use super::condition::{Condition, LogicCondition}; use super::expression::{ExpressionOperations, ExpressionResult}; use super::types::StructTypes; -use super::{Constant, Function, FunctionStatement, LabelName, Value}; +use super::{Constant, Function, FunctionParameter, FunctionStatement, LabelName, Value}; pub trait GlobalSemanticContext { fn function_declaration(&mut self, fn_decl: FunctionStatement); @@ -60,6 +60,7 @@ pub trait SemanticContext { label_if_end: LabelName, result_register: u64, ); + fn function_arg(&mut self, value: Value, func_arg: FunctionParameter); } /// # Semantic stack @@ -69,6 +70,7 @@ pub struct SemanticStack(Vec); impl SemanticStack { /// Init Semantic stack + #[must_use] pub fn new() -> Self { Self::default() } @@ -79,6 +81,7 @@ impl SemanticStack { } /// Get all context stack data as array data + #[must_use] pub fn get(self) -> Vec { self.0 } @@ -319,9 +322,9 @@ impl SemanticContext for SemanticStack { /// Result of calculation stored to `register_number`. /// /// ## Parameters - /// - left_register_result - result of left condition - /// - right_register_result - result of right condition - /// - register_number - register to store instruction result + /// - `left_register_result` - result of left condition + /// - `right_register_result` - result of right condition + /// - `register_number` - register to store instruction result fn logic_condition( &mut self, logic_condition: LogicCondition, @@ -363,6 +366,16 @@ impl SemanticContext for SemanticStack { result_register, }); } + + /// Push Context to the stack as `function argument` data. + /// This instruction should allocate pointer (if argument type is + /// not Ptr) and store argument value to the pointer. + /// + /// ## Parameters + /// - `func_arg` - function parameter data + fn function_arg(&mut self, value: Value, func_arg: FunctionParameter) { + self.push(SemanticStackContext::FunctionArg { value, func_arg }); + } } /// # Semantic stack Context @@ -448,4 +461,8 @@ pub enum SemanticStackContext { label_if_end: LabelName, result_register: u64, }, + FunctionArg { + value: Value, + func_arg: FunctionParameter, + }, } diff --git a/src/types/types.rs b/src/types/types.rs index 7bbd949..8bbb236 100644 --- a/src/types/types.rs +++ b/src/types/types.rs @@ -56,11 +56,13 @@ pub enum Type { impl Type { /// Get type name + #[must_use] pub fn name(&self) -> TypeName { self.to_string().into() } /// Get structure type if it is + #[must_use] pub fn get_struct(&self) -> Option { match self { Self::Struct(ty) => Some(ty.clone()), @@ -231,7 +233,7 @@ impl From> for StructTypes { for (index, val) in value.attributes.iter().enumerate() { let name = (*val.attr_name.fragment()).to_string(); let mut v: StructAttributeType = val.clone().into(); - v.attr_index = index as u32; + v.attr_index = u32::try_from(index).unwrap_or_default(); res.insert(name.into(), v); } res diff --git a/tests/if_tests.rs b/tests/if_tests.rs index 9d00a67..d865ba9 100644 --- a/tests/if_tests.rs +++ b/tests/if_tests.rs @@ -305,7 +305,7 @@ fn check_if_and_else_if_statement_duplicate() { 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.state.if_condition(&if_stmt, &block_state, &None, None); assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); assert!( t.check_error(StateErrorKind::IfElseDuplicated), @@ -773,9 +773,10 @@ fn else_if_statement() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); + println!("{:#?}", t.state.errors); assert!(t.is_empty_error()); let main_ctx = block_state.borrow().get_context().clone().get(); @@ -981,7 +982,7 @@ fn if_body_statements() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); assert!(t.is_empty_error()); @@ -1268,7 +1269,7 @@ fn if_loop_body_statements() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); assert!(t.is_empty_error()); @@ -1495,7 +1496,7 @@ fn if_loop_body_instructions_after_return() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); @@ -1547,7 +1548,7 @@ fn else_if_loop_body_instructions_after_return() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); @@ -1596,7 +1597,7 @@ fn if_body_instructions_after_return() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); @@ -1648,7 +1649,7 @@ fn if_else_body_instructions_after_return() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); @@ -1694,7 +1695,7 @@ fn if_loop_body_instructions_after_break() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); @@ -1739,7 +1740,7 @@ fn if_loop_body_instructions_after_continue() { t.state.if_condition( &if_stmt, &block_state, - None, + &None, Some((&label_loop_begin, &label_loop_end)), ); assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); diff --git a/tests/main_tests.rs b/tests/main_tests.rs index c9ccf9f..9ee1c3f 100644 --- a/tests/main_tests.rs +++ b/tests/main_tests.rs @@ -1,6 +1,7 @@ use crate::utils::SemanticTest; use semantic_analyzer::ast::{self, GetName, Ident}; use semantic_analyzer::types::error::StateErrorKind; +use semantic_analyzer::types::expression::ExpressionOperations; use semantic_analyzer::types::{ expression::{ExpressionResult, ExpressionResultValue}, semantic::SemanticStackContext, @@ -544,3 +545,167 @@ fn if_return_from_function() { } ); } + +#[test] +fn function_args_and_let_binding() { + let mut t = SemanticTest::new(); + let body_let_binding = ast::BodyStatement::LetBinding(ast::LetBinding { + name: ast::ValueName::new(Ident::new("y")), + mutable: true, + value_type: Some(ast::Type::Primitive(ast::PrimitiveTypes::U64)), + value: Box::new(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(23)), + operation: Some(( + ast::ExpressionOperations::Plus, + Box::new(ast::Expression { + expression_value: ast::ExpressionValue::ValueName(ast::ValueName::new( + Ident::new("x"), + )), + operation: None, + }), + )), + }), + }); + let body_expr_return = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::ValueName(ast::ValueName::new(Ident::new("y"))), + operation: None, + }); + + let fn_param1 = ast::FunctionParameter { + name: ast::ParameterName::new(Ident::new("x")), + parameter_type: ast::Type::Primitive(ast::PrimitiveTypes::U64), + }; + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![fn_param1.clone()], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U64), + body: vec![body_let_binding, 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()); + + assert_eq!(t.state.context.len(), 1); + let ctx = t.state.context[0].borrow(); + assert!(ctx.children.is_empty()); + assert!(ctx.parent.is_none()); + + let stm_ctx = ctx.get_context().get(); + let ty = Type::Primitive(PrimitiveTypes::U64); + let value_x = Value { + inner_name: "x".into(), + inner_type: ty.clone(), + mutable: false, + alloca: false, + malloc: false, + }; + let value_y = Value { + inner_name: "y.0".into(), + inner_type: ty.clone(), + mutable: true, + alloca: false, + malloc: false, + }; + assert_eq!( + stm_ctx[0], + SemanticStackContext::FunctionArg { + value: value_x.clone(), + func_arg: fn_param1.into() + } + ); + assert_eq!( + stm_ctx[1], + SemanticStackContext::ExpressionValue { + expression: value_x, + register_number: 1 + } + ); + assert_eq!( + stm_ctx[2], + SemanticStackContext::ExpressionOperation { + operation: ExpressionOperations::Plus, + left_value: ExpressionResult { + expr_type: ty.clone(), + expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::U64(23)) + }, + right_value: ExpressionResult { + expr_type: ty.clone(), + expr_value: ExpressionResultValue::Register(1) + }, + register_number: 2 + } + ); + assert_eq!( + stm_ctx[3], + SemanticStackContext::LetBinding { + let_decl: value_y.clone(), + expr_result: ExpressionResult { + expr_type: ty.clone(), + expr_value: ExpressionResultValue::Register(2), + }, + } + ); + assert_eq!( + stm_ctx[4], + SemanticStackContext::ExpressionValue { + expression: value_y, + register_number: 3 + } + ); + assert_eq!( + stm_ctx[5], + SemanticStackContext::ExpressionFunctionReturn { + expr_result: ExpressionResult { + expr_type: ty.clone(), + expr_value: ExpressionResultValue::Register(3), + } + } + ); + + // Verify global entities + 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_eq!(fn_state.parameters.len(), 1); + assert_eq!(fn_state.parameters[0], ty); + let global_ctx = t.state.global.context.get(); + assert_eq!(global_ctx.len(), 1); + assert_eq!( + global_ctx[0], + SemanticStackContext::FunctionDeclaration { + fn_decl: fn1.into() + } + ); +} + +#[test] +fn function_args_duplication() { + let mut t = SemanticTest::new(); + let body_expr_return = ast::BodyStatement::Expression(ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(ast::PrimitiveValue::U64(10)), + operation: None, + }); + + let fn_param1 = ast::FunctionParameter { + name: ast::ParameterName::new(Ident::new("x")), + parameter_type: ast::Type::Primitive(ast::PrimitiveTypes::U64), + }; + let fn_param2 = fn_param1.clone(); + let fn1 = ast::FunctionStatement { + name: ast::FunctionName::new(Ident::new("fn1")), + parameters: vec![fn_param1.clone(), fn_param2.clone()], + result_type: ast::Type::Primitive(ast::PrimitiveTypes::U64), + body: vec![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.check_errors_len(1)); + assert!(t.check_error(StateErrorKind::FunctionArgumentNameDuplicated)); +} diff --git a/tests/state_tests.rs b/tests/state_tests.rs index c33087e..802fb91 100644 --- a/tests/state_tests.rs +++ b/tests/state_tests.rs @@ -9,8 +9,8 @@ use semantic_analyzer::types::{ block_state::BlockState, semantic::SemanticStack, types::{PrimitiveTypes, Type}, - Constant, ConstantExpression, ConstantValue, Function, InnerValueName, LabelName, Value, - ValueName, + Constant, ConstantExpression, ConstantValue, Function, FunctionParameter, InnerValueName, + LabelName, Value, ValueName, }; use std::cell::RefCell; use std::rc::Rc; @@ -277,7 +277,12 @@ fn block_state_instructions_with_parent() { let logic_condition = LogicCondition::And; bst.logic_condition(logic_condition, 1, 2, 3); bst.if_condition_logic(label.clone(), label, 1); + let func_arg = FunctionParameter { + name: ast::ParameterName::new(Ident::new("x")).into(), + parameter_type: Type::Primitive(PrimitiveTypes::Ptr), + }; + bst.function_arg(val, func_arg); let parent_ctx = parent_bst.borrow().get_context().get(); - assert_eq!(parent_ctx.len(), 16); + assert_eq!(parent_ctx.len(), 17); }