refactor: migrate syntax errors into new struct

This commit is contained in:
Araozu 2024-09-13 19:46:43 -05:00
parent fef5ee8041
commit b59d014383
14 changed files with 183 additions and 78 deletions

View File

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

View File

@ -1,4 +1,9 @@
-- A map of error codes to error messages -- A map of error codes to error messages
0x000000: Incomplete string
0x000001: Incomplete string 0x000001: Invalid hex number
0x000002: Invalid octal number
0x000003: Invalid binary number
0x000004: Invalid floating point number
0x000005: Invalid scientific number
0x000006: Incomplete multiline comment

View File

@ -7,7 +7,7 @@ impl Transpilable for PPrimary<'_> {
PPrimary::FloatingLiteral(value) => value.to_string(), PPrimary::FloatingLiteral(value) => value.to_string(),
PPrimary::StringLiteral(value) => format!("\"{}\"", value), PPrimary::StringLiteral(value) => format!("\"{}\"", value),
PPrimary::Variable(name) => format!("${}", name), PPrimary::Variable(name) => format!("${}", name),
PPrimary::Symbol(name) => format!("{}", name), // PPrimary::Symbol(name) => format!("{}", name),
PPrimary::BoolLiteral(bool) => { PPrimary::BoolLiteral(bool) => {
if *bool { if *bool {
String::from("true") String::from("true")

View File

@ -1,3 +1,9 @@
//! Contains constants that point to error messages //! Contains constants that point to error messages
pub const LEX_INCOMPLETE_STRING: u32 = 0; pub const LEX_INCOMPLETE_STRING: u32 = 0;
pub const LEX_INVALID_HEX_NUMBER: u32 = 1;
pub const LEX_INVALID_OCTAL_NUMBER: u32 = 2;
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;

View File

@ -31,7 +31,7 @@ impl PrintableError for LexError {
.with_color(Color::Red), .with_color(Color::Red),
) )
.finish(); .finish();
report.eprint(("sample.thp", Source::from(source))); report.eprint(("sample.thp", Source::from(source))).unwrap();
} }
} }

View File

@ -1,3 +1,6 @@
use std::ops::Range;
use ariadne::{Label, Report, ReportKind, Source};
use serde::Serialize; use serde::Serialize;
use self::semantic_error::SemanticError; use self::semantic_error::SemanticError;
@ -56,7 +59,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) => panic!("REMOVED: manually generating an error message"), Self::Lex(_) => 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),
} }
@ -70,3 +73,26 @@ impl PrintableError for MistiError {
} }
} }
} }
impl PrintableError for ErrorContainer {
fn get_error_str(&self, _: &Vec<char>) -> String {
panic!("REMOVED: manually generating an error message")
}
fn print_ariadne(&self, source: &String) {
let mut report: ariadne::ReportBuilder<'_, (&str, Range<usize>)> =
Report::build(ReportKind::Error, "sample.thp", self.error_offset);
for label in self.labels.iter() {
let l = Label::new(("sample.thp", label.start..label.end))
.with_message(label.message.clone());
report = report.with_label(l)
}
report
.with_code(self.error_code)
.finish()
.eprint(("sample.thp", Source::from(source)))
.unwrap()
}
}

View File

@ -41,6 +41,6 @@ impl PrintableError for SemanticError {
) )
.finish(); .finish();
report.eprint(("sample.thp", Source::from(source))); report.eprint(("sample.thp", Source::from(source))).unwrap();
} }
} }

View File

@ -33,7 +33,7 @@ impl PrintableError for SyntaxError {
) )
.finish(); .finish();
report.eprint(("sample.thp", Source::from(source))); report.eprint(("sample.thp", Source::from(source))).unwrap();
} }
} }

View File

@ -3,7 +3,7 @@ mod utils;
pub mod token; pub mod token;
use crate::error_handling::{ErrorContainer, ErrorLabel, LexError, MistiError}; use crate::error_handling::{ErrorContainer, ErrorLabel, MistiError};
use token::Token; use token::Token;
use self::token::TokenType; use self::token::TokenType;

