Skip to content

Commit

Permalink
Merge pull request #15 from mrLSD/feat/after-return-cleanup-instructions
Browse files Browse the repository at this point in the history
Feat: after termination instructions `return`, `break`, `continue` do not allow other instructions
  • Loading branch information
mrLSD authored Nov 11, 2023
2 parents 7e8883a + 7d9fd52 commit 5ad44c2
Show file tree
Hide file tree
Showing 14 changed files with 733 additions and 137 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
- name: Run grcov
run: |
mkdir ./target/debug/coverage/
grcov . -s . -b ./target/debug/ -o ./target/debug/coverage/ --ignore-not-existing --excl-line="grcov-excl-line|#\\[derive\\(|//!|///" --ignore="*.cargo/*" --ignore="src/lib.rs"
grcov . -s . -b ./target/debug/ -o ./target/debug/coverage/ --ignore-not-existing --excl-line="grcov-excl-line|#\\[derive\\(|//!|///" --ignore="*.cargo/*" --ignore="src/lib.rs" --ignore="tests/*"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
Expand Down
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.5"
version = "0.2.6"
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
135 changes: 106 additions & 29 deletions src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//! - `Context` - stack for `Block state` of each functions body state.
//! - `Errors` - semantic analyzes errors.z
use crate::ast::{self, GetLocation, GetName, MAX_PRIORITY_LEVEL_FOR_EXPRESSIONS};
use crate::ast::{self, CodeLocation, GetLocation, GetName, MAX_PRIORITY_LEVEL_FOR_EXPRESSIONS};
use crate::types::block_state::BlockState;
use crate::types::expression::{
Expression, ExpressionResult, ExpressionResultValue, ExpressionStructValue,
Expand Down Expand Up @@ -290,6 +290,13 @@ impl State {
let mut return_is_called = false;
// Fetch function elements and gather errors
for body in &data.body {
if return_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
match body {
ast::BodyStatement::LetBinding(bind) => {
self.let_binding(bind, &body_state);
Expand Down Expand Up @@ -599,14 +606,24 @@ impl State {
/// Analyze body for ant if condition:
/// - if, else, if-else
/// NOTE: label_end - is always already exists
/// ## Return
/// Return body statement "return" status
pub fn if_condition_body(
&mut self,
body: &[ast::IfBodyStatement<'_>],
if_body_state: &Rc<RefCell<BlockState>>,
label_end: &LabelName,
label_loop: Option<(&LabelName, &LabelName)>,
) {
) -> bool {
let mut return_is_called = false;
for body in body.iter() {
if return_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
match body {
ast::IfBodyStatement::LetBinding(bind) => {
self.let_binding(bind, if_body_state);
Expand Down Expand Up @@ -636,24 +653,53 @@ impl State {
// return
if_body_state.borrow_mut().context.jump_function_return(res);
if_body_state.borrow_mut().set_return();
return_is_called = true;
};
}
}
}
return_is_called
}

/// # If-condition loop body
/// Analyze body for ant if condition:
/// - if, else, if-else
/// ## Return
/// Return body statement "return" status
pub fn if_condition_loop_body(
&mut self,
body: &[ast::IfLoopBodyStatement<'_>],
if_body_state: &Rc<RefCell<BlockState>>,
label_if_end: &LabelName,
label_loop_start: &LabelName,
label_loop_end: &LabelName,
) {
) -> bool {
let mut return_is_called = false;
let mut break_is_called = false;
let mut continue_is_called = false;
for body in body.iter() {
if return_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
if break_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterBreakDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
if continue_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterContinueDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}

match body {
ast::IfLoopBodyStatement::LetBinding(bind) => {
self.let_binding(bind, if_body_state);
Expand Down Expand Up @@ -683,9 +729,11 @@ impl State {
// return
if_body_state.borrow_mut().context.jump_function_return(res);
if_body_state.borrow_mut().set_return();
return_is_called = true;
}
}
ast::IfLoopBodyStatement::Continue => {
continue_is_called = true;
// Skip next loop step and jump to the start
// of loop
if_body_state
Expand All @@ -694,6 +742,7 @@ impl State {
.jump_to(label_loop_start.clone());
}
ast::IfLoopBodyStatement::Break => {
break_is_called = true;
// Break loop and jump to the end of loop
if_body_state
.borrow_mut()
Expand All @@ -702,6 +751,7 @@ impl State {
}
}
}
return_is_called
}

/// # If conditions calculations
Expand Down Expand Up @@ -833,11 +883,12 @@ impl State {
//== If condition main body
// Set if-begin label
if_body_state.borrow_mut().context.set_label(label_if_begin);
// Analyze if-conditions body kind
match &data.body {
// Analyze if-conditions body kind.
// Return flag for current body state, excluding children return claims
let return_is_called = match &data.body {
ast::IfBodyStatements::If(body) => {
// Analyze if-statement body
self.if_condition_body(body, &if_body_state, &label_if_end, label_loop);
self.if_condition_body(body, &if_body_state, &label_if_end, label_loop)
}
ast::IfBodyStatements::Loop(body) => {
// It's special case for the Loop, when `label_loop` should always be set.
Expand All @@ -851,12 +902,12 @@ impl State {
&label_if_end,
label_loop_start,
label_loop_end,
);
)
}
}
};
// Codegen for jump to if-end statement - return to program flow.
// If return is set do not add jump-to-end label.
if !if_body_state.borrow().manual_return {
if !return_is_called {
if_body_state
.borrow_mut()
.context
Expand All @@ -878,15 +929,10 @@ impl State {
.borrow_mut()
.set_child(if_else_body_state.clone());

match else_body {
let return_is_called = match else_body {
ast::IfBodyStatements::If(body) => {
// Analyze if-statement body
self.if_condition_body(
body,
&if_else_body_state,
&label_if_end,
label_loop,
);
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 @@ -898,13 +944,13 @@ impl State {
&label_if_end,
label_loop_start,
label_loop_end,
);
)
}
}
};

// Codegen for jump to if-end statement -return to program flow
// If return is set do not add jump-to-end label.
if !if_body_state.borrow().manual_return {
if !return_is_called {
if_body_state
.borrow_mut()
.context
Expand Down Expand Up @@ -966,7 +1012,32 @@ impl State {
.context
.set_label(label_loop_begin.clone());

let mut return_is_called = false;
let mut break_is_called = false;
let mut continue_is_called = false;
for body in data.iter() {
if return_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterReturnDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
if break_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterBreakDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}
if continue_is_called {
self.add_error(error::StateErrorResult::new(
error::StateErrorKind::ForbiddenCodeAfterContinueDeprecated,
format!("{body:?}"),
CodeLocation::new(1, 1),
));
}

match body {
ast::LoopBodyStatement::LetBinding(bind) => {
self.let_binding(bind, &loop_body_state);
Expand Down Expand Up @@ -997,6 +1068,7 @@ impl State {
.context
.jump_function_return(res);
loop_body_state.borrow_mut().set_return();
return_is_called = true;
}
}
ast::LoopBodyStatement::Break => {
Expand All @@ -1005,6 +1077,7 @@ impl State {
.borrow_mut()
.context
.jump_to(label_loop_end.clone());
break_is_called = true;
}
ast::LoopBodyStatement::Continue => {
// Skip next loop step and jump to the start
Expand All @@ -1013,21 +1086,25 @@ impl State {
.borrow_mut()
.context
.jump_to(label_loop_begin.clone());
continue_is_called = true;
}
}
}

// Because it's loop jump to loop begin
loop_body_state
.borrow_mut()
.context
.jump_to(label_loop_begin.clone());
// If return is called do not set loop-specific instructions
if !return_is_called {
// 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()
.context
.set_label(label_loop_end);
// Loop ending
loop_body_state
.borrow_mut()
.context
.set_label(label_loop_end);
}
}

#[allow(clippy::doc_markdown)]
Expand Down
3 changes: 3 additions & 0 deletions src/types/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub enum StateErrorKind {
WrongReturnType,
ConditionExpressionWrongType,
ConditionExpressionNotSupported,
ForbiddenCodeAfterReturnDeprecated,
ForbiddenCodeAfterContinueDeprecated,
ForbiddenCodeAfterBreakDeprecated,
}

/// State error location. Useful to determine location of error
Expand Down
24 changes: 18 additions & 6 deletions tests/binding_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ fn binding_wrong_expression() {
value: Box::new(expr),
};
t.state.binding(&binding, &block_state);
assert!(t.check_errors_len(1));
assert!(t.check_error(StateErrorKind::ValueNotFound));
assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len());
assert!(
t.check_error(StateErrorKind::ValueNotFound),
"Errors: {:?}",
t.state.errors[0]
);
}

#[test]
Expand All @@ -62,8 +66,12 @@ fn binding_value_not_exist() {
value: Box::new(expr),
};
t.state.binding(&binding, &block_state);
assert!(t.check_errors_len(1));
assert!(t.check_error(StateErrorKind::ValueNotFound));
assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len());
assert!(
t.check_error(StateErrorKind::ValueNotFound),
"Errors: {:?}",
t.state.errors[0]
);
}

#[test]
Expand Down Expand Up @@ -113,8 +121,12 @@ fn binding_value_not_mutable() {
value: Box::new(expr),
};
t.state.binding(&binding, &block_state);
assert!(t.check_errors_len(1));
assert!(t.check_error(StateErrorKind::ValueIsNotMutable));
assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len());
assert!(
t.check_error(StateErrorKind::ValueIsNotMutable),
"Errors: {:?}",
t.state.errors[0]
);
}

#[test]
Expand Down
16 changes: 12 additions & 4 deletions tests/const_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ fn const_declaration() {
);

t.state.constant(&const_statement);
assert!(t.check_errors_len(1));
assert!(t.check_error(StateErrorKind::ConstantAlreadyExist));
assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len());
assert!(
t.check_error(StateErrorKind::ConstantAlreadyExist),
"Errors: {:?}",
t.state.errors[0]
);
let state = t.state.global.context.clone().get();
assert_eq!(state.len(), 1);
}
Expand Down Expand Up @@ -153,8 +157,12 @@ fn const_declaration_with_operations() {
};
assert_eq!(const_statement.location(), CodeLocation::new(1, 0));
t.state.constant(&const_statement);
assert!(t.check_errors_len(1));
assert!(t.check_error(StateErrorKind::ConstantNotFound));
assert!(t.check_errors_len(1), "Errors: {:?}", t.state.errors.len());
assert!(
t.check_error(StateErrorKind::ConstantNotFound),
"Errors: {:?}",
t.state.errors[0]
);
assert!(!t
.state
.global
Expand Down
Loading

0 comments on commit 5ad44c2

Please sign in to comment.