refactor: begin to implement new error interface

This commit is contained in:
Araozu 2024-09-13 19:02:19 -05:00
parent eed0bb8c76
commit fef5ee8041
6 changed files with 105 additions and 41 deletions

View File

@ -6,18 +6,17 @@
- Implement functions as first class citizens - Implement functions as first class citizens
- Parse __more__ binary operators - Parse __more__ binary operators
- Parse more complex bindings - Parse more complex bindings
- Rework error messages
- Parse other language constructions
- Namespace identifiers in the symbol table - Namespace identifiers in the symbol table
- Stdlib - Stdlib
- Document code - Document code
- Watch mode - Watch mode
- Simple language server - Simple language server
- Decide how to handle comments in the syntax (?)(should comments mean something like in rust?) - 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 - Abstract the parsing of datatypes, such that in the future generics can be implemented in a single place
- Begin work on the code formatter - Begin work on the code formatter
- Remove all panic! and todo! - 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 ## v0.1.2
@ -29,6 +28,7 @@
- [x] Typecheck if/else if/else - [x] Typecheck if/else if/else
- [x] Typecheck for loops - [x] Typecheck for loops
- [x] Typecheck while loops - [x] Typecheck while loops
- [x] Include Ariadne for error reporting
## v0.1.1 ## v0.1.1

4
error_codes.yaml Normal file
View File

@ -0,0 +1,4 @@
-- A map of error codes to error messages
0x000001: Incomplete string

View File

@ -0,0 +1,3 @@
//! Contains constants that point to error messages
pub const LEX_INCOMPLETE_STRING: u32 = 0;

View File

@ -7,14 +7,33 @@ pub mod semantic_error;
mod syntax_error; mod syntax_error;
mod utils; mod utils;
pub mod error_messages;
pub trait PrintableError { pub trait PrintableError {
fn get_error_str(&self, chars: &Vec<char>) -> String; fn get_error_str(&self, chars: &Vec<char>) -> String;
fn print_ariadne(&self, source: &String); fn print_ariadne(&self, source: &String);
} }
#[derive(Serialize, Debug)]
pub struct ErrorContainer {
pub error_code: u32,
pub error_offset: usize,
pub labels: Vec<ErrorLabel>,
pub note: Option<String>,
pub help: Option<String>,
}
/// Mirrors ariadne's Label
#[derive(Serialize, Debug)]
pub struct ErrorLabel {
pub message: String,
pub start: usize,
pub end: usize,
}
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub enum MistiError { pub enum MistiError {
Lex(LexError), Lex(ErrorContainer),
Syntax(SyntaxError), Syntax(SyntaxError),
Semantic(SemanticError), Semantic(SemanticError),
} }
@ -37,7 +56,7 @@ pub struct SyntaxError {
impl PrintableError for MistiError { impl PrintableError for MistiError {
fn get_error_str(&self, chars: &Vec<char>) -> String { fn get_error_str(&self, chars: &Vec<char>) -> String {
match self { 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::Syntax(err) => err.get_error_str(chars),
Self::Semantic(err) => err.get_error_str(chars), Self::Semantic(err) => err.get_error_str(chars),
} }

View File

@ -3,7 +3,7 @@ mod utils;
pub mod token; pub mod token;
use crate::error_handling::{LexError, MistiError}; use crate::error_handling::{ErrorContainer, ErrorLabel, LexError, MistiError};
use token::Token; use token::Token;
use self::token::TokenType; use self::token::TokenType;
@ -36,7 +36,7 @@ pub enum LexResult {
/// Contains the last position, which should be the input lenght - 1 /// Contains the last position, which should be the input lenght - 1
None(usize), None(usize),
/// An error was found while scanning. /// An error was found while scanning.
Err(LexError), Err(ErrorContainer),
} }
/// Scans and returns all the tokens in the input String /// Scans and returns all the tokens in the input String
@ -151,16 +151,20 @@ fn next_token(
} }
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
let error = LexError { let label = ErrorLabel {
position: current_pos, message: String::from("This character is not allowed"),
end_position: current_pos + 1, start: current_pos,
reason: format!( end: current_pos + 1,
"Illegal character `{}` (escaped: {})",
next_char,
next_char.escape_default().to_string(),
),
}; };
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; break;
} else { } else {
// Illegal state: Indentation error // Illegal state: Indentation error
let error = LexError { let econtaner = ErrorContainer {
position: current_pos, error_code: 0,
end_position: current_pos + 1, error_offset: current_pos,
reason: format!( labels: vec![],
"Indentation error: expected {} spaces, found {}", note: None,
new_top, spaces help: None,
),
}; };
return LexResult::Err(error);
return LexResult::Err(econtaner);
} }
} }

View File

@ -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::token::Token;
use crate::lexic::{utils, LexResult}; use crate::lexic::{utils, LexResult};
@ -26,11 +27,27 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
start_pos + 1, start_pos + 1,
) )
} }
Some(c) if *c == '\n' => LexResult::Err(LexError { Some(c) if *c == '\n' => {
position: start_pos, let string_start_pos = start_pos - (current.len() + 1);
end_position: start_pos + 1, let label_2 = ErrorLabel {
reason: String::from("Unexpected new line inside a string."), 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 == '\\' => { Some(c) if *c == '\\' => {
if let Some(escape) = test_escape_char(chars, start_pos + 1) { if let Some(escape) = test_escape_char(chars, start_pos + 1) {
// This should only detect an escaped `"` // This should only detect an escaped `"`
@ -40,11 +57,28 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
} }
} }
Some(c) => scan_impl(chars, start_pos + 1, utils::str_append(current, *c)), Some(c) => scan_impl(chars, start_pos + 1, utils::str_append(current, *c)),
None => LexResult::Err(LexError { None => {
position: start_pos, let string_start_pos = start_pos - (current.len() + 1);
end_position: start_pos + 1, let label_1 = ErrorLabel {
reason: String::from("Incomplete string found"), 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() { fn should_not_scan_a_new_line() {
let input = str_to_vec("\"Hello,\nworld!\""); let input = str_to_vec("\"Hello,\nworld!\"");
let start_pos = 1; let start_pos = 1;
if let LexResult::Err(reason) = scan(&input, start_pos) { if let LexResult::Err(err) = scan(&input, start_pos) {
assert_eq!("Unexpected new line inside a string.", reason.reason) assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
} else { } else {
panic!() panic!()
} }
@ -204,8 +238,8 @@ mod tests {
let result = scan(&input, start_pos); let result = scan(&input, start_pos);
match result { match result {
LexResult::Err(reason) => { LexResult::Err(err) => {
assert_eq!("Incomplete string found", reason.reason) assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
} }
_ => panic!("expected an error"), _ => panic!("expected an error"),
} }
@ -217,8 +251,8 @@ mod tests {
let result = scan(&input, 1); let result = scan(&input, 1);
match result { match result {
LexResult::Err(reason) => { LexResult::Err(err) => {
assert_eq!("Incomplete string found", reason.reason) assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
} }
_ => panic!("expected an error"), _ => panic!("expected an error"),
} }