From 17345b2649f9bc931c89298c820f9ad43fde7977 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Sat, 25 Nov 2023 19:07:12 +0100 Subject: [PATCH 1/5] Extend registed for SemanticStackContext. Fixed tests --- Cargo.toml | 2 +- src/semantic.rs | 33 +++++++++++++++------ src/types/semantic.rs | 37 ++++++++++++++++++----- tests/expressions_tests.rs | 61 ++++++++++++++++++++++---------------- tests/if_tests.rs | 6 ++++ tests/loop_tests.rs | 3 ++ tests/main_tests.rs | 4 ++- 7 files changed, 102 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 385ccb7..29245f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "semantic-analyzer" -version = "0.2.6" +version = "0.3.0" 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/semantic.rs b/src/semantic.rs index 8f53c42..409f975 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -533,8 +533,14 @@ impl State { params.push(expr_result); } + // Result of function call is stored to register + body_state.borrow_mut().inc_register(); + let last_register_number = body_state.borrow().last_register_number; // Store always result to register even for void result - body_state.borrow_mut().context.call(func_data, params); + body_state + .borrow_mut() + .context + .call(func_data, params, last_register_number); Some(fn_type) } @@ -1169,18 +1175,21 @@ impl State { ast::ExpressionValue::ValueName(value) => { // Get value from block state let value_from_state = body_state.borrow_mut().get_value_name(&value.name().into()); + // Register contains result + body_state.borrow_mut().inc_register(); + let last_register_number = body_state.borrow().last_register_number; // First check value in body state let ty = if let Some(val) = value_from_state { body_state .borrow_mut() .context - .expression_value(val.clone()); + .expression_value(val.clone(), last_register_number); val.inner_type } else if let Some(const_val) = self.global.constants.get(&value.name().into()) { body_state .borrow_mut() .context - .expression_const(const_val.clone()); + .expression_const(const_val.clone(), last_register_number); const_val.constant_type.clone() } else { // If value doesn't exist in State or as Constant @@ -1192,7 +1201,6 @@ impl State { return None; }; // Return result as register - body_state.borrow_mut().inc_register(); ExpressionResult { expr_type: ty, expr_value: ExpressionResultValue::Register( @@ -1274,10 +1282,14 @@ impl State { })? .clone(); - body_state - .borrow_mut() - .context - .expression_struct_value(val.clone(), attributes.clone().attr_index); + // Register contains result + body_state.borrow_mut().inc_register(); + let last_register_number = body_state.borrow().last_register_number; + body_state.borrow_mut().context.expression_struct_value( + val.clone(), + attributes.clone().attr_index, + last_register_number, + ); body_state.borrow_mut().inc_register(); ExpressionResult { @@ -1303,14 +1315,17 @@ impl State { // Do not fetch other expression flow if type is wrong return None; } + // Expression operation is set to register + body_state.borrow_mut().inc_register(); + let last_register_number = body_state.borrow().last_register_number; // Call expression operation for: OP(left_value, right_value) body_state.borrow_mut().context.expression_operation( op.clone().into(), left_value.clone(), right_value.clone(), + last_register_number, ); // Expression result value for Operations is always should be "register" - body_state.borrow_mut().inc_register(); ExpressionResult { expr_type: right_value.expr_type, expr_value: ExpressionResultValue::Register( diff --git a/src/types/semantic.rs b/src/types/semantic.rs index 9121ddd..c987d5f 100644 --- a/src/types/semantic.rs +++ b/src/types/semantic.rs @@ -30,18 +30,28 @@ impl SemanticStack { } /// Push Context to the stack as expression value data - pub fn expression_value(&mut self, expression: Value) { - self.push(SemanticStackContext::ExpressionValue { expression }); + pub fn expression_value(&mut self, expression: Value, register_number: u64) { + self.push(SemanticStackContext::ExpressionValue { + expression, + register_number, + }); } /// Push Context to the stack as expression const data - pub fn expression_const(&mut self, expression: Constant) { - self.push(SemanticStackContext::ExpressionConst { expression }); + pub fn expression_const(&mut self, expression: Constant, register_number: u64) { + self.push(SemanticStackContext::ExpressionConst { + expression, + register_number, + }); } /// Push Context to the stack as expression struct value data - pub fn expression_struct_value(&mut self, expression: Value, index: u32) { - self.push(SemanticStackContext::ExpressionStructValue { expression, index }); + pub fn expression_struct_value(&mut self, expression: Value, index: u32, register_number: u64) { + self.push(SemanticStackContext::ExpressionStructValue { + expression, + index, + register_number, + }); } /// Push Context to the stack as expression operation data @@ -50,17 +60,23 @@ impl SemanticStack { operation: ExpressionOperations, left_value: ExpressionResult, right_value: ExpressionResult, + register_number: u64, ) { self.push(SemanticStackContext::ExpressionOperation { operation, left_value, right_value, + register_number, }); } /// Push Context to the stack as function call data - pub fn call(&mut self, call: Function, params: Vec) { - self.push(SemanticStackContext::Call { call, params }); + pub fn call(&mut self, call: Function, params: Vec, register_number: u64) { + self.push(SemanticStackContext::Call { + call, + params, + register_number, + }); } /// Push Context to the stack as let-binding data @@ -164,22 +180,27 @@ impl SemanticStack { pub enum SemanticStackContext { ExpressionValue { expression: Value, + register_number: u64, }, ExpressionConst { expression: Constant, + register_number: u64, }, ExpressionStructValue { expression: Value, index: u32, + register_number: u64, }, ExpressionOperation { operation: ExpressionOperations, left_value: ExpressionResult, right_value: ExpressionResult, + register_number: u64, }, Call { call: Function, params: Vec, + register_number: u64, }, LetBinding { let_decl: Value, diff --git a/tests/expressions_tests.rs b/tests/expressions_tests.rs index a63c0d8..9d53e57 100644 --- a/tests/expressions_tests.rs +++ b/tests/expressions_tests.rs @@ -27,6 +27,7 @@ fn set_result_type( left: u64, reg_right: bool, right: u64, + register_number: u64, ) -> SemanticStackContext { let left_val = if reg_left { ExpressionResultValue::Register(left) @@ -48,6 +49,7 @@ fn set_result_type( expr_type: Type::Primitive(PrimitiveTypes::U16), expr_value: right_val, }, + register_number, } } @@ -404,7 +406,10 @@ fn expression_value_name_exists() { assert_eq!(state.len(), 1); assert_eq!( state[0], - SemanticStackContext::ExpressionValue { expression: value } + SemanticStackContext::ExpressionValue { + expression: value, + register_number: 1 + } ); assert!(t.is_empty_error()); } @@ -437,7 +442,10 @@ fn expression_const_exists() { assert_eq!(state.len(), 1); assert_eq!( state[0], - SemanticStackContext::ExpressionConst { expression: value } + SemanticStackContext::ExpressionConst { + expression: value, + register_number: 1 + } ); assert!(t.is_empty_error()); } @@ -768,7 +776,7 @@ fn expression_struct_value() { assert!(t.is_empty_error()); let res = t.state.expression(&expr, &block_state).unwrap(); assert!(t.is_empty_error()); - assert_eq!(res.expr_value, ExpressionResultValue::Register(1)); + assert_eq!(res.expr_value, ExpressionResultValue::Register(2)); assert_eq!(res.expr_type, Type::Primitive(PrimitiveTypes::Bool)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 1); @@ -776,7 +784,8 @@ fn expression_struct_value() { state[0], SemanticStackContext::ExpressionStructValue { expression: value, - index: 0 + index: 0, + register_number: 1 } ); } @@ -824,7 +833,7 @@ fn expression_func_call() { assert_eq!( res, ExpressionResult { - expr_value: ExpressionResultValue::Register(1), + expr_value: ExpressionResultValue::Register(2), expr_type: Type::Primitive(PrimitiveTypes::Ptr) } ); @@ -839,6 +848,7 @@ fn expression_func_call() { parameters: vec![] }, params: vec![], + register_number: 1 } ); } @@ -903,6 +913,7 @@ fn expression_operation() { expr_type: Type::Primitive(PrimitiveTypes::Char), expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::Char('b')) }, + register_number: 1 } ); } @@ -974,11 +985,11 @@ fn expression_multiple_operation1() { assert_eq!(res.expr_value, ExpressionResultValue::Register(5)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 5); - assert_eq!(state[0], set_result_type(Plus, false, 1, false, 2)); - assert_eq!(state[1], set_result_type(Multiply, true, 1, false, 3)); - assert_eq!(state[2], set_result_type(Minus, true, 2, false, 4)); - assert_eq!(state[3], set_result_type(Multiply, false, 5, false, 6)); - assert_eq!(state[4], set_result_type(Minus, true, 3, true, 4)); + assert_eq!(state[0], set_result_type(Plus, false, 1, false, 2, 1)); + assert_eq!(state[1], set_result_type(Multiply, true, 1, false, 3, 2)); + assert_eq!(state[2], set_result_type(Minus, true, 2, false, 4, 3)); + assert_eq!(state[3], set_result_type(Multiply, false, 5, false, 6, 4)); + assert_eq!(state[4], set_result_type(Minus, true, 3, true, 4, 5)); assert!(t.is_empty_error()); } @@ -1034,11 +1045,11 @@ fn expression_multiple_operation2() { assert_eq!(res.expr_value, ExpressionResultValue::Register(5)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 5); - assert_eq!(state[0], set_result_type(Plus, false, 100, false, 2)); - assert_eq!(state[1], set_result_type(Minus, false, 3, false, 4)); - assert_eq!(state[2], set_result_type(Multiply, false, 5, false, 6)); - assert_eq!(state[3], set_result_type(Minus, true, 2, true, 3)); - assert_eq!(state[4], set_result_type(Multiply, true, 1, true, 4)); + assert_eq!(state[0], set_result_type(Plus, false, 100, false, 2, 1)); + assert_eq!(state[1], set_result_type(Minus, false, 3, false, 4, 2)); + assert_eq!(state[2], set_result_type(Multiply, false, 5, false, 6, 3)); + assert_eq!(state[3], set_result_type(Minus, true, 2, true, 3, 4)); + assert_eq!(state[4], set_result_type(Multiply, true, 1, true, 4, 5)); assert!(t.is_empty_error()); } @@ -1067,8 +1078,8 @@ fn expression_multiple_operation_simple1() { assert_eq!(res.expr_value, ExpressionResultValue::Register(2)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 2); - assert_eq!(state[0], set_result_type(Multiply, false, 5, false, 6)); - assert_eq!(state[1], set_result_type(Minus, false, 100, true, 1)); + assert_eq!(state[0], set_result_type(Multiply, false, 5, false, 6, 1)); + assert_eq!(state[1], set_result_type(Minus, false, 100, true, 1, 2)); assert!(t.is_empty_error()); } @@ -1094,8 +1105,8 @@ fn expression_multiple_operation_simple2() { assert_eq!(res.expr_value, ExpressionResultValue::Register(2)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 2); - assert_eq!(state[0], set_result_type(Multiply, false, 20, false, 5)); - assert_eq!(state[1], set_result_type(Minus, true, 1, false, 40)); + assert_eq!(state[0], set_result_type(Multiply, false, 20, false, 5, 1)); + assert_eq!(state[1], set_result_type(Minus, true, 1, false, 40, 2)); assert!(t.is_empty_error()); } @@ -1125,9 +1136,9 @@ fn expression_multiple_operation_simple3() { assert_eq!(res.expr_value, ExpressionResultValue::Register(3)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 3); - assert_eq!(state[0], set_result_type(Multiply, false, 20, false, 4)); - assert_eq!(state[1], set_result_type(Minus, true, 1, false, 40)); - assert_eq!(state[2], set_result_type(Minus, true, 2, false, 5)); + assert_eq!(state[0], set_result_type(Multiply, false, 20, false, 4, 1)); + assert_eq!(state[1], set_result_type(Minus, true, 1, false, 40, 2)); + assert_eq!(state[2], set_result_type(Minus, true, 2, false, 5, 3)); assert!(t.is_empty_error()); } @@ -1161,8 +1172,8 @@ fn expression_multiple_operation_simple4() { assert_eq!(res.expr_value, ExpressionResultValue::Register(3)); let state = block_state.borrow().context.clone().get(); assert_eq!(state.len(), 3); - assert_eq!(state[0], set_result_type(Multiply, false, 5, false, 6)); - assert_eq!(state[1], set_result_type(Minus, false, 100, true, 1)); - assert_eq!(state[2], set_result_type(Minus, true, 2, false, 15)); + assert_eq!(state[0], set_result_type(Multiply, false, 5, false, 6, 1)); + assert_eq!(state[1], set_result_type(Minus, false, 100, true, 1, 2)); + assert_eq!(state[2], set_result_type(Minus, true, 2, false, 15, 3)); assert!(t.is_empty_error()); } diff --git a/tests/if_tests.rs b/tests/if_tests.rs index 580cc20..da36875 100644 --- a/tests/if_tests.rs +++ b/tests/if_tests.rs @@ -983,6 +983,7 @@ fn if_body_statements() { parameters: vec![], }, params: vec![], + register_number: 1 } ); assert_eq!( @@ -1033,6 +1034,7 @@ fn if_body_statements() { parameters: vec![], }, params: vec![], + register_number: 2 } ); assert_eq!( @@ -1070,6 +1072,7 @@ fn if_body_statements() { parameters: vec![], }, params: vec![], + register_number: 3 } ); assert_eq!( @@ -1257,6 +1260,7 @@ fn if_loop_body_statements() { parameters: vec![], }, params: vec![], + register_number: 1 } ); assert_eq!( @@ -1307,6 +1311,7 @@ fn if_loop_body_statements() { parameters: vec![], }, params: vec![], + register_number: 2 } ); assert_eq!( @@ -1343,6 +1348,7 @@ fn if_loop_body_statements() { parameters: vec![], }, params: vec![], + register_number: 3 } ); assert_eq!( diff --git a/tests/loop_tests.rs b/tests/loop_tests.rs index 2d8c6b3..38b408a 100644 --- a/tests/loop_tests.rs +++ b/tests/loop_tests.rs @@ -214,6 +214,7 @@ fn loop_statements() { parameters: vec![], }, params: vec![], + register_number: 1, } ); assert_eq!( @@ -261,6 +262,7 @@ fn loop_statements() { parameters: vec![], }, params: vec![], + register_number: 2 } ); assert_eq!( @@ -303,6 +305,7 @@ fn loop_statements() { parameters: vec![], }, params: vec![], + register_number: 3 } ); assert_eq!( diff --git a/tests/main_tests.rs b/tests/main_tests.rs index 663a378..764cc9a 100644 --- a/tests/main_tests.rs +++ b/tests/main_tests.rs @@ -193,6 +193,7 @@ fn main_run() { parameters: vec![], }, params: vec![], + register_number: 1 } ); @@ -247,6 +248,7 @@ fn main_run() { parameters: vec![], }, params: vec![], + register_number: 2 } ); assert_eq!( @@ -285,6 +287,7 @@ fn main_run() { parameters: vec![], }, params: vec![], + register_number: 3 } ); assert_eq!( @@ -349,7 +352,6 @@ fn double_return() { let fn_stm = ast::MainStatement::Function(fn1); let main_stm: ast::Main = vec![fn_stm]; t.state.run(&main_stm); - println!("{:#?}", t.state.errors); assert!(t.check_errors_len(2), "Errors: {:?}", t.state.errors.len()); assert!(t.check_error_index(0, StateErrorKind::ForbiddenCodeAfterReturnDeprecated)); assert!(t.check_error_index(1, StateErrorKind::ReturnAlreadyCalled)); From 294461ff9e1e4934536da1f9b7b1fa093826ab92 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Sun, 26 Nov 2023 12:41:39 +0100 Subject: [PATCH 2/5] Extend test coverage and fix clippy for new nightly --- tests/expressions_tests.rs | 16 ++++++++++++++++ tests/loop_tests.rs | 2 ++ tests/utils.rs | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/expressions_tests.rs b/tests/expressions_tests.rs index 9d53e57..30fd919 100644 --- a/tests/expressions_tests.rs +++ b/tests/expressions_tests.rs @@ -356,6 +356,22 @@ fn expression_ast_transform_primitive_struct_value() { assert_eq!(expr_struct_val.to_string(), "val"); } +#[test] +fn expression_ast_transform_expression() { + let val = ast::PrimitiveValue::Ptr; + let sub_expr = ast::Expression { + expression_value: ast::ExpressionValue::PrimitiveValue(val), + operation: None, + }; + let expr: Expression = ast::Expression { + expression_value: ast::ExpressionValue::Expression(Box::new(sub_expr)), + operation: None, + } + .into(); + format!("{expr:#?}"); + assert_eq!(expr.to_string(), "ptr"); +} + #[test] fn expression_value_name_not_found() { let block_state = Rc::new(RefCell::new(BlockState::new(None))); diff --git a/tests/loop_tests.rs b/tests/loop_tests.rs index 38b408a..6ecd911 100644 --- a/tests/loop_tests.rs +++ b/tests/loop_tests.rs @@ -63,6 +63,8 @@ fn loop_transform() { format!("{loop_stmts:#?}"); for loop_stmt in loop_stmts { let loop_stmt_into: LoopBodyStatement = loop_stmt.into(); + // For grcov + format!("{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()), diff --git a/tests/utils.rs b/tests/utils.rs index 81326c7..4da5f8d 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -35,7 +35,7 @@ impl SemanticTest { #[allow(dead_code)] pub fn check_error(&self, err_kind: StateErrorKind) -> bool { - self.state.errors.get(0).unwrap().kind == err_kind + self.state.errors.first().unwrap().kind == err_kind } #[allow(dead_code)] From b54cd7c2f7f977a6b8bc5c495159c03fe9dc6c36 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Mon, 27 Nov 2023 23:15:16 +0100 Subject: [PATCH 3/5] Extend README. Added register to if-condition-logic instructions. Added extra docs for instructions --- README.md | 14 +++++-- src/semantic.rs | 68 ++++++++++++++++++++---------- src/types/error.rs | 1 + src/types/semantic.rs | 97 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 144 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 81eb4d2..e759b7c 100644 --- a/README.md +++ b/README.md @@ -53,14 +53,14 @@ Supported condition expressions and condition expression correctness check. - **Building the Symbol Table**: For analyzing used the symbol table as data structure used by the semantic analyzer to keep track of symbols (variables, functions, constants) in the source code. Each entry in the symbol table contains the symbol's name, type, and scope related for block state, and other relevant information. -### Semantic State Tree +### ๐ŸŒณ Semantic State Tree The result of executing and passing stages of the semantic analyzer is: **Semantic State Tree**. This can be used for Intermediate Code Generation, for further passes semantic tree optimizations, linting, backend codegen (like LLVM) to target machine. -#### Structure of Semantic State Tree +#### ๐ŸŒฒ Structure of Semantic State Tree - **blocks state** and related block state child branches. It's a basic entity for scopes: variables, blocks (function, if, loop). @@ -87,7 +87,7 @@ However, parent elements cannot access child elements, which effectively limits All of that source data, that can be used for Intermediate Representation for next optimizations and compilers codegen. -### Subset of programming languages +### ๐Ÿงบ Subset of programming languages The input parameter for the analyzer is a predefined AST (abstract syntax tree). As a library for building AST and the only dependency @@ -104,4 +104,12 @@ analysis and source code parsing, it is recommended to use: [nom is a parser com AST displays the **Turing complete** programming language and contains all the necessary elements for this. +## ๐Ÿ›‹๏ธ Examples + +- ๐Ÿ”Ž There is the example implementation separate project [Toy Codegen](https://github.com/mrLSD/toy-codegen). +The project uses the `SemanticStack` results and converts them into **Code Generation** logic. Which clearly shows the +possibilities of using the results of the `semantic-analyzer-rs` `SemanticStackContext` results. LLVM is used as a +backend, [inkwell](https://github.com/TheDan64/inkwell) as a library for LLVM codegen, and compiled into an executable +program. The source of data is the AST structure itself. + ## MIT [LICENSE](LICENSE) diff --git a/src/semantic.rs b/src/semantic.rs index 409f975..c4236e5 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -545,12 +545,14 @@ impl State { } /// # condition-expression - /// Analyse condition operations. + /// Analyse condition operations. + /// ## Return + /// Return result register of `condition-expression` calculation. pub fn condition_expression( &mut self, data: &ast::ExpressionLogicCondition<'_>, function_body_state: &Rc>, - ) { + ) -> u64 { // Analyse left expression of left condition let left_expr = &data.left.left; let left_res = self.expression(left_expr, function_body_state); @@ -559,12 +561,15 @@ impl State { let right_expr = &data.left.right; let right_res = self.expression(right_expr, function_body_state); + // If some of the `left` or `right` expression is empty just return with error in the state let (Some(left_res), Some(right_res)) = (left_res, right_res) else { - return; + self.add_error(error::StateErrorResult::new( + error::StateErrorKind::ConditionIsEmpty, + format!("left={left_res:?}, right={right_res:?}"), + data.left.left.location(), + )); + return function_body_state.borrow().last_register_number; }; - // Unwrap result only after analysing - // let left_res = left_res?; - // let right_res = right_res?; // Currently strict type comparison if left_res.expr_type != right_res.expr_type { @@ -573,7 +578,7 @@ impl State { left_res.expr_type.to_string(), data.left.left.location(), )); - return; + return function_body_state.borrow().last_register_number; } match left_res.expr_type { Type::Primitive(_) => (), @@ -583,29 +588,44 @@ impl State { left_res.expr_type.to_string(), data.left.left.location(), )); - return; + return function_body_state.borrow().last_register_number; } } + // Increment register + function_body_state.borrow_mut().inc_register(); + // Codegen for left condition and set result to register function_body_state .borrow_mut() .context - .condition_expression(left_res, right_res, data.left.condition.clone().into()); + .condition_expression( + left_res, + right_res, + data.left.condition.clone().into(), + function_body_state.borrow_mut().last_register_number, + ); // Analyze right condition if let Some(right) = &data.right { + let left_register_result = function_body_state.borrow_mut().last_register_number; // Analyse recursively right part of condition - self.condition_expression(&right.1, function_body_state); + let right_register_result = self.condition_expression(&right.1, function_body_state); + + // Increment register + function_body_state.borrow_mut().inc_register(); // Stategen for logical condition for: left [LOGIC-OP] right // The result generated from registers, and stored to // new register - function_body_state - .borrow_mut() - .context - .logic_condition(right.0.clone().into()); + function_body_state.borrow_mut().context.logic_condition( + right.0.clone().into(), + left_register_result, + right_register_result, + function_body_state.borrow_mut().last_register_number, + ); } + function_body_state.borrow_mut().last_register_number } /// # If-condition body @@ -799,18 +819,20 @@ impl State { // If condition contains logic condition expression ast::IfCondition::Logic(expr_logic) => { // Analyse if-condition logic - self.condition_expression(expr_logic, if_body_state); + let result_register = self.condition_expression(expr_logic, if_body_state); // State for if-condition-logic with if-body start if is_else { - if_body_state - .borrow_mut() - .context - .if_condition_logic(label_if_begin.clone(), label_if_else.clone()); + if_body_state.borrow_mut().context.if_condition_logic( + label_if_begin.clone(), + label_if_else.clone(), + result_register, + ); } else { - if_body_state - .borrow_mut() - .context - .if_condition_logic(label_if_begin.clone(), label_if_end.clone()); + if_body_state.borrow_mut().context.if_condition_logic( + label_if_begin.clone(), + label_if_end.clone(), + result_register, + ); } } } diff --git a/src/types/error.rs b/src/types/error.rs index 6f9cfb3..50b87f6 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -26,6 +26,7 @@ pub enum StateErrorKind { TypeNotFound, WrongReturnType, ConditionExpressionWrongType, + ConditionIsEmpty, ConditionExpressionNotSupported, ForbiddenCodeAfterReturnDeprecated, ForbiddenCodeAfterContinueDeprecated, diff --git a/src/types/semantic.rs b/src/types/semantic.rs index c987d5f..fb5c50a 100644 --- a/src/types/semantic.rs +++ b/src/types/semantic.rs @@ -9,7 +9,7 @@ use super::types::StructTypes; use super::{Constant, Function, FunctionStatement, LabelName, Value}; /// # Semantic stack -/// Semantic stack represent stack of Semantic Context +/// Semantic stack represent stack of Semantic Context results #[derive(Debug, Default, Clone, PartialEq)] pub struct SemanticStack(Vec); @@ -29,7 +29,11 @@ impl SemanticStack { self.0 } - /// Push Context to the stack as expression value data + /// Push Context to the stack as expression value data. + /// + /// ## Parameters + /// - `expression` - contains expression value + /// - `register_number` - register to store result data pub fn expression_value(&mut self, expression: Value, register_number: u64) { self.push(SemanticStackContext::ExpressionValue { expression, @@ -37,7 +41,11 @@ impl SemanticStack { }); } - /// Push Context to the stack as expression const data + /// Push Context to the stack as expression const data. + /// + /// ## Parameters + /// - `expression` - contains expression constant + /// - `register_number` - register to store result data pub fn expression_const(&mut self, expression: Constant, register_number: u64) { self.push(SemanticStackContext::ExpressionConst { expression, @@ -45,7 +53,12 @@ impl SemanticStack { }); } - /// Push Context to the stack as expression struct value data + /// Push Context to the stack as expression struct value data. + /// + /// ## Parameters + /// - `expression` - contains expression value for specific `Structure` attribute + /// - `index` - represent attribute index in the `Structure` type + /// - `register_number` - register to store result data pub fn expression_struct_value(&mut self, expression: Value, index: u32, register_number: u64) { self.push(SemanticStackContext::ExpressionStructValue { expression, @@ -54,7 +67,15 @@ impl SemanticStack { }); } - /// Push Context to the stack as expression operation data + /// Push Context to the stack as expression operation data. + /// `expression_operation` imply operation between `left_value` and + /// `right_value` and store result to `register_number`. + /// + /// ## Parameters + /// - `operation` - specific operation + /// - `left_value` - left expression result + /// - `right_value` - right expression result + /// - `register_number` - register to store result of expression operation pub fn expression_operation( &mut self, operation: ExpressionOperations, @@ -71,6 +92,7 @@ impl SemanticStack { } /// Push Context to the stack as function call data + /// ## Parameters pub fn call(&mut self, call: Function, params: Vec, register_number: u64) { self.push(SemanticStackContext::Call { call, @@ -80,6 +102,7 @@ impl SemanticStack { } /// Push Context to the stack as let-binding data + /// ## Parameters pub fn let_binding(&mut self, let_decl: Value, expr_result: ExpressionResult) { self.push(SemanticStackContext::LetBinding { let_decl, @@ -88,46 +111,55 @@ impl SemanticStack { } /// Push Context to the stack as binding data + /// ## Parameters pub fn binding(&mut self, val: Value, expr_result: ExpressionResult) { self.push(SemanticStackContext::Binding { val, expr_result }); } /// Push Context to the stack as function declaration data + /// ## Parameters pub fn function_declaration(&mut self, fn_decl: FunctionStatement) { self.push(SemanticStackContext::FunctionDeclaration { fn_decl }); } /// Push Context to the stack as constant data + /// ## Parameters pub fn constant(&mut self, const_decl: Constant) { self.push(SemanticStackContext::Constant { const_decl }); } /// Push Context to the stack as types data + /// ## Parameters pub fn types(&mut self, type_decl: StructTypes) { self.push(SemanticStackContext::Types { type_decl }); } /// Push Context to the stack as expression function return data + /// ## Parameters pub fn expression_function_return(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::ExpressionFunctionReturn { expr_result }); } /// Push Context to the stack as `expression function return with label` data + /// ## Parameters pub fn expression_function_return_with_label(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::ExpressionFunctionReturnWithLabel { expr_result }); } /// Push Context to the stack as `set label` data + /// ## Parameters pub fn set_label(&mut self, label: LabelName) { self.push(SemanticStackContext::SetLabel { label }); } /// Push Context to the stack as `jump to` data + /// ## Parameters pub fn jump_to(&mut self, label: LabelName) { self.push(SemanticStackContext::JumpTo { label }); } /// Push Context to the stack as `if condition expression` data + /// ## Parameters pub fn if_condition_expression( &mut self, expr_result: ExpressionResult, @@ -142,34 +174,74 @@ impl SemanticStack { } /// Push Context to the stack as `condition expression` data + /// ## Parameters pub fn condition_expression( &mut self, left_result: ExpressionResult, right_result: ExpressionResult, condition: Condition, + register_number: u64, ) { self.push(SemanticStackContext::ConditionExpression { left_result, right_result, condition, + register_number, }); } /// Push Context to the stack as `jump function return` data + /// ## Parameters pub fn jump_function_return(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::JumpFunctionReturn { expr_result }); } - /// Push Context to the stack as `logic condition` data - pub fn logic_condition(&mut self, logic_condition: LogicCondition) { - self.push(SemanticStackContext::LogicCondition { logic_condition }); + /// Push Context to the stack as `logic condition` data. + /// Operate with registers: + /// + /// ## Parameters + /// - left_register_result - result of left condition + /// - right_register_result - result of right condition + /// - register_number - register to store instruction result + pub fn logic_condition( + &mut self, + logic_condition: LogicCondition, + left_register_result: u64, + right_register_result: u64, + register_number: u64, + ) { + self.push(SemanticStackContext::LogicCondition { + logic_condition, + left_register_result, + right_register_result, + register_number, + }); } - /// Push Context to the stack as `if condition logic` data - pub fn if_condition_logic(&mut self, label_if_begin: LabelName, label_if_end: LabelName) { + /// Push Context to the stack as `if condition logic` data. + /// `if_condition_logic` instruction read data from `result_register` + /// and conditionally jump: if "true' to `label_if_begin` or + /// `label_if_end` if "false" (data contained as result after + /// reading `result_register`). + /// + /// ## Parameters + /// - `label_if_begin` - label for a jump if `result_register` contains + /// result with "true" + /// - `label_if_end` - label for a jump if `result_register` contains + /// result with "false". It can be not only `if_end` but any kind (for + /// example `if_else`) + /// - `result_register` - contains register of previous condition logic + /// calculations. + pub fn if_condition_logic( + &mut self, + label_if_begin: LabelName, + label_if_end: LabelName, + result_register: u64, + ) { self.push(SemanticStackContext::IfConditionLogic { label_if_begin, label_if_end, + result_register, }); } } @@ -240,15 +312,20 @@ pub enum SemanticStackContext { left_result: ExpressionResult, right_result: ExpressionResult, condition: Condition, + register_number: u64, }, JumpFunctionReturn { expr_result: ExpressionResult, }, LogicCondition { logic_condition: LogicCondition, + left_register_result: u64, + right_register_result: u64, + register_number: u64, }, IfConditionLogic { label_if_begin: LabelName, label_if_end: LabelName, + result_register: u64, }, } From cc7c9f7146d8f1d56ec2c31c538cbfa6a0f4cbe9 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Tue, 28 Nov 2023 23:57:50 +0100 Subject: [PATCH 4/5] Extend tests --- README.md | 28 ++++++++-------- src/semantic.rs | 8 +++-- tests/if_tests.rs | 82 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 90 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e759b7c..f567420 100644 --- a/README.md +++ b/README.md @@ -11,46 +11,46 @@ Semantic analyzer is an open source semantic analyzer for programming languages that makes it easy to build your own efficient compilers. -## What is the library for and what tasks does it solve +## ๐ŸŒ€ What is the library for and what tasks does it solve Creating a compilers for a programming language is process that involves several key stages. Most commonly it is: -- **Lexical Analysis (Lexer)**: This stage involves breaking down the input stream +โ–ถ๏ธ **Lexical Analysis (Lexer)**: This stage involves breaking down the input stream of characters into a series of tokens. Tokens are the atomic elements of the programming language, such as identifiers, keywords, operators, etc. -- **Syntax Analysis (Parsing)**: At this stage, the tokens obtained in the previous +โ–ถ๏ธ **Syntax Analysis (Parsing)**: At this stage, the tokens obtained in the previous stage are grouped according to the grammar rules of the programming language. The result of this process is an **Abstract Syntax Tree (AST)**, which represents a hierarchical structure of the code. -- **Semantic Analysis**: This stage involves checking the semantic correctness of the code. This can include +โฉ **Semantic Analysis**: This stage involves checking the semantic correctness of the code. This can include type checking, scope verification of variables, etc. -- **Intermediate Code Optimization**: At this stage, the compiler tries to improve the intermediate representation of the code to make it more efficient. +โ–ถ๏ธ **Intermediate Code Optimization**: At this stage, the compiler tries to improve the intermediate representation of the code to make it more efficient. This can include dead code elimination, expression simplification, etc. -- **Code Generation**: This is the final stage where the compiler transforms the optimized intermediate representation (IR) into +โ–ถ๏ธ **Code Generation**: This is the final stage where the compiler transforms the optimized intermediate representation (IR) into machine code specific to the target architecture. This library represent **Semantic Analysis** stage. -### Features +### ๐ŸŒป Features -- **Name Binding and Scope Checking**: The analyzer verifies that all variables, constants, functions are declared before they're used, +โœ… **Name Binding and Scope Checking**: The analyzer verifies that all variables, constants, functions are declared before they're used, and that they're used within their scope. It also checks for name collisions, where variables, constants, functions, types in the same scope have the same name. -- **Checking Function Calls**: The analyzer verifies that functions are called with the number of parameters and that the type of +โœ… **Checking Function Calls**: The analyzer verifies that functions are called with the number of parameters and that the type of arguments matches the type expected by the function. -- **Scope Rules**: Checks that variables, functions, constants, types are used within their scope, and available in the visibility scope. +โœ… **Scope Rules**: Checks that variables, functions, constants, types are used within their scope, and available in the visibility scope. -- **Type Checking**: The analyzer checks that operations are performed on compatible types for expressions, functions, constant, bindings. +โœ… **Type Checking**: The analyzer checks that operations are performed on compatible types for expressions, functions, constant, bindings. For operations in expressions. It is the process of verifying that the types of expressions are consistent with their usage in the context. -- **Flow Control Checking**: The analyzer checks that the control flow statements (if-else, loop, return, break, continue) are used correctly. +โœ… **Flow Control Checking**: The analyzer checks that the control flow statements (if-else, loop, return, break, continue) are used correctly. Supported condition expressions and condition expression correctness check. -- **Building the Symbol Table**: For analyzing used the symbol table as data structure used by the semantic analyzer to keep track of +โœ… **Building the Symbol Table**: For analyzing used the symbol table as data structure used by the semantic analyzer to keep track of symbols (variables, functions, constants) in the source code. Each entry in the symbol table contains the symbol's name, type, and scope related for block state, and other relevant information. ### ๐ŸŒณ Semantic State Tree @@ -106,7 +106,7 @@ AST displays the **Turing complete** programming language and contains all the n ## ๐Ÿ›‹๏ธ Examples -- ๐Ÿ”Ž There is the example implementation separate project [Toy Codegen](https://github.com/mrLSD/toy-codegen). +- ๐Ÿ”Ž There is the example implementation separate project [๐Ÿ’พ Toy Codegen](https://github.com/mrLSD/toy-codegen). The project uses the `SemanticStack` results and converts them into **Code Generation** logic. Which clearly shows the possibilities of using the results of the `semantic-analyzer-rs` `SemanticStackContext` results. LLVM is used as a backend, [inkwell](https://github.com/TheDan64/inkwell) as a library for LLVM codegen, and compiled into an executable diff --git a/src/semantic.rs b/src/semantic.rs index c4236e5..dba21ba 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -562,7 +562,7 @@ impl State { let right_res = self.expression(right_expr, function_body_state); // If some of the `left` or `right` expression is empty just return with error in the state - let (Some(left_res), Some(right_res)) = (left_res, right_res) else { + let (Some(left_res), Some(right_res)) = (left_res.clone(), right_res.clone()) else { self.add_error(error::StateErrorResult::new( error::StateErrorKind::ConditionIsEmpty, format!("left={left_res:?}, right={right_res:?}"), @@ -595,6 +595,7 @@ impl State { // Increment register function_body_state.borrow_mut().inc_register(); + let register_number = function_body_state.borrow_mut().last_register_number; // Codegen for left condition and set result to register function_body_state .borrow_mut() @@ -603,7 +604,7 @@ impl State { left_res, right_res, data.left.condition.clone().into(), - function_body_state.borrow_mut().last_register_number, + register_number, ); // Analyze right condition @@ -615,6 +616,7 @@ impl State { // Increment register function_body_state.borrow_mut().inc_register(); + let register_number = function_body_state.borrow_mut().last_register_number; // Stategen for logical condition for: left [LOGIC-OP] right // The result generated from registers, and stored to // new register @@ -622,7 +624,7 @@ impl State { right.0.clone().into(), left_register_result, right_register_result, - function_body_state.borrow_mut().last_register_number, + register_number, ); } function_body_state.borrow_mut().last_register_number diff --git a/tests/if_tests.rs b/tests/if_tests.rs index da36875..682daa8 100644 --- a/tests/if_tests.rs +++ b/tests/if_tests.rs @@ -489,33 +489,88 @@ fn if_condition_calculation_logic() { expr_type: Type::Primitive(PrimitiveTypes::I8), expr_value: ExpressionResultValue::PrimitiveValue(PrimitiveValue::I8(6)) }, - condition: Condition::Eq + condition: Condition::Eq, + register_number: 1, } ); assert_eq!( ctx[1], SemanticStackContext::IfConditionLogic { label_if_begin: label_if_begin.clone(), - label_if_end + label_if_end: label_if_end.clone(), + result_register: 1 + } + ); + assert_eq!( + ctx[2], + 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, + register_number: 2, } ); - assert_eq!(ctx[2], ctx[0]); assert_eq!( ctx[3], SemanticStackContext::IfConditionLogic { - label_if_begin, - label_if_end: label_if_else + label_if_begin: label_if_begin.clone(), + label_if_end: label_if_else, + result_register: 2 + } + ); + assert_eq!( + ctx[4], + 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, + register_number: 3, + } + ); + assert_eq!( + ctx[5], + 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, + register_number: 4, } ); - assert_eq!(ctx[4], ctx[0]); - assert_eq!(ctx[5], ctx[0]); assert_eq!( ctx[6], SemanticStackContext::LogicCondition { - logic_condition: LogicCondition::Or + logic_condition: LogicCondition::Or, + left_register_result: 3, + right_register_result: 4, + register_number: 5 + } + ); + assert_eq!( + ctx[7], + SemanticStackContext::IfConditionLogic { + label_if_begin, + label_if_end, + result_register: 5 } ); - assert_eq!(ctx[7], ctx[1]); assert_eq!(ctx.len(), 8); assert!(t.is_empty_error()); } @@ -557,12 +612,17 @@ fn if_condition_when_left_expr_return_error() { &label_if_end, false, ); - assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len()); + assert!(t.check_errors_len(2), "Errors: {:?}", t.state.errors.len()); assert!( - t.check_error(StateErrorKind::FunctionNotFound), + t.check_error_index(0, StateErrorKind::FunctionNotFound), "Errors: {:?}", t.state.errors[0] ); + assert!( + t.check_error_index(1, StateErrorKind::ConditionIsEmpty), + "Errors: {:?}", + t.state.errors[1] + ); } #[test] From c66261879af1e86aa2df0f3f9b8856cfabfeebb8 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 29 Nov 2023 17:32:21 +0100 Subject: [PATCH 5/5] Extend instration documentation for SematicStack --- src/types/semantic.rs | 95 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/src/types/semantic.rs b/src/types/semantic.rs index fb5c50a..8cad452 100644 --- a/src/types/semantic.rs +++ b/src/types/semantic.rs @@ -1,7 +1,6 @@ //! # Semantic types //! Semantic analyzer result state types. -//! It contains `SemanticStack` as Semantic results -//! Context data. +//! It contains `SemanticStack` as Semantic results Context data. use super::condition::{Condition, LogicCondition}; use super::expression::{ExpressionOperations, ExpressionResult}; @@ -91,8 +90,13 @@ impl SemanticStack { }); } - /// Push Context to the stack as function call data + /// Push Context to the stack as function call data. + /// Function call instruction with parameters and result data. + /// /// ## Parameters + /// - `call` - function declaration data + /// - `params` - function parameters + /// - `register_number` - register to store result of function call pub fn call(&mut self, call: Function, params: Vec, register_number: u64) { self.push(SemanticStackContext::Call { call, @@ -101,8 +105,13 @@ impl SemanticStack { }); } - /// Push Context to the stack as let-binding data + /// Push Context to the stack as let-binding data. + /// Let binding instruction that "bind" expression result to + /// the new value. + /// /// ## Parameters + /// - `let_decl` - value declaration + /// - `expr_result` - expression result that will be bind to the value pub fn let_binding(&mut self, let_decl: Value, expr_result: ExpressionResult) { self.push(SemanticStackContext::LetBinding { let_decl, @@ -110,56 +119,95 @@ impl SemanticStack { }); } - /// Push Context to the stack as binding data + /// Push Context to the stack as binding data. + /// Binding instruction that "bind" expression result to + /// the old. previously init value. + /// /// ## Parameters + /// - `val` - value declaration + /// - `expr_result` - expression result that will be bind to the value pub fn binding(&mut self, val: Value, expr_result: ExpressionResult) { self.push(SemanticStackContext::Binding { val, expr_result }); } - /// Push Context to the stack as function declaration data + /// Push Context to the stack as function declaration data. + /// Function declaration instruction. + /// /// ## Parameters + /// - `fn_decl` - function declaration parameters pub fn function_declaration(&mut self, fn_decl: FunctionStatement) { self.push(SemanticStackContext::FunctionDeclaration { fn_decl }); } - /// Push Context to the stack as constant data + /// Push Context to the stack as constant data. + /// Constant declaration instruction. + /// /// ## Parameters + /// - `const_decl` - constant declaration parameters pub fn constant(&mut self, const_decl: Constant) { self.push(SemanticStackContext::Constant { const_decl }); } - /// Push Context to the stack as types data + /// Push Context to the stack as types data. + /// Types declaration instruction. + /// /// ## Parameters + /// - `type_decl` - type declaration parameters pub fn types(&mut self, type_decl: StructTypes) { self.push(SemanticStackContext::Types { type_decl }); } - /// Push Context to the stack as expression function return data + /// Push Context to the stack as expression function return data. + /// Return instruction, should be used in the end of functions. + /// Alwats should be only once. + /// /// ## Parameters + /// - `expr_result` - result data for the return pub fn expression_function_return(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::ExpressionFunctionReturn { expr_result }); } - /// Push Context to the stack as `expression function return with label` data + /// Push Context to the stack as `expression function return with label` data. + /// Return instruction with additional logic. Most useful case when + /// `return` previously was call from `if-body` or `loop-body.`. + /// As additional behavior this `expression_function_return_with_label` should + /// set `return` label. It will allow `jump-to-return` case. Also + /// before `return` label Codegen, for normal instruction flow, must + /// jump to `return` label anyway. + /// /// ## Parameters + /// - `expr_result` - result data for the return pub fn expression_function_return_with_label(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::ExpressionFunctionReturnWithLabel { expr_result }); } - /// Push Context to the stack as `set label` data + /// Push Context to the stack as `set label` data. + /// Set label. Useful for any kind of jump operations and conditional flow. + /// /// ## Parameters + /// - `label` - label name pub fn set_label(&mut self, label: LabelName) { self.push(SemanticStackContext::SetLabel { label }); } - /// Push Context to the stack as `jump to` data + /// Push Context to the stack as `jump to` data. + /// Unconditional direct jump to label. + /// /// ## Parameters + /// - `label` - label for the jump pub fn jump_to(&mut self, label: LabelName) { self.push(SemanticStackContext::JumpTo { label }); } - /// Push Context to the stack as `if condition expression` data + /// Push Context to the stack as `if condition expression` data. + /// `if-condition expression` represent if-condition, when if expression + /// is "true" jump to `label_if_begin` else `label_if_end`. + /// /// ## Parameters + /// - `expr_result` - expression result of `if-condition` for + /// conditional instruction + /// - `label_if_begin` - label for jump if expression is "true" + /// - `label_if_end` - label for jump if expression is "false" pub fn if_condition_expression( &mut self, expr_result: ExpressionResult, @@ -173,8 +221,14 @@ impl SemanticStack { }); } - /// Push Context to the stack as `condition expression` data + /// Push Context to the stack as `condition expression` data. + /// Condition expression between left and right condition calculation. + /// /// ## Parameters + /// - `left_result` - left expression result + /// - `right_result` - right expression result + /// - `condition` - condition operation + /// - `register_number` - register to store result of expression operation pub fn condition_expression( &mut self, left_result: ExpressionResult, @@ -190,14 +244,20 @@ impl SemanticStack { }); } - /// Push Context to the stack as `jump function return` data + /// Push Context to the stack as `jump function return` data. + /// Jump to function return with expression result data. Label for jumping + /// to return position (always end of function) should be always the same + /// and should be managed by Codegen. + /// /// ## Parameters + /// - `expr_result` - expression result for return condition pub fn jump_function_return(&mut self, expr_result: ExpressionResult) { self.push(SemanticStackContext::JumpFunctionReturn { expr_result }); } /// Push Context to the stack as `logic condition` data. - /// Operate with registers: + /// Operate with registers: left and right for specific logic condition. + /// Result of calculation stored to `register_number`. /// /// ## Parameters /// - left_register_result - result of left condition @@ -247,7 +307,8 @@ impl SemanticStack { } /// # Semantic stack Context -/// Context data of Semantic results +/// Context data of Semantic results. Contains type declarations +/// for specific instructions. #[derive(Debug, Clone, PartialEq)] pub enum SemanticStackContext { ExpressionValue {