Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: fix multiple return issue and extend analyzer tests #7

Merged
merged 19 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <mrlsd@ya.ru>"]
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