From 1a7dd72783ba37ac5547fb0de0b6272a53c813ed Mon Sep 17 00:00:00 2001 From: Araozu Date: Sun, 15 Sep 2024 16:34:46 -0500 Subject: [PATCH] refactor: migrate syntax errors into new struct --- error_codes.yaml | 11 + src/error_handling/error_messages.rs | 16 ++ src/error_handling/mod.rs | 10 +- src/syntax/ast/mod.rs | 8 + src/syntax/functions/arguments_list.rs | 48 +++- src/syntax/functions/params_list.rs | 101 ++++++-- src/syntax/parseable.rs | 4 +- src/syntax/parsers/binding.rs | 197 ++++++++------- src/syntax/parsers/block.rs | 40 ++- src/syntax/parsers/conditional.rs | 184 ++++++++++---- src/syntax/parsers/for_loop.rs | 174 +++++++++---- src/syntax/parsers/function_declaration.rs | 269 ++++++++++++--------- src/syntax/parsers/module.rs | 50 ++-- src/syntax/parsers/while_loop.rs | 79 ++++-- src/syntax/utils.rs | 2 + 15 files changed, 801 insertions(+), 392 deletions(-) diff --git a/error_codes.yaml b/error_codes.yaml index 894dd04..1dd807a 100644 --- a/error_codes.yaml +++ b/error_codes.yaml @@ -6,4 +6,15 @@ 0x000004: Invalid floating point number 0x000005: Invalid scientific number 0x000006: Incomplete multiline comment +0x000007: Unfinished statement +0x000008: Unexpected tokens +0x000009: Incomplete arguments list +0x000010: Invalid variable declaration +0x000011: Incomplete parameter list +0x000012: Invalid parameter declaration +0x000013: Invalid while loop +0x000014: Invalid function declaration +0x000015: Invalid for loop +0x000016: Invalid if condition +0x000017: Incomplete block diff --git a/src/error_handling/error_messages.rs b/src/error_handling/error_messages.rs index 1e1c1c5..2720282 100644 --- a/src/error_handling/error_messages.rs +++ b/src/error_handling/error_messages.rs @@ -7,3 +7,19 @@ pub const LEX_INVALID_BINARY_NUMBER: u32 = 3; pub const LEX_INVALID_FLOATING_NUMBER: u32 = 4; pub const LEX_INVALID_SCIENTIFIC_NUMBER: u32 = 5; pub const LEX_INCOMPLETE_MULTILINE_COMMENT: u32 = 6; +pub const SYNTAX_INCOMPLETE_STATEMENT: u32 = 7; +pub const SYNTAX_UNEXPECTED_TOKENS: u32 = 8; +pub const SYNTAX_INCOMPLETE_ARGUMENT_LIST: u32 = 9; +pub const SYNTAX_INVALID_VARIABLE_DECLARATION: u32 = 10; +pub const SYNTAX_INCOMPLETE_PARAMETER_LIST: u32 = 11; +pub const SYNTAX_INVALID_PARAMETER_DECLARATION: u32 = 12; +pub const SYNTAX_INVALID_WHILE_LOOP: u32 = 13; +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; + +/// Reads the error codes from the error code list +pub fn error_code_to_string() -> String { + todo!() +} diff --git a/src/error_handling/mod.rs b/src/error_handling/mod.rs index 84ac347..b7a6510 100644 --- a/src/error_handling/mod.rs +++ b/src/error_handling/mod.rs @@ -37,7 +37,7 @@ pub struct ErrorLabel { #[derive(Serialize, Debug)] pub enum MistiError { Lex(ErrorContainer), - Syntax(SyntaxError), + Syntax(ErrorContainer), Semantic(SemanticError), } @@ -89,6 +89,14 @@ impl PrintableError for ErrorContainer { report = report.with_label(l) } + if let Some(help) = &self.help { + report = report.with_help(help); + } + + if let Some(note) = &self.note { + report = report.with_help(note); + } + report .with_code(self.error_code) .finish() diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index baccbd5..dada670 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -83,6 +83,14 @@ pub enum BlockMember<'a> { #[derive(Debug)] pub struct ParamsList<'a> { pub parameters: Vec>, + pub start: usize, + pub end: usize, +} + +impl Positionable for ParamsList<'_> { + fn get_position(&self) -> (usize, usize) { + todo!() + } } #[derive(Debug)] diff --git a/src/syntax/functions/arguments_list.rs b/src/syntax/functions/arguments_list.rs index 3c4c5db..4410094 100644 --- a/src/syntax/functions/arguments_list.rs +++ b/src/syntax/functions/arguments_list.rs @@ -1,5 +1,5 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{error_messages::SYNTAX_INCOMPLETE_ARGUMENT_LIST, ErrorContainer, ErrorLabel}, lexic::token::{Token, TokenType}, syntax::{ ast::{functions::ArgumentsList, Expression}, @@ -58,18 +58,44 @@ pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult (t, next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing paren after the function identifier."), - error_start: t.position, - error_end: t.get_end_position(), - })); + let label_1 = ErrorLabel { + message: String::from("The argument list starts here"), + start: opening_paren.position, + end: opening_paren.get_end_position(), + }; + let label = ErrorLabel { + message: String::from("Expected a closing paren `)` here"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_ARGUMENT_LIST, + error_offset: t.position, + labels: vec![label_1, label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing paren after the function identifier."), - error_start: opening_paren.position, - error_end: opening_paren.get_end_position(), - })); + let label_1 = ErrorLabel { + message: String::from("The argument list starts here"), + start: opening_paren.position, + end: opening_paren.get_end_position(), + }; + let label_2 = ErrorLabel { + message: String::from("The code ends here without closing the argument list"), + start: current_pos, + end: current_pos + 1, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_ARGUMENT_LIST, + error_offset: current_pos, + labels: vec![label_1, label_2], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; diff --git a/src/syntax/functions/params_list.rs b/src/syntax/functions/params_list.rs index 94ec476..ecb238e 100644 --- a/src/syntax/functions/params_list.rs +++ b/src/syntax/functions/params_list.rs @@ -1,5 +1,8 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{ + error_messages::{SYNTAX_INCOMPLETE_PARAMETER_LIST, SYNTAX_INVALID_PARAMETER_DECLARATION}, + ErrorContainer, ErrorLabel, + }, lexic::token::{Token, TokenType}, syntax::{utils::parse_token_type, ParsingError, ParsingResult}, }; @@ -60,28 +63,61 @@ pub fn parse_params_list(tokens: &Vec, pos: usize) -> ParsingResult (t, next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing paren after the function identifier."), - error_start: t.position, - error_end: t.get_end_position(), - })); + let label_1 = ErrorLabel { + message: String::from("The parameter list starts here"), + start: opening_paren.position, + end: opening_paren.get_end_position(), + }; + let label = ErrorLabel { + message: String::from("Expected a closing paren `)` here"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_PARAMETER_LIST, + error_offset: t.position, + labels: vec![label_1, label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing paren after the function identifier."), - error_start: opening_paren.position, - error_end: opening_paren.get_end_position(), - })); + let label_1 = ErrorLabel { + message: String::from("The parameter list starts here"), + start: opening_paren.position, + end: opening_paren.get_end_position(), + }; + let label_2 = ErrorLabel { + message: String::from("The code ends here without closing the parameter list"), + start: current_pos, + end: current_pos + 1, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_PARAMETER_LIST, + error_offset: current_pos, + labels: vec![label_1, label_2], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; - Ok((ParamsList { parameters }, current_pos)) + Ok(( + ParamsList { + parameters, + start: opening_paren.position, + end: closing_paren.get_end_position(), + }, + current_pos, + )) } /// Parse a single parameter definition of the form: @@ -112,19 +148,36 @@ fn parse_param_definition(tokens: &Vec, pos: usize) -> ParsingResult { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an identifier for the parameter."), - error_start: tokens[pos].position, - error_end: tokens[pos].get_end_position(), - })); + Err(ParsingError::Mismatch(t)) => { + let label = ErrorLabel { + message: String::from("Expected an identifier here, found this instead"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_PARAMETER_DECLARATION, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an identifier for the parameter."), - error_start: tokens[pos].position, - error_end: tokens[pos].get_end_position(), - })) + let datatype_token = &tokens[pos]; + let label = ErrorLabel { + message: String::from("Expected an identifier after this datatype"), + start: datatype_token.position, + end: datatype_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_PARAMETER_DECLARATION, + error_offset: datatype_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; diff --git a/src/syntax/parseable.rs b/src/syntax/parseable.rs index bc658f6..3f998b1 100644 --- a/src/syntax/parseable.rs +++ b/src/syntax/parseable.rs @@ -1,4 +1,4 @@ -use crate::{error_handling::SyntaxError, lexic::token::Token}; +use crate::{error_handling::ErrorContainer, lexic::token::Token}; /// The result of a parsing operation. /// On success, it contains the item and the position of the next token @@ -15,7 +15,7 @@ pub enum ParsingError<'a> { /// /// For example, when parsing a function declaration /// the `fun` token is found, but then no identifier - Err(SyntaxError), + Err(ErrorContainer), } /// Represents a type that can be parsed using Recursive Descent diff --git a/src/syntax/parsers/binding.rs b/src/syntax/parsers/binding.rs index 21f5c55..024aca7 100644 --- a/src/syntax/parsers/binding.rs +++ b/src/syntax/parsers/binding.rs @@ -1,5 +1,8 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{ + error_messages::{SYNTAX_INCOMPLETE_STATEMENT, SYNTAX_INVALID_VARIABLE_DECLARATION}, + ErrorContainer, ErrorLabel, + }, lexic::token::{Token, TokenType}, syntax::{ ast::{var_binding::VariableBinding, Expression}, @@ -52,31 +55,57 @@ impl<'a> Parseable<'a> for VariableBinding<'a> { Ok((t, n)) => (t, n), Err(ParsingError::Mismatch(token)) => { // The parser found a token, but it's not an identifier - return Err(ParsingError::Err(SyntaxError { - error_start: token.position, - error_end: token.get_end_position(), - reason: "There should be an identifier after a binding".into(), - })); + let label = ErrorLabel { + message: String::from("Expected an identifier here"), + start: token.position, + end: token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } _ => { // The parser didn't find an Identifier after VAL/VAR or the Datatype match (binding_token, datatype) { (Some(binding_token), None) => { - return Err(ParsingError::Err(SyntaxError { - reason: format!( - "There should be an identifier after a `{}` token", + let label = ErrorLabel { + message: format!( + "There should be an identifier after this `{}` token", if is_var { "var" } else { "val" } ), - error_start: binding_token.position, - error_end: binding_token.get_end_position(), - })); + start: binding_token.position, + end: binding_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: binding_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } (_, Some(datatype_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: "There should be an identifier after the datatype".into(), - error_start: datatype_token.position, - error_end: datatype_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from( + "There should be an identifier after this datatype", + ), + start: datatype_token.position, + end: datatype_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: datatype_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } _ => { unreachable!( @@ -94,19 +123,37 @@ impl<'a> Parseable<'a> for VariableBinding<'a> { Ok((t, _)) => t, Err(ParsingError::Mismatch(t)) => { // The parser found a token, but it's not the `=` operator - return Err(ParsingError::Err(SyntaxError { - reason: format!("There should be an equal sign `=` after the identifier"), - error_start: t.position, - error_end: t.get_end_position(), - })); + let label = ErrorLabel { + message: String::from( + "Expected an equal sign `=` here, after the variable identifier", + ), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } _ => { // The parser didn't find the `=` operator after the identifier - return Err(ParsingError::Err(SyntaxError { - reason: format!("There should be an equal sign `=` after the identifier",), - error_start: identifier.position, - error_end: identifier.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected an equal sign `=` after this identifier"), + start: identifier.position, + end: identifier.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: identifier.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; let next_pos = next_pos + 1; @@ -117,11 +164,19 @@ impl<'a> Parseable<'a> for VariableBinding<'a> { let (expression, next_pos) = match Expression::try_parse(tokens, next_pos) { Ok((exp, next)) => (exp, next), _ => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an expression after the equal `=` operator"), - error_start: equal_operator.position, - error_end: equal_operator.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected an expression after this equal `=` operator"), + start: equal_operator.position, + end: equal_operator.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_VARIABLE_DECLARATION, + error_offset: equal_operator.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -130,11 +185,19 @@ impl<'a> Parseable<'a> for VariableBinding<'a> { let next_pos = match parse_terminator(tokens, next_pos) { Ok((_, next)) => next, Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: t.position, - error_end: t.get_end_position(), - reason: format!("Unexpected token `{}`, expected a new line", t.value), - })) + let label = ErrorLabel { + message: String::from("Expected a new line here, found another token"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_STATEMENT, + error_offset: t.position, + labels: vec![label], + note: Some(String::from("There may only be one statement per line")), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } _ => unreachable!(), }; @@ -229,8 +292,7 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(4, error.error_start); - assert_eq!(6, error.error_end); + assert_eq!(4, error.error_offset); } _ => panic!("Error expected"), } @@ -245,8 +307,7 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(0, error.error_start); - assert_eq!(3, error.error_end); + assert_eq!(0, error.error_offset); } _ => panic!("Error expected"), } @@ -261,8 +322,7 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(4, error.error_start); - assert_eq!(7, error.error_end); + assert_eq!(4, error.error_offset); } _ => panic!("Error expected"), } @@ -272,12 +332,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(4, error.error_start); - assert_eq!(11, error.error_end); - assert_eq!( - "There should be an identifier after a binding", - error.reason - ); + assert_eq!(4, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION) } _ => panic!("Error expected"), } @@ -290,8 +346,7 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(7, error.error_start); - assert_eq!(14, error.error_end); + assert_eq!(7, error.error_offset); } _ => panic!("Error expected"), } @@ -304,12 +359,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(4, error.error_start); - assert_eq!(10, error.error_end); - assert_eq!( - "There should be an identifier after the datatype", - error.reason - ); + assert_eq!(4, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION) } _ => panic!("Error expected"), } @@ -322,12 +373,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(0, error.error_start); - assert_eq!(3, error.error_end); - assert_eq!( - "There should be an identifier after a `val` token", - error.reason - ); + assert_eq!(0, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION) } _ => panic!("Error expected"), } @@ -340,12 +387,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(4, error.error_start); - assert_eq!(14, error.error_end); - assert_eq!( - "There should be an equal sign `=` after the identifier", - error.reason - ); + assert_eq!(4, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION) } _ => panic!("Error expected"), } @@ -358,12 +401,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(15, error.error_start); - assert_eq!(16, error.error_end); - assert_eq!( - "Expected an expression after the equal `=` operator", - error.reason - ); + assert_eq!(15, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION) } _ => panic!("Error expected"), } @@ -376,12 +415,8 @@ mod tests { match binding { Err(ParsingError::Err(error)) => { - assert_eq!(21, error.error_start); - assert_eq!(26, error.error_end); - assert_eq!( - "Unexpected token `print`, expected a new line", - error.reason - ); + assert_eq!(21, error.error_offset); + assert_eq!(error.error_code, SYNTAX_INCOMPLETE_STATEMENT) } _ => panic!("Error expected"), } diff --git a/src/syntax/parsers/block.rs b/src/syntax/parsers/block.rs index 6b4aae2..8e3846d 100644 --- a/src/syntax/parsers/block.rs +++ b/src/syntax/parsers/block.rs @@ -1,5 +1,5 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{error_messages::SYNTAX_INCOMPLETE_BLOCK, ErrorContainer, ErrorLabel}, lexic::token::{Token, TokenType}, syntax::{ ast::{Block, BlockMember, Expression, Statement}, @@ -65,20 +65,34 @@ impl<'a> Parseable<'a> for Block<'a> { Ok((t, next)) => (t, next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing brace after the block body."), - error_start: t.position, - error_end: t.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a closing brace `}` here"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_BLOCK, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a closing brace after the block body."), - // TODO: use the last token (at pos current_pos) as guide for the error - // msg position - error_start: opening_brace.position, - error_end: opening_brace.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a closing brace `}` here"), + start: current_pos, + end: current_pos + 1, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_BLOCK, + error_offset: current_pos, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; diff --git a/src/syntax/parsers/conditional.rs b/src/syntax/parsers/conditional.rs index 078bcd6..c26e03a 100644 --- a/src/syntax/parsers/conditional.rs +++ b/src/syntax/parsers/conditional.rs @@ -1,5 +1,5 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{error_messages::SYNTAX_INVALID_IF_CONDITION, ErrorContainer, ErrorLabel}, lexic::token::{Token, TokenType}, syntax::{ ast::{Block, Condition, Conditional, Expression, Positionable}, @@ -23,18 +23,34 @@ impl<'a> Parseable<'a> for Conditional<'a> { Ok(tuple) => tuple, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an expression after the if token"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Bool expression here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an expression after the if token"), - error_start: if_token.position, - error_end: if_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Bool expression after this `if` keyword"), + start: if_token.position, + end: if_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: if_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -43,19 +59,35 @@ impl<'a> Parseable<'a> for Conditional<'a> { Ok(t) => t, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the condition"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a block here, after the condition"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { let (error_start, error_end) = if_expression.get_position(); - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the condition"), - error_start, - error_end, - })); + let label = ErrorLabel { + message: String::from("Expected a block after this condition"), + start: error_start, + end: error_end, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: error_start, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -87,18 +119,34 @@ impl<'a> Parseable<'a> for Conditional<'a> { Ok(tuple) => tuple, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an expression after the if token"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Bool expression here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an expression after the if token"), - error_start: if_token.position, - error_end: if_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Bool expression after this `if` keyword"), + start: if_token.position, + end: if_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: if_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -107,19 +155,35 @@ impl<'a> Parseable<'a> for Conditional<'a> { Ok(t) => t, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the condition"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a block here, after the condition"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { let (error_start, error_end) = condition.get_position(); - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the condition"), - error_start, - error_end, - })); + let label = ErrorLabel { + message: String::from("Expected a block after this condition"), + start: error_start, + end: error_end, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: error_start, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -140,18 +204,36 @@ impl<'a> Parseable<'a> for Conditional<'a> { Ok(t) => t, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the else keyword"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a block here, after the condition"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the else keyword"), - error_start: else_token.position, - error_end: else_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from( + "Expected a block here, after this `else` keyword", + ), + start: else_token.position, + end: else_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_IF_CONDITION, + error_offset: else_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; diff --git a/src/syntax/parsers/for_loop.rs b/src/syntax/parsers/for_loop.rs index 75bac5b..7e2d294 100644 --- a/src/syntax/parsers/for_loop.rs +++ b/src/syntax/parsers/for_loop.rs @@ -1,5 +1,5 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{error_messages::SYNTAX_INVALID_FOR_LOOP, ErrorContainer, ErrorLabel}, lexic::token::{Token, TokenType}, syntax::{ ast::{loops::ForLoop, Block, Expression, Positionable}, @@ -23,21 +23,34 @@ impl<'a> Parseable<'a> for ForLoop<'a> { Ok(t) => t, Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(ParsingError::Mismatch(e)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: e.position, - error_end: e.get_end_position(), - reason: format!( - "Expected an identifier after the `for` keyword, found {}", - e.value - ), - })) + let label = ErrorLabel { + message: String::from("Expected an identifier here, after the `for` keyword"), + start: e.position, + end: e.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: e.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - error_start: for_keyword.position, - error_end: for_keyword.get_end_position(), - reason: format!("Expected an identifier after the `for` keyword"), - })) + let label = ErrorLabel { + message: String::from("Expected an identifier after this `for` keyword"), + start: for_keyword.position, + end: for_keyword.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: for_keyword.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -55,21 +68,38 @@ impl<'a> Parseable<'a> for ForLoop<'a> { Ok((second_id, next)) => (Some(second_id), next), Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: t.position, - error_end: t.get_end_position(), - reason: format!( - "Expected an identifier after the comma, found `{}`", - t.value - ), - })) + let label = ErrorLabel { + message: String::from("Expected an identifier here, after the comma"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: t.position, + labels: vec![label], + note: Some(String::from( + "To iterate only over values, use `for identifier in ...`", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - error_start: comma.position, - error_end: comma.get_end_position(), - reason: format!("Expected an identifier after the comma"), - })); + let label = ErrorLabel { + message: String::from("Expected an identifier after this comma"), + start: comma.position, + end: comma.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: comma.position, + labels: vec![label], + note: Some(String::from( + "To iterate only over values, use `for identifier in ...`", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } } }; @@ -79,11 +109,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> { Ok(tuple) => tuple, Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: t.position, - error_end: t.get_end_position(), - reason: format!("Expected the `in` keyword, found `{}`", t.value), - })) + let label = ErrorLabel { + message: String::from("Expected the `in` keyword here"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { let previous_token = if second_id.is_none() { @@ -91,11 +129,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> { } else { second_id.unwrap() }; - return Err(ParsingError::Err(SyntaxError { - error_start: previous_token.position, - error_end: previous_token.get_end_position(), - reason: format!("Expected the `in` keyword"), - })); + let label = ErrorLabel { + message: String::from("Expected the `in` keyword after this identifier"), + start: previous_token.position, + end: previous_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: previous_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -104,11 +150,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> { Ok(t) => t, Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(_) => { - return Err(ParsingError::Err(SyntaxError { - error_start: in_keyword.position, - error_end: in_keyword.get_end_position(), - reason: format!("Expected an expression after the `in` keyword"), - })) + let label = ErrorLabel { + message: String::from("Expected an expression after this `in` keyword"), + start: in_keyword.position, + end: in_keyword.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: in_keyword.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -117,19 +171,35 @@ impl<'a> Parseable<'a> for ForLoop<'a> { Ok(t) => t, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the collection"), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a block here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { let (error_start, error_end) = expr.get_position(); - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the collection"), - error_start, - error_end, - })); + let label = ErrorLabel { + message: String::from("Expected a block here, after the collection"), + start: error_start, + end: error_end, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FOR_LOOP, + error_offset: error_start, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; diff --git a/src/syntax/parsers/function_declaration.rs b/src/syntax/parsers/function_declaration.rs index ad08193..d07234b 100644 --- a/src/syntax/parsers/function_declaration.rs +++ b/src/syntax/parsers/function_declaration.rs @@ -1,8 +1,10 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{ + error_messages::SYNTAX_INVALID_FUNCTION_DECLARATION, ErrorContainer, ErrorLabel, + }, lexic::token::{Token, TokenType}, syntax::{ - ast::{Block, FunctionDeclaration}, + ast::{Block, FunctionDeclaration, Positionable}, functions::params_list::parse_params_list, parseable::{Parseable, ParsingError, ParsingResult}, utils::{parse_token_type, try_operator}, @@ -28,18 +30,34 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> { Ok((id, next)) => (id, next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an identifier after the `fun` keyword."), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected an identifier here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected an identifier after the `fun` keyword."), - error_start: fun_keyword.position, - error_end: fun_keyword.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected an identifier after this `fun` keyword"), + start: fun_keyword.position, + end: fun_keyword.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: fun_keyword.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; @@ -50,22 +68,38 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> { Ok((params, next_pos)) => (params, next_pos), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from( - "Expected an opening paren after the function identifier.", - ), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a parameter list here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: wrong_token.get_end_position(), + labels: vec![label], + note: Some(String::from( + "If this function doesn't take any parameter, use an empty list `()`", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from( - "Expected an opening paren after the function identifier.", - ), - error_start: identifier.position, - error_end: identifier.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a parameter list after this identifier"), + start: identifier.position, + end: identifier.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: identifier.get_end_position(), + labels: vec![label], + note: Some(String::from( + "If this function doesn't take any parameter, use an empty list `()`", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; @@ -83,18 +117,38 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> { Ok((t, next)) => (Some(t), next), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a datatype after the arrow operator."), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Datatype here"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: wrong_token.position, + labels: vec![label], + note: Some(String::from( + "If you want a function without a return type, omit the arrow as well", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a datatype after the arrow operator."), - error_start: arrow_op.position, - error_end: arrow_op.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a Datatype after this arrow `->` operator"), + start: arrow_op.position, + end: arrow_op.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: arrow_op.position, + labels: vec![label], + note: Some(String::from( + "If you want a function without a return type, omit the arrow as well", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } } }; @@ -107,18 +161,41 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> { return Err(ParsingError::Err(error)); } Err(ParsingError::Mismatch(wrong_token)) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the function declaration."), - error_start: wrong_token.position, - error_end: wrong_token.get_end_position(), - })); + let label = ErrorLabel { + message: String::from("Expected a block here, after the function declaration"), + start: wrong_token.position, + end: wrong_token.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: wrong_token.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - reason: String::from("Expected a block after the function declaration."), - error_start: identifier.position, - error_end: identifier.get_end_position(), - })); + let (error_start, error_end) = { + if let Some(return_type) = return_type { + (return_type.position, return_type.get_end_position()) + } else { + params_list.get_position() + } + }; + let label = ErrorLabel { + message: String::from("Expected a block here, after the function declaration"), + start: error_start, + end: error_end, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, + error_offset: error_start, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; current_pos = next_pos; @@ -159,12 +236,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an identifier after the `fun` keyword." - ); - assert_eq!(err.error_start, 4); - assert_eq!(err.error_end, 5); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 4); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -173,12 +246,8 @@ mod tests { let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an identifier after the `fun` keyword." - ); - assert_eq!(err.error_start, 0); - assert_eq!(err.error_end, 3); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 0); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -191,12 +260,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an opening paren after the function identifier." - ); - assert_eq!(err.error_start, 7); - assert_eq!(err.error_end, 8); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 7); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -205,12 +270,8 @@ mod tests { let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an opening paren after the function identifier." - ); - assert_eq!(err.error_start, 4); - assert_eq!(err.error_end, 6); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 4); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -223,12 +284,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected a closing paren after the function identifier." - ); - assert_eq!(err.error_start, 7); - assert_eq!(err.error_end, 8); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 7); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -237,12 +294,8 @@ mod tests { let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected a closing paren after the function identifier." - ); - assert_eq!(err.error_start, 6); - assert_eq!(err.error_end, 7); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 6); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -255,12 +308,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an identifier after the `fun` keyword." - ); - assert_eq!(err.error_start, 0); - assert_eq!(err.error_end, 3); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 0); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -271,12 +320,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected an identifier after the `fun` keyword." - ); - assert_eq!(err.error_start, 0); - assert_eq!(err.error_end, 3); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 0); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -289,12 +334,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected a block after the function declaration." - ); - assert_eq!(err.error_start, 9); - assert_eq!(err.error_end, 10); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 9); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -303,12 +344,8 @@ mod tests { let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!( - err.reason, - "Expected a block after the function declaration." - ); - assert_eq!(err.error_start, 4); - assert_eq!(err.error_end, 6); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 4); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -321,9 +358,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!(err.reason, "Expected a closing brace after the block body."); - assert_eq!(err.error_start, 9); - assert_eq!(err.error_end, 10); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 9); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -333,9 +369,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!(err.reason, "Expected a closing brace after the block body."); - assert_eq!(err.error_start, 9); - assert_eq!(err.error_end, 10); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 9); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -369,9 +404,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!(err.reason, "Expected a datatype after the arrow operator."); - assert_eq!(err.error_start, 12); - assert_eq!(err.error_end, 13); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 9); } _ => panic!("Expected an error: {:?}", fun_decl), } @@ -384,9 +418,8 @@ mod tests { match fun_decl { Err(ParsingError::Err(err)) => { - assert_eq!(err.reason, "Expected a datatype after the arrow operator."); - assert_eq!(err.error_start, 9); - assert_eq!(err.error_end, 11); + assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); + assert_eq!(err.error_offset, 9); } _ => panic!("Expected an error: {:?}", fun_decl), } diff --git a/src/syntax/parsers/module.rs b/src/syntax/parsers/module.rs index 18f06fd..24cc89f 100644 --- a/src/syntax/parsers/module.rs +++ b/src/syntax/parsers/module.rs @@ -1,5 +1,8 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{ + error_messages::{SYNTAX_INCOMPLETE_STATEMENT, SYNTAX_UNEXPECTED_TOKENS}, + ErrorContainer, ErrorLabel, + }, lexic::token::{Token, TokenType}, syntax::{ ast::{Expression, ModuleAST, ModuleMembers, Statement}, @@ -46,14 +49,23 @@ impl<'a> Parseable<'a> for ModuleAST<'a> { let next_pos = match parse_terminator(tokens, next_pos) { Ok((_, next)) => next, Err(ParsingError::Mismatch(t)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: t.position, - error_end: t.get_end_position(), - reason: format!( - "Unexpected token `{}`, expected a new line", - t.value + let label = ErrorLabel { + message: String::from( + "Expected a new line here, found another token", ), - })) + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INCOMPLETE_STATEMENT, + error_offset: t.position, + labels: vec![label], + note: Some(String::from( + "There may only be one statement per line", + )), + help: None, + }; + return Err(ParsingError::Err(econtainer)); } _ => unreachable!(), }; @@ -78,14 +90,22 @@ impl<'a> Parseable<'a> for ModuleAST<'a> { } } - // If we reached this point we didn't match any productions and fail + // If we reached this point we didn't match any productions and should fail let t = &tokens[current_pos]; - return Err(ParsingError::Err(SyntaxError { - error_start: t.position, - error_end: t.get_end_position(), - reason: "Expected an statement or an expresion at the top level.".into(), - })); + let label = ErrorLabel { + message: String::from("This sequence of tokens couldn't be parsed"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_UNEXPECTED_TOKENS, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Ok((ModuleAST { productions }, current_pos)) @@ -150,7 +170,7 @@ mod test { match result { Ok(_) => panic!("Expected an error"), Err(ParsingError::Err(err)) => { - assert_eq!("Unexpected token `print`, expected a new line", err.reason); + assert_eq!(err.error_code, SYNTAX_INCOMPLETE_STATEMENT) } _ => panic!("Expected a parsing error"), } diff --git a/src/syntax/parsers/while_loop.rs b/src/syntax/parsers/while_loop.rs index 644de84..e016ee4 100644 --- a/src/syntax/parsers/while_loop.rs +++ b/src/syntax/parsers/while_loop.rs @@ -1,5 +1,5 @@ use crate::{ - error_handling::SyntaxError, + error_handling::{error_messages::SYNTAX_INVALID_WHILE_LOOP, ErrorContainer, ErrorLabel}, lexic::token::{Token, TokenType}, syntax::{ ast::{loops::WhileLoop, Block, Expression, Positionable}, @@ -23,21 +23,34 @@ impl<'a> Parseable<'a> for WhileLoop<'a> { Ok(t) => t, Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(ParsingError::Mismatch(e)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: e.position, - error_end: e.get_end_position(), - reason: format!( - "Expected an expression after the `while` keyword, found {}", - e.value - ), - })) + let label = ErrorLabel { + message: String::from("Expected a Bool expression here"), + start: e.position, + end: e.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_WHILE_LOOP, + error_offset: e.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - error_start: while_keyword.position, - error_end: while_keyword.get_end_position(), - reason: format!("Expected an identifier after the `while` keyword"), - })) + let label = ErrorLabel { + message: String::from("Expected a Bool expression after this `while` keyword"), + start: while_keyword.position, + end: while_keyword.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_WHILE_LOOP, + error_offset: while_keyword.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; @@ -46,18 +59,36 @@ impl<'a> Parseable<'a> for WhileLoop<'a> { Ok(t) => t, Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), Err(ParsingError::Mismatch(e)) => { - return Err(ParsingError::Err(SyntaxError { - error_start: e.position, - error_end: e.get_end_position(), - reason: format!("Expected a block after the condition, found {}", e.value), - })) + let label = ErrorLabel { + message: String::from("Expected a block here, after the condition"), + start: e.position, + end: e.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_WHILE_LOOP, + error_offset: e.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } Err(ParsingError::Unmatched) => { - return Err(ParsingError::Err(SyntaxError { - error_start: while_keyword.position, - error_end: while_keyword.get_end_position(), - reason: format!("Expected a block after the condition"), - })) + let (error_start, error_end) = condition.get_position(); + + let label = ErrorLabel { + message: String::from("Expected a block after this condition"), + start: error_start, + end: error_end, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_WHILE_LOOP, + error_offset: error_start, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); } }; diff --git a/src/syntax/utils.rs b/src/syntax/utils.rs index 514e949..f5a1044 100644 --- a/src/syntax/utils.rs +++ b/src/syntax/utils.rs @@ -72,6 +72,8 @@ pub fn parse_token_type( match tokens.get(current_pos) { Some(t) if t.token_type == token_type => Ok((t, current_pos + 1)), + // TODO: Why are we checking if the token is NewLine here? Arent all newlines filtered + // above? Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => { Err(ParsingError::Unmatched) }