refactor: migrate all errors to new struct

This commit is contained in:
Araozu 2024-09-15 19:09:19 -05:00
parent 1a7dd72783
commit 15030635fb
15 changed files with 417 additions and 428 deletions

View File

@ -30,6 +30,8 @@
- [x] Typecheck while loops
- [x] Include Ariadne for error reporting
- [x] Migrate lexic errors to new error interface
- [x] Migrate syntax errors to new error interface
- [x] Migrate semantic errors to new error interface
## v0.1.1

2
Cargo.lock generated
View File

@ -152,7 +152,7 @@ dependencies = [
[[package]]
name = "thp"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"ariadne",
"colored",

View File

@ -1,6 +1,6 @@
[package]
name = "thp"
version = "0.1.1"
version = "0.1.2"
edition = "2021"

View File

@ -17,4 +17,10 @@
0x000015: Invalid for loop
0x000016: Invalid if condition
0x000017: Incomplete block
0x000018: Missing reference
0x000019: Invalid reference
0x000020: Unknown error (compiler todo)
0x000021: Mismatched types
0x000022: Duplicated reference
0x000023: Mismatched argument count

View File

@ -18,6 +18,12 @@ pub const SYNTAX_INVALID_FUNCTION_DECLARATION: u32 = 14;
pub const SYNTAX_INVALID_FOR_LOOP: u32 = 15;
pub const SYNTAX_INVALID_IF_CONDITION: u32 = 16;
pub const SYNTAX_INCOMPLETE_BLOCK: u32 = 17;
pub const SEMANTIC_MISSING_REFERENCE: u32 = 18;
pub const SEMANTIC_INVALID_REFERENCE: u32 = 19;
pub const COMPILER_TODO: u32 = 20;
pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21;
pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22;
pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23;
/// Reads the error codes from the error code list
pub fn error_code_to_string() -> String {

View File

@ -1,170 +0,0 @@
use ariadne::{Color, Label, Report, ReportKind, Source};
use super::{LexError, PrintableError};
use std::collections::VecDeque;
impl PrintableError for LexError {
fn get_error_str(&self, chars: &Vec<char>) -> String {
let line_number = get_line_number(chars, self.position);
let (erroneous_code, column_number_zero) = get_line(chars, self.position);
let column_number = column_number_zero + 1;
let line_number_whitespace = " ".repeat(line_number.to_string().len());
let whitespace = " ".repeat(column_number_zero);
let reason = &self.reason;
format!(
r#"
{line_number_whitespace} |
{line_number } | {erroneous_code}
{line_number_whitespace} | {whitespace}^
{reason} at line {line_number}:{column_number}"#,
)
}
fn print_ariadne(&self, source: &String) {
let report = Report::build(ReportKind::Error, "sample.thp", self.position)
.with_label(
Label::new(("sample.thp", self.position..self.end_position))
.with_message(self.reason.clone())
.with_color(Color::Red),
)
.finish();
report.eprint(("sample.thp", Source::from(source))).unwrap();
}
}
/// Extracts a line of code from `chars` and the number of characters in the back.
/// `pos` indicates a position, from where to extract the line.
///
/// Ex. Given:
/// - `input = "first line\nsecond line\nthird line"`
/// - `pos = 15`
///
/// this function should return `("second line", 4)`
fn get_line(chars: &Vec<char>, pos: usize) -> (String, usize) {
let mut result_chars = VecDeque::<char>::new();
// Push chars to the front until a new line is found
// TODO: refactor
let mut before_pos = pos;
loop {
let current_char = chars[before_pos];
if current_char == '\n' {
// This is important because before_pos will be used to calculate
// the number of chars before pos
before_pos += 1;
break;
}
result_chars.push_front(current_char);
if before_pos == 0 {
break;
}
before_pos -= 1;
}
// Push chars to the end until a new line is found
let mut after_pos = pos + 1;
let char_count = chars.len();
while after_pos < char_count {
let current_char = chars[after_pos];
if current_char == '\n' {
break;
}
result_chars.push_back(current_char);
after_pos += 1;
}
(result_chars.iter().collect::<String>(), pos - before_pos)
}
fn get_line_number(chars: &Vec<char>, target_pos: usize) -> usize {
let mut count = 1;
for (pos, char) in chars.iter().enumerate() {
if pos >= target_pos {
break;
}
if *char == '\n' {
count += 1;
}
}
count
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexic;
#[test]
fn test_error_msg() {
let input = String::from("val name' = 20");
let result = lexic::get_tokens(&input);
match result {
Ok(_) => assert!(false),
Err(err_data) => {
let chars: Vec<char> = input.chars().into_iter().collect();
let err_str = err_data.get_error_str(&chars);
let expected_str = format!(
r#"
|
1 | val name' = 20
| ^
Illegal character `'` (escaped: \') at line 1:9"#,
);
assert_eq!(expected_str, err_str,);
}
}
}
#[test]
fn should_extract_line() {
let input = String::from("first line\nsecond line\nthird line");
let chars: Vec<char> = input.chars().into_iter().collect();
let (result, back_count) = get_line(&chars, 15);
assert_eq!("second line", result);
assert_eq!(4, back_count);
let input = String::from("val binding = 322");
let chars: Vec<char> = input.chars().into_iter().collect();
let (result, back_count) = get_line(&chars, 6);
assert_eq!("val binding = 322", result);
assert_eq!(6, back_count);
}
#[test]
fn should_get_line_number() {
let input = String::from("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
let chars: Vec<char> = input.chars().into_iter().collect();
let line_number = get_line_number(&chars, 11);
assert_eq!(3, line_number);
let line_number = get_line_number(&chars, 0);
assert_eq!(1, line_number);
let line_number = get_line_number(&chars, 3);
assert_eq!(1, line_number);
let line_number = get_line_number(&chars, 15);
assert_eq!(4, line_number);
}
}

View File

@ -5,9 +5,7 @@ use serde::Serialize;
use self::semantic_error::SemanticError;
mod lex_error;
pub mod semantic_error;
mod syntax_error;
mod utils;
pub mod error_messages;
@ -38,22 +36,7 @@ pub struct ErrorLabel {
pub enum MistiError {
Lex(ErrorContainer),
Syntax(ErrorContainer),
Semantic(SemanticError),
}
#[derive(Serialize, Debug)]
pub struct LexError {
pub position: usize,
// TODO: Add and end position
pub end_position: usize,
pub reason: String,
}
#[derive(Serialize, Debug)]
pub struct SyntaxError {
pub error_start: usize,
pub error_end: usize,
pub reason: String,
Semantic(ErrorContainer),
}
impl PrintableError for MistiError {

View File

@ -1,78 +0,0 @@
use ariadne::{Color, Label, Report, ReportKind, Source};
use super::utils::{get_line, get_line_number};
use super::{PrintableError, SyntaxError};
impl PrintableError for SyntaxError {
fn get_error_str(&self, chars: &Vec<char>) -> String {
let (line, before, length) = get_line(chars, self.error_start, self.error_end);
let line_number = get_line_number(chars, self.error_start);
let line_number_whitespace = " ".repeat(line_number.to_string().len());
let whitespace = vec![' '; before].iter().collect::<String>();
let indicator = vec!['^'; length].iter().collect::<String>();
let reason = &self.reason;
format!(
r#"
{line_number_whitespace} |
{line_number } | {line}
{line_number_whitespace} | {whitespace}{indicator}
{reason} at line {line_number}:{before}"#,
)
}
fn print_ariadne(&self, source: &String) {
let report = Report::build(ReportKind::Error, "sample.thp", self.error_start)
.with_label(
Label::new(("sample.thp", self.error_start..self.error_end))
.with_message(self.reason.clone())
.with_color(Color::Red),
)
.finish();
report.eprint(("sample.thp", Source::from(source))).unwrap();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{error_handling::MistiError, lexic::get_tokens, syntax::build_ast};
fn _get_error_data(input: String) -> (Vec<char>, MistiError) {
let tokens = get_tokens(&input).unwrap();
let error_holder = build_ast(&tokens);
match error_holder {
Ok(_) => panic!(
"syntax_error test: Input expected to throw error didn't:\n\n{}",
input
),
Err(error) => {
let chars: Vec<char> = input.chars().into_iter().collect();
(chars, error)
}
}
}
#[test]
fn should_get_line() {
let input: Vec<char> = String::from("\n\nval number == 50\n\n")
.chars()
.into_iter()
.collect();
let start_position = 13;
let end_position = 15;
let (line, before, length) = get_line(&input, start_position, end_position);
assert_eq!("val number == 50", line);
assert_eq!(11, before);
assert_eq!(2, length);
}
}

View File

@ -1,5 +1,8 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::SEMANTIC_DUPLICATED_REFERENCE, semantic_error::SemanticError,
ErrorContainer, ErrorLabel, MistiError,
},
semantic::{
impls::SemanticCheck,
types::{Type, Typed},
@ -17,16 +20,19 @@ impl SemanticCheck for VariableBinding<'_> {
// TODO: Define if variables can be redeclared.
// If so, it is irrelevant to check if the variable is already defined
if scope.test(binding_name) {
let error = SemanticError {
error_start: self.identifier.position,
error_end: self.identifier.get_end_position(),
reason: format!(
"Duplicated: A symbol with name {} was already defined",
binding_name
),
let label = ErrorLabel {
message: String::from("A reference with this name was already defined"),
start: self.identifier.position,
end: self.identifier.get_end_position(),
};
return Err(MistiError::Semantic(error));
let econtainer = ErrorContainer {
error_code: SEMANTIC_DUPLICATED_REFERENCE,
error_offset: self.identifier.position,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// This gets the datatype of the assigned expression,
@ -41,16 +47,22 @@ impl SemanticCheck for VariableBinding<'_> {
// Both the declared & actual datatypes must be the same
if datatype != expression_datatype {
let error = SemanticError {
error_start: self.identifier.position,
error_end: self.identifier.get_end_position(),
reason: format!(
let label = ErrorLabel {
message: format!(
"The variable `{}` was declared as `{:?}` but its expression has type `{:?}`",
binding_name, datatype, expression_datatype
),
start: self.identifier.position,
end: self.identifier.get_end_position(),
};
return Err(MistiError::Semantic(error));
let econtainer = ErrorContainer {
error_code: SEMANTIC_DUPLICATED_REFERENCE,
error_offset: self.identifier.position,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
scope.insert(binding_name.clone(), datatype);

View File

@ -1,5 +1,8 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::SEMANTIC_MISMATCHED_TYPES, semantic_error::SemanticError, ErrorContainer,
ErrorLabel, MistiError,
},
semantic::{
impls::SemanticCheck,
symbol_table::SymbolTable,
@ -17,14 +20,22 @@ impl SemanticCheck for Conditional<'_> {
let if_condition_type = if_condition.get_type(scope)?;
if !if_condition_type.equals(&bool_type) {
let (error_start, error_end) = if_condition.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a condition of type Bool, found {:?}",
if_condition_type
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// Check if block
@ -37,14 +48,22 @@ impl SemanticCheck for Conditional<'_> {
let condition_type = condition.get_type(scope)?;
if !condition_type.equals(&bool_type) {
let (error_start, error_end) = condition.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a condition of type Bool, found {:?}",
condition_type
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
else_if_member.body.check_semantics(scope)?;

View File

@ -1,5 +1,11 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::{
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
SEMANTIC_MISMATCHED_TYPES,
},
ErrorContainer, ErrorLabel, MistiError,
},
semantic::{
impls::SemanticCheck,
symbol_table::SymbolTable,
@ -22,15 +28,23 @@ impl SemanticCheck for Expression<'_> {
if parameters.len() != arguments.len() {
let (error_start, error_end) = f.arguments.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected {} arguments, got {}",
parameters.len(),
arguments.len(),
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// Check that each argument matches the required datatype
@ -42,14 +56,22 @@ impl SemanticCheck for Expression<'_> {
if !argument_datatype.is_value(parameter) {
// The argument and the parameter have diferent types
let (error_start, error_end) = argument.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a {}, got {:?}",
parameter, argument_datatype
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}
@ -57,14 +79,22 @@ impl SemanticCheck for Expression<'_> {
}
_ => {
let (error_start, error_end) = fun.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a function type, got {:?}",
function_datatype
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}
}
@ -87,21 +117,37 @@ impl SemanticCheck for Expression<'_> {
} else {
// Error: unary negation can only be applied to a Bool
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Bool, got a {}", t),
}));
let label = ErrorLabel {
message: format!("Expected a Bool, got {}", t),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}
("!", Type::Function(_, _)) => {
// Error: unary negation can only be applied to a Bool
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Bool, got a function",),
}));
let label = ErrorLabel {
message: format!("Expected a Bool, got a function"),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
("-", Type::Value(t)) => {
if t == "Int" || t == "Float" {
@ -110,21 +156,37 @@ impl SemanticCheck for Expression<'_> {
} else {
// Error: unary negation can only be applied to a Number
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Float or Int, got a {}", t),
}));
let label = ErrorLabel {
message: format!("Expected a Float or Int, got a {}", t),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}
("-", Type::Function(_, _)) => {
// Error: unary negation can only be applied to a Bool
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Float or Int, got a function",),
}));
let label = ErrorLabel {
message: format!("Expected a Float or Int, got a function"),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
(op, _) => {
// Compiler error: something that shouldn't be
@ -146,11 +208,19 @@ impl SemanticCheck for Expression<'_> {
// If the operator is not found its a user error,
// because we allow arbitrary operators
let (error_start, error_end) = (op.position, op.get_end_position());
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("The binary operator {} does not exist", op.value),
}));
let label = ErrorLabel {
message: format!("The binary operator {} does not exist", op.value),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_INVALID_REFERENCE,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
};
@ -168,26 +238,42 @@ impl SemanticCheck for Expression<'_> {
if !left_expr_type.is_value(&op_params[0]) {
let (error_start, error_end) = left_expr.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a {}, got a {:?} on the left side of the {} operator",
op_params[0], left_expr_type, op.value
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
if !right_expr_type.is_value(&op_params[1]) {
let (error_start, error_end) = right_expr.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a {}, got a {:?} on the right side of the {} operator",
op_params[1], left_expr_type, op.value
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// After all these checks, we are ok
@ -206,13 +292,21 @@ impl SemanticCheck for Expression<'_> {
// TODO: if the array is empty then its
// datatype should be determined by its usage.
if arr.exps.is_empty() {
return Err(MistiError::Semantic(SemanticError {
error_start: arr.start,
error_end: arr.end,
reason: format!(
let label = ErrorLabel {
message: format!(
"An array must have at least 1 element to determine its type. This will be fixed later."
),
}));
start: arr.start,
end: arr.end,
};
let econtainer = ErrorContainer {
error_code: COMPILER_TODO,
error_offset: arr.start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
let mut expressions = arr.exps.iter();
@ -227,15 +321,23 @@ impl SemanticCheck for Expression<'_> {
// error, found an item with a diferent datatype
let (error_start, error_end) = exp.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"All elements of an array must have the same datatype. Expected {:?}, got {:?}",
first_type,
exp_type,
),
}));
),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: COMPILER_TODO,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}

View File

@ -1,5 +1,7 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
},
semantic::{
impls::SemanticCheck,
symbol_table::SymbolTable,
@ -30,11 +32,19 @@ impl SemanticCheck for ForLoop<'_> {
_ => {
// error, types other than an Array are not supported
let (error_start, error_end) = self.collection.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Only Array[T] are allowed as a for-loop collection."),
}));
let label = ErrorLabel {
message: String::from("Only Arrays are allowed here"),
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
};
let item_type = &item_type[0];

View File

@ -1,5 +1,7 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::SEMANTIC_DUPLICATED_REFERENCE, ErrorContainer, ErrorLabel, MistiError,
},
semantic::{impls::SemanticCheck, symbol_table::SymbolTable, types::Type},
syntax::ast::FunctionDeclaration,
};
@ -13,16 +15,24 @@ impl SemanticCheck for FunctionDeclaration<'_> {
// Check that the function is not already defined
if scope.test(&function_name) {
let error = SemanticError {
error_start: self.identifier.position,
error_end: self.identifier.get_end_position(),
reason: format!(
"Duplicated: A symbol with name {} was already defined",
function_name
let (error_start, error_end) =
(self.identifier.position, self.identifier.get_end_position());
let label = ErrorLabel {
message: format!(
"A symbol with name {} was already defined at this scope",
function_name,
),
start: error_start,
end: error_end,
};
return Err(MistiError::Semantic(error));
let econtainer = ErrorContainer {
error_code: SEMANTIC_DUPLICATED_REFERENCE,
error_offset: error_start,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// Create a new scope and use it in the function block

View File

@ -1,5 +1,7 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
},
semantic::{
impls::SemanticCheck,
symbol_table::SymbolTable,
@ -16,14 +18,23 @@ impl SemanticCheck for WhileLoop<'_> {
if !condition_type.equals(&Type::Value("Bool".into())) {
let (error_start, error_end) = condition.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
let label = ErrorLabel {
message: format!(
"Expected a condition of type Bool, found {:?}",
condition_type
),
}));
start: error_start,
end: error_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: error_start,
labels: vec![label],
note: Some(String::from("THP does not have truthy/falsey values.")),
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// TODO: Define scoping rules for while loops

View File

@ -1,5 +1,11 @@
use crate::{
error_handling::{semantic_error::SemanticError, MistiError},
error_handling::{
error_messages::{
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_TYPES,
SEMANTIC_MISSING_REFERENCE,
},
ErrorContainer, ErrorLabel, MistiError,
},
semantic::symbol_table::SymbolTable,
syntax::ast::Expression,
};
@ -19,11 +25,19 @@ impl Typed for Expression<'_> {
let datatype = match scope.get_type(&identifier.value) {
Some(x) => x,
None => {
return Err(MistiError::Semantic(SemanticError {
error_start: identifier.position,
error_end: identifier.get_end_position(),
reason: format!("Cannot find `{}` in this scope.", identifier.value),
}))
let label = ErrorLabel {
message: String::from("Cannot find this identifier in this scope"),
start: identifier.position,
end: identifier.get_end_position(),
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISSING_REFERENCE,
error_offset: identifier.position,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
};
@ -43,20 +57,44 @@ impl Typed for Expression<'_> {
// not the function itself
Ok(Type::Value(return_type))
}
Some(_) => Err(MistiError::Semantic(SemanticError {
error_start: id.position,
error_end: id.get_end_position(),
reason: format!("Expected `{}` to be a function", &id.value),
})),
None => Err(MistiError::Semantic(SemanticError {
error_start: id.position,
error_end: id.get_end_position(),
reason: format!("Cannot find `{}` in this scope.", id.value),
})),
Some(_) => {
let label = ErrorLabel {
message: String::from(
"Expected this identifier to be a function",
),
start: id.position,
end: id.get_end_position(),
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_INVALID_REFERENCE,
error_offset: id.position,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
None => {
let label = ErrorLabel {
message: String::from(
"Cannot find this identifier in this scope",
),
start: id.position,
end: id.get_end_position(),
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_INVALID_REFERENCE,
error_offset: id.position,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
}
}
_ => unimplemented!(
"Get datatype of an expression that resolves into a function call"
"Get datatype of an expression that may resolve into a function call"
),
}
}
@ -64,35 +102,59 @@ impl Typed for Expression<'_> {
let expr_type = match exp.get_type(scope) {
Ok(t) => t,
Err(_reason) => {
return Err(MistiError::Semantic(SemanticError {
error_start: 0,
error_end: 1,
reason: format!("Error getting type of expression"),
}))
let label = ErrorLabel {
message: String::from("Error getting type of this expression"),
// TODO: Fix these positions
start: 0,
end: 1,
};
let econtainer = ErrorContainer {
error_code: COMPILER_TODO,
error_offset: 0,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
};
// Only supported unary operator: - & !
if op.value == "-" {
if !expr_type.is_value("Int") && !expr_type.is_value("Float") {
return Err(MistiError::Semantic(SemanticError {
error_start: 0,
error_end: 1,
reason: format!(
"Expected a Int or Float after unary `-`, got {:?}",
expr_type
),
}));
let label = ErrorLabel {
message: format!("Expected an `Int` or `Float`, got {:?}", expr_type),
// TODO: Fix positioning
start: 0,
end: 1,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: 0,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
} else {
return Ok(Type::Value("Int".into()));
}
} else if op.value == "!" {
if !expr_type.is_value("Bool") {
return Err(MistiError::Semantic(SemanticError {
error_start: 0,
error_end: 1,
reason: format!("Expected a Bool after unary `!`, got {:?}", expr_type),
}));
let label = ErrorLabel {
message: format!("Expected a `Bool`, got {:?}", expr_type),
// TODO: Fix positioning
start: 0,
end: 1,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: 0,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
} else {
return Ok(Type::Value("Bool".into()));
}
@ -105,20 +167,27 @@ impl Typed for Expression<'_> {
let t2 = exp2.get_type(scope)?;
// TODO: There's definitely a better way to do this
// maybe store operators as functions?
if operator.value == "+" && t1.is_value("Int") && t2.is_value("Int") {
return Ok(Type::Value("Int".into()));
} else if operator.value == "-" && t1.is_value("Int") && t2.is_value("Int") {
return Ok(Type::Value("Int".into()));
}
return Err(MistiError::Semantic(SemanticError {
// TODO: fix positions
error_start: 0,
error_end: 1,
reason: format!(
"Unsupported binary operator or invalid arguments to the operator."
),
}));
let label = ErrorLabel {
message: format!("Unsupported binary operator"),
// TODO: Fix positioning
start: 0,
end: 1,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: 0,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
Expression::Array(arr) => {
// The first expression found determines the
@ -129,13 +198,20 @@ impl Typed for Expression<'_> {
// TODO: if the array is empty then its
// datatype should be determined by its usage.
if arr.exps.is_empty() {
return Err(MistiError::Semantic(SemanticError {
error_start: arr.start,
error_end: arr.end,
reason: format!(
"An array must have at least 1 element to determine its type. This will be fixed later."
),
}));
let label = ErrorLabel {
message: format!("Compiler limit: Arrays must have at least 1 element to determine their type"),
// TODO: Fix positioning
start: arr.start,
end: arr.end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_MISMATCHED_TYPES,
error_offset: 0,
labels: vec![label],
note: None,
help: None,
};
return Err(MistiError::Semantic(econtainer));
}
// Just get the first type and use it