Skip to content

Commit

Permalink
Merge pull request #7 from mrLSD/feat/extend-tests-v0.2.4
Browse files Browse the repository at this point in the history
Feat: fix multiple return issue and extend analyzer tests
  • Loading branch information
mrLSD authored Nov 5, 2023
2 parents 2992080 + 26dc812 commit b25203d
Show file tree
Hide file tree
Showing 11 changed files with 2,372 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "semantic-analyzer"
version = "0.2.3"
version = "0.2.4"
authors = ["Evgeny Ukhanov <[email protected]>"]
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"]
Expand Down
3 changes: 2 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,8 @@ pub struct IfStatement<'a> {

impl GetLocation for IfStatement<'_> {
fn location(&self) -> CodeLocation {
todo!()
// TODO
CodeLocation::new(1, 0)
}
}

Expand Down
95 changes: 70 additions & 25 deletions src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -466,7 +474,6 @@ impl State {
));
return;
}

function_state
.borrow_mut()
.context
Expand Down Expand Up @@ -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<RefCell<BlockState>>,
label_end: &LabelName,
label_loop: Option<(&LabelName, &LabelName)>,
) {
for body in body.iter() {
match body {
Expand All @@ -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);
Expand All @@ -634,6 +649,7 @@ impl State {
&mut self,
body: &[ast::IfLoopBodyStatement<'_>],
if_body_state: &Rc<RefCell<BlockState>>,
label_if_end: &LabelName,
label_loop_start: &LabelName,
label_loop_end: &LabelName,
) {
Expand All @@ -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)),
);
}
Expand Down Expand Up @@ -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,
Expand All @@ -784,24 +800,25 @@ 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()
.get_and_set_next_label(&"if_begin".to_string().into());
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(
Expand All @@ -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
Expand All @@ -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) =
Expand All @@ -859,19 +893,22 @@ impl State {
self.if_condition_loop_body(
body,
&if_else_body_state,
&label_if_end,
label_loop_start,
label_loop_end,
);
}
}

// 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,
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand Down
1 change: 0 additions & 1 deletion src/types/block_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ pub struct BlockState {
pub labels: HashSet<LabelName>,
/// Last register for unique register representation
pub last_register_number: u64,

/// Manual return from other states
pub manual_return: bool,
/// Parent state
Expand Down
1 change: 1 addition & 0 deletions src/types/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum StateErrorKind {
FunctionNotFound,
FunctionParameterTypeWrong,
ReturnNotFound,
ReturnAlreadyCalled,
IfElseDuplicated,
TypeNotFound,
WrongReturnType,
Expand Down
2 changes: 1 addition & 1 deletion src/types/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 27 additions & 1 deletion tests/lbinding_tests.rs → tests/binding_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
},
}
);
}
3 changes: 2 additions & 1 deletion tests/function_call_tests.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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());
Expand Down
Loading

0 comments on commit b25203d

Please sign in to comment.