diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d0587..26c2500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 13b15ba..3bfe35f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,7 +152,7 @@ dependencies = [ [[package]] name = "thp" -version = "0.1.1" +version = "0.1.2" dependencies = [ "ariadne", "colored", diff --git a/Cargo.toml b/Cargo.toml index 5ee6cc9..48c40f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thp" -version = "0.1.1" +version = "0.1.2" edition = "2021" diff --git a/error_codes.yaml b/error_codes.yaml index 1dd807a..3861d0c 100644 --- a/error_codes.yaml +++ b/error_codes.yaml @@ -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 diff --git a/src/error_handling/error_messages.rs b/src/error_handling/error_messages.rs index 2720282..ea01446 100644 --- a/src/error_handling/error_messages.rs +++ b/src/error_handling/error_messages.rs @@ -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 { diff --git a/src/error_handling/lex_error.rs b/src/error_handling/lex_error.rs deleted file mode 100644 index 614a5ec..0000000 --- a/src/error_handling/lex_error.rs +++ /dev/null @@ -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) -> 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, pos: usize) -> (String, usize) { - let mut result_chars = VecDeque::::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::(), pos - before_pos) -} - -fn get_line_number(chars: &Vec, 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 = 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 = 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 = 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 = 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); - } -} diff --git a/src/error_handling/mod.rs b/src/error_handling/mod.rs index b7a6510..127bd97 100644 --- a/src/error_handling/mod.rs +++ b/src/error_handling/mod.rs @@ -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 { diff --git a/src/error_handling/syntax_error.rs b/src/error_handling/syntax_error.rs deleted file mode 100644 index ff42e3a..0000000 --- a/src/error_handling/syntax_error.rs +++ /dev/null @@ -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) -> 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::(); - let indicator = vec!['^'; length].iter().collect::(); - 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, 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 = input.chars().into_iter().collect(); - - (chars, error) - } - } - } - - #[test] - fn should_get_line() { - let input: Vec = 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); - } -} diff --git a/src/semantic/checks/binding.rs b/src/semantic/checks/binding.rs index f6994fd..dfeef7b 100644 --- a/src/semantic/checks/binding.rs +++ b/src/semantic/checks/binding.rs @@ -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); diff --git a/src/semantic/checks/conditional.rs b/src/semantic/checks/conditional.rs index f866643..dfb18ee 100644 --- a/src/semantic/checks/conditional.rs +++ b/src/semantic/checks/conditional.rs @@ -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)?; diff --git a/src/semantic/checks/expression.rs b/src/semantic/checks/expression.rs index 5f34342..4c113ab 100644 --- a/src/semantic/checks/expression.rs +++ b/src/semantic/checks/expression.rs @@ -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)); } } diff --git a/src/semantic/checks/for_loop.rs b/src/semantic/checks/for_loop.rs index 917a7b8..a0ee116 100644 --- a/src/semantic/checks/for_loop.rs +++ b/src/semantic/checks/for_loop.rs @@ -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]; diff --git a/src/semantic/checks/function_declaration.rs b/src/semantic/checks/function_declaration.rs index 1f3735d..2435f18 100644 --- a/src/semantic/checks/function_declaration.rs +++ b/src/semantic/checks/function_declaration.rs @@ -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 diff --git a/src/semantic/checks/while_loop.rs b/src/semantic/checks/while_loop.rs index 22ad9e6..9a8c8cf 100644 --- a/src/semantic/checks/while_loop.rs +++ b/src/semantic/checks/while_loop.rs @@ -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 diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index e0a38fb..04c8479 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -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