View File

@ -1,6 +1,8 @@
use super::token::Token; use super::token::Token;
use crate::{ use crate::{
error_handling::LexError, error_handling::{
error_messages::LEX_INCOMPLETE_MULTILINE_COMMENT, ErrorContainer, ErrorLabel,
},
lexic::{utils, LexResult}, lexic::{utils, LexResult},
}; };
@ -40,12 +42,21 @@ pub fn scan_multiline(chars: &Vec<char>, start_pos: usize) -> LexResult {
), ),
Err(last_position) => { Err(last_position) => {
// Throw an error: Incomplete multiline comment // Throw an error: Incomplete multiline comment
LexResult::Err(LexError { let label = ErrorLabel {
position: start_pos, message: String::from("The code ends here without closing the multiline comment"),
// TODO: add an end_position // This is minus 1 so we are pointing at something, and not at EOF
end_position: last_position, start: last_position - 1,
reason: "Unfinished multiline commend".into(), end: last_position,
}) };
let econtainer = ErrorContainer {
error_code: LEX_INCOMPLETE_MULTILINE_COMMENT,
error_offset: last_position,
labels: vec![label],
note: None,
help: Some(String::from("End the multiline comment with `*/`")),
};
LexResult::Err(econtainer)
} }
} }
} }
@ -228,7 +239,7 @@ mod tests {
let result = scan_multiline(&input, 0); let result = scan_multiline(&input, 0);
match result { match result {
LexResult::Err(error) => { LexResult::Err(error) => {
assert_eq!(0, error.position) assert_eq!(error.error_code, LEX_INCOMPLETE_MULTILINE_COMMENT);
} }
_ => { _ => {
panic!("Expected an error scannning an incomplete multiline comment") panic!("Expected an error scannning an incomplete multiline comment")

View File

@ -1,4 +1,8 @@
use crate::error_handling::LexError; use crate::error_handling::error_messages::{
LEX_INVALID_BINARY_NUMBER, LEX_INVALID_FLOATING_NUMBER, LEX_INVALID_HEX_NUMBER,
LEX_INVALID_OCTAL_NUMBER, LEX_INVALID_SCIENTIFIC_NUMBER,
};
use crate::error_handling::{ErrorContainer, ErrorLabel};
use crate::lexic::{token::Token, utils, LexResult}; use crate::lexic::{token::Token, utils, LexResult};
/// Function to scan an int/float /// Function to scan an int/float
@ -57,11 +61,22 @@ fn scan_hex(chars: &[char], start_pos: usize, current: String) -> LexResult {
let (t, next) = scan_hex_digits(chars, start_pos + 1, utils::str_append(current, *c)); let (t, next) = scan_hex_digits(chars, start_pos + 1, utils::str_append(current, *c));
LexResult::Some(t, next) LexResult::Some(t, next)
} }
_ => LexResult::Err(LexError { _ => {
position: start_pos, let label = ErrorLabel {
end_position: start_pos + 1, message: String::from("The hex number ends here, without any digit"),
reason: String::from("Tried to scan an incomplete hex value"), start: start_pos,
}), end: start_pos + 1,
};
let econtainer = ErrorContainer {
error_code: LEX_INVALID_HEX_NUMBER,
error_offset: start_pos,
labels: vec![label],
note: None,
help: None,
};
LexResult::Err(econtainer)
}
} }
} }
@ -82,12 +97,21 @@ fn scan_octal(chars: &[char], start_pos: usize) -> LexResult {
} }
if token_vec.is_empty() { if token_vec.is_empty() {
LexResult::Err(LexError { let label = ErrorLabel {
message: String::from("The octal number ends here, without any digit"),
start: current_pos,
end: current_pos + 1,
};
let econtainer = ErrorContainer {
error_code: LEX_INVALID_OCTAL_NUMBER,
// minus 2 to account for the opening '0o' // minus 2 to account for the opening '0o'
position: start_pos - 2, error_offset: current_pos - 2,
end_position: current_pos, labels: vec![label],
reason: String::from("Found an incomplete octal number"), note: None,
}) help: None,
};
LexResult::Err(econtainer)
} else { } else {
let octal_numbers = format!("0o{}", token_vec.iter().collect::<String>()); let octal_numbers = format!("0o{}", token_vec.iter().collect::<String>());
let new_token = Token::new_int(octal_numbers, start_pos - 2); let new_token = Token::new_int(octal_numbers, start_pos - 2);
@ -113,12 +137,21 @@ fn scan_binary(chars: &[char], start_pos: usize) -> LexResult {
} }
if token_vec.is_empty() { if token_vec.is_empty() {
LexResult::Err(LexError { let label = ErrorLabel {
message: String::from("The binary number ends here, without any digit"),
start: current_pos,
end: current_pos + 1,
};
let econtainer = ErrorContainer {
error_code: LEX_INVALID_BINARY_NUMBER,
// minus 2 to account for the opening '0b' // minus 2 to account for the opening '0b'
position: start_pos - 2, error_offset: current_pos - 2,
end_position: current_pos, labels: vec![label],
reason: String::from("Found an incomplete binary number"), note: None,
}) help: None,
};
LexResult::Err(econtainer)
} else { } else {
let octal_numbers = format!("0b{}", token_vec.iter().collect::<String>()); let octal_numbers = format!("0b{}", token_vec.iter().collect::<String>());
let new_token = Token::new_int(octal_numbers, start_pos - 2); let new_token = Token::new_int(octal_numbers, start_pos - 2);
@ -135,18 +168,45 @@ fn scan_binary(chars: &[char], start_pos: usize) -> LexResult {
fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> LexResult { fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> LexResult {
match chars.get(start_pos) { match chars.get(start_pos) {
Some(c) if utils::is_digit(*c) => scan_double_impl(chars, start_pos, current), Some(c) if utils::is_digit(*c) => scan_double_impl(chars, start_pos, current),
Some(_) => LexResult::Err(LexError { Some(_) => {
position: start_pos, let label = ErrorLabel {
end_position: start_pos + 1, message: String::from("The floating number ends here, without any digit"),
reason: String::from( start: start_pos,
"The character after the dot when scanning a double is not a number.", end: start_pos + 1,
), };
}), let econtainer = ErrorContainer {
_ => LexResult::Err(LexError { error_code: LEX_INVALID_FLOATING_NUMBER,
position: start_pos, // minus 2 to account for the opening '0b'
end_position: start_pos + 1, error_offset: start_pos,
reason: String::from("EOF when scanning a double number."), labels: vec![label],
}), note: Some(String::from(
"Floating point numbers must always have at least 1 digit after the period",
)),
help: None,
};
LexResult::Err(econtainer)
}
_ => {
let label = ErrorLabel {
message: String::from(
"The code ends here, without completing the floating point number",
),
start: start_pos,
end: start_pos + 1,
};
let econtainer = ErrorContainer {
error_code: LEX_INVALID_FLOATING_NUMBER,
error_offset: start_pos,
labels: vec![label],
note: Some(String::from(
"Floating point numbers must always have at least 1 digit after the period",
)),
help: None,
};
LexResult::Err(econtainer)
}
} }
} }
@ -190,13 +250,24 @@ fn scan_scientific(chars: &Vec<char>, start_pos: usize, current: String) -> LexR
let (t, next) = scan_digits(chars, start_pos + 2, new_value); let (t, next) = scan_digits(chars, start_pos + 2, new_value);
LexResult::Some(t, next) LexResult::Some(t, next)
} }
_ => LexResult::Err(LexError { _ => {
position: start_pos, let label = ErrorLabel {
end_position: start_pos + 1, message: String::from("The scientific number ends here, incorrectly"),
reason: String::from( start: start_pos,
"The characters after 'e' are not + or -, or are not followed by a number", end: start_pos + 1,
), };
}), let econtainer = ErrorContainer {
error_code: LEX_INVALID_SCIENTIFIC_NUMBER,
error_offset: start_pos,
labels: vec![label],
note: Some(String::from(
"Scientific numbers must always have a sign (+ or -) and a digit afterwards: `3.22e+2`"
)),
help: None,
};
LexResult::Err(econtainer)
}
} }
} }
@ -334,7 +405,7 @@ mod tests {
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => { LexResult::Err(reason) => {
assert_eq!("Tried to scan an incomplete hex value", reason.reason) assert_eq!(reason.error_code, LEX_INVALID_HEX_NUMBER)
} }
_ => panic!(), _ => panic!(),
} }
@ -383,9 +454,8 @@ mod tests {
let result = scan(&input, 0); let result = scan(&input, 0);
match result { match result {
LexResult::Err(error) => { LexResult::Err(error) => {
assert_eq!(error.position, 0); assert_eq!(error.error_offset, 0);
assert_eq!(error.end_position, 2); assert_eq!(error.error_code, LEX_INVALID_OCTAL_NUMBER)
assert_eq!(error.reason, "Found an incomplete octal number");
} }
_ => panic!("Expected an error, got {:?}", result), _ => panic!("Expected an error, got {:?}", result),
} }
@ -412,9 +482,8 @@ mod tests {
let result = scan(&input, 0); let result = scan(&input, 0);
match result { match result {
LexResult::Err(error) => { LexResult::Err(error) => {
assert_eq!(error.position, 0); assert_eq!(error.error_offset, 0);
assert_eq!(error.end_position, 2); assert_eq!(error.error_code, LEX_INVALID_BINARY_NUMBER)
assert_eq!(error.reason, "Found an incomplete binary number");
} }
_ => panic!("Expected an error, got {:?}", result), _ => panic!("Expected an error, got {:?}", result),
} }
@ -453,10 +522,7 @@ mod tests {
let start_pos = 0; let start_pos = 0;
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => assert_eq!( LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
"The character after the dot when scanning a double is not a number.",
reason.reason
),
_ => panic!(), _ => panic!(),
} }
@ -464,9 +530,7 @@ mod tests {
let start_pos = 0; let start_pos = 0;
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => { LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
assert_eq!("EOF when scanning a double number.", reason.reason)
}
_ => panic!(), _ => panic!(),
} }
} }
@ -567,10 +631,7 @@ mod tests {
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => { LexResult::Err(reason) => {
assert_eq!( assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
"The characters after 'e' are not + or -, or are not followed by a number",
reason.reason
)
} }
_ => panic!("Expected an error"), _ => panic!("Expected an error"),
} }
@ -583,10 +644,7 @@ mod tests {
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => { LexResult::Err(reason) => {
assert_eq!( assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
"The characters after 'e' are not + or -, or are not followed by a number",
reason.reason
)
} }
_ => panic!("Expected an error"), _ => panic!("Expected an error"),
} }

View File

@ -59,6 +59,6 @@ pub enum PPrimary<'a> {
/// ///
/// This is a $variable /// This is a $variable
Variable(&'a String), Variable(&'a String),
/// This is a symbol, e.g. a function name // This is a symbol, e.g. a function name
Symbol(&'a String), // Symbol(&'a String),
} }

View File

@ -1,7 +1,5 @@
use std::io::{self, Write}; use std::io::{self, Write};
use colored::Colorize;
use crate::codegen::Transpilable; use crate::codegen::Transpilable;
use crate::error_handling::PrintableError; use crate::error_handling::PrintableError;

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
error_handling::{semantic_error::SemanticError, MistiError}, error_handling::{semantic_error::SemanticError, MistiError},
semantic::symbol_table::SymbolTable, semantic::symbol_table::SymbolTable,
syntax::ast::{Expression, Positionable}, syntax::ast::Expression,
}; };
use super::{Type, Typed}; use super::{Type, Typed};