diff --git a/CHANGELOG.md b/CHANGELOG.md index cff4b78..0539f41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,18 +6,17 @@ - Implement functions as first class citizens - Parse __more__ binary operators - Parse more complex bindings -- Rework error messages -- Parse other language constructions - Namespace identifiers in the symbol table - Stdlib - Document code - Watch mode - Simple language server - Decide how to handle comments in the syntax (?)(should comments mean something like in rust?) -- Fix comment handling in the AST - Abstract the parsing of datatypes, such that in the future generics can be implemented in a single place - Begin work on the code formatter - Remove all panic! and todo! +- Change REPL to execute code only after `;;` is found +- Forward the code generated by the REPL to the PHP repl ## v0.1.2 @@ -29,6 +28,7 @@ - [x] Typecheck if/else if/else - [x] Typecheck for loops - [x] Typecheck while loops +- [x] Include Ariadne for error reporting ## v0.1.1 diff --git a/error_codes.yaml b/error_codes.yaml new file mode 100644 index 0000000..106af20 --- /dev/null +++ b/error_codes.yaml @@ -0,0 +1,4 @@ +-- A map of error codes to error messages + +0x000001: Incomplete string + diff --git a/src/error_handling/error_messages.rs b/src/error_handling/error_messages.rs new file mode 100644 index 0000000..da61168 --- /dev/null +++ b/src/error_handling/error_messages.rs @@ -0,0 +1,3 @@ +//! Contains constants that point to error messages + +pub const LEX_INCOMPLETE_STRING: u32 = 0; diff --git a/src/error_handling/mod.rs b/src/error_handling/mod.rs index 7596e6d..7ac26bf 100644 --- a/src/error_handling/mod.rs +++ b/src/error_handling/mod.rs @@ -7,14 +7,33 @@ pub mod semantic_error; mod syntax_error; mod utils; +pub mod error_messages; + pub trait PrintableError { fn get_error_str(&self, chars: &Vec) -> String; fn print_ariadne(&self, source: &String); } +#[derive(Serialize, Debug)] +pub struct ErrorContainer { + pub error_code: u32, + pub error_offset: usize, + pub labels: Vec, + pub note: Option, + pub help: Option, +} + +/// Mirrors ariadne's Label +#[derive(Serialize, Debug)] +pub struct ErrorLabel { + pub message: String, + pub start: usize, + pub end: usize, +} + #[derive(Serialize, Debug)] pub enum MistiError { - Lex(LexError), + Lex(ErrorContainer), Syntax(SyntaxError), Semantic(SemanticError), } @@ -37,7 +56,7 @@ pub struct SyntaxError { impl PrintableError for MistiError { fn get_error_str(&self, chars: &Vec) -> String { match self { - Self::Lex(err) => err.get_error_str(chars), + Self::Lex(err) => panic!("REMOVED: manually generating an error message"), Self::Syntax(err) => err.get_error_str(chars), Self::Semantic(err) => err.get_error_str(chars), } diff --git a/src/lexic/mod.rs b/src/lexic/mod.rs index bb5db37..43d4d5a 100755 --- a/src/lexic/mod.rs +++ b/src/lexic/mod.rs @@ -3,7 +3,7 @@ mod utils; pub mod token; -use crate::error_handling::{LexError, MistiError}; +use crate::error_handling::{ErrorContainer, ErrorLabel, LexError, MistiError}; use token::Token; use self::token::TokenType; @@ -36,7 +36,7 @@ pub enum LexResult { /// Contains the last position, which should be the input lenght - 1 None(usize), /// An error was found while scanning. - Err(LexError), + Err(ErrorContainer), } /// Scans and returns all the tokens in the input String @@ -151,16 +151,20 @@ fn next_token( } }) .unwrap_or_else(|| { - let error = LexError { - position: current_pos, - end_position: current_pos + 1, - reason: format!( - "Illegal character `{}` (escaped: {})", - next_char, - next_char.escape_default().to_string(), - ), + let label = ErrorLabel { + message: String::from("This character is not allowed"), + start: current_pos, + end: current_pos + 1, }; - LexResult::Err(error) + let error_container = ErrorContainer { + error_offset: current_pos, + error_code: 0x010001, + labels: vec![label], + note: None, + help: Some(String::from("Remove this character")), + }; + + LexResult::Err(error_container) }) } @@ -196,15 +200,15 @@ fn handle_indentation( break; } else { // Illegal state: Indentation error - let error = LexError { - position: current_pos, - end_position: current_pos + 1, - reason: format!( - "Indentation error: expected {} spaces, found {}", - new_top, spaces - ), + let econtaner = ErrorContainer { + error_code: 0, + error_offset: current_pos, + labels: vec![], + note: None, + help: None, }; - return LexResult::Err(error); + + return LexResult::Err(econtaner); } } diff --git a/src/lexic/scanner/string.rs b/src/lexic/scanner/string.rs index 06f61e7..eb1191e 100755 --- a/src/lexic/scanner/string.rs +++ b/src/lexic/scanner/string.rs @@ -1,4 +1,5 @@ -use crate::error_handling::LexError; +use crate::error_handling::error_messages::LEX_INCOMPLETE_STRING; +use crate::error_handling::{ErrorContainer, ErrorLabel}; use crate::lexic::token::Token; use crate::lexic::{utils, LexResult}; @@ -26,11 +27,27 @@ pub fn scan_impl(chars: &Vec, start_pos: usize, current: String) -> LexRes start_pos + 1, ) } - Some(c) if *c == '\n' => LexResult::Err(LexError { - position: start_pos, - end_position: start_pos + 1, - reason: String::from("Unexpected new line inside a string."), - }), + Some(c) if *c == '\n' => { + let string_start_pos = start_pos - (current.len() + 1); + let label_2 = ErrorLabel { + message: String::from("The line ends here"), + start: start_pos, + end: start_pos + 1, + }; + let label_1 = ErrorLabel { + message: String::from("The string starts here"), + start: string_start_pos, + end: string_start_pos + 1, + }; + let econtainer = ErrorContainer { + error_code: LEX_INCOMPLETE_STRING, + error_offset: start_pos, + labels: vec![label_1, label_2], + note: Some(String::from("Strings cannot have newlines")), + help: None, + }; + LexResult::Err(econtainer) + } Some(c) if *c == '\\' => { if let Some(escape) = test_escape_char(chars, start_pos + 1) { // This should only detect an escaped `"` @@ -40,11 +57,28 @@ pub fn scan_impl(chars: &Vec, start_pos: usize, current: String) -> LexRes } } Some(c) => scan_impl(chars, start_pos + 1, utils::str_append(current, *c)), - None => LexResult::Err(LexError { - position: start_pos, - end_position: start_pos + 1, - reason: String::from("Incomplete string found"), - }), + None => { + let string_start_pos = start_pos - (current.len() + 1); + let label_1 = ErrorLabel { + message: String::from("The string starts here"), + start: string_start_pos, + end: string_start_pos + 1, + }; + let label_2 = ErrorLabel { + message: String::from("The code ends here"), + start: start_pos, + end: start_pos + 1, + }; + let econtainer = ErrorContainer { + error_code: LEX_INCOMPLETE_STRING, + error_offset: start_pos, + labels: vec![label_1, label_2], + note: None, + help: None, + }; + + LexResult::Err(econtainer) + } } } @@ -107,8 +141,8 @@ mod tests { fn should_not_scan_a_new_line() { let input = str_to_vec("\"Hello,\nworld!\""); let start_pos = 1; - if let LexResult::Err(reason) = scan(&input, start_pos) { - assert_eq!("Unexpected new line inside a string.", reason.reason) + if let LexResult::Err(err) = scan(&input, start_pos) { + assert_eq!(LEX_INCOMPLETE_STRING, err.error_code) } else { panic!() } @@ -204,8 +238,8 @@ mod tests { let result = scan(&input, start_pos); match result { - LexResult::Err(reason) => { - assert_eq!("Incomplete string found", reason.reason) + LexResult::Err(err) => { + assert_eq!(LEX_INCOMPLETE_STRING, err.error_code) } _ => panic!("expected an error"), } @@ -217,8 +251,8 @@ mod tests { let result = scan(&input, 1); match result { - LexResult::Err(reason) => { - assert_eq!("Incomplete string found", reason.reason) + LexResult::Err(err) => { + assert_eq!(LEX_INCOMPLETE_STRING, err.error_code) } _ => panic!("expected an error"), }