Skip to content

Commit

Permalink
Extend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mrLSD committed Nov 17, 2023
1 parent b54cd7c commit cc7c9f7
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 28 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:?}"),
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -615,14 +616,15 @@ 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
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,
register_number,
);
}
function_body_state.borrow_mut().last_register_number
Expand Down
82 changes: 71 additions & 11 deletions tests/if_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit cc7c9f7

Please sign in to comment.