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 while loops
- [x] Include Ariadne for error reporting
- [x] Migrate lexic errors to new error interface
## v0.1.1

View File

@ -1,4 +1,9 @@
-- A map of error codes to error messages
0x000001: Incomplete string
0x000000: 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::StringLiteral(value) => format!("\"{}\"", value),
PPrimary::Variable(name) => format!("${}", name),
PPrimary::Symbol(name) => format!("{}", name),
// PPrimary::Symbol(name) => format!("{}", name),
PPrimary::BoolLiteral(bool) => {
if *bool {
String::from("true")

View File

@ -1,3 +1,9 @@
//! Contains constants that point to error messages
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),
)
.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 self::semantic_error::SemanticError;
@ -56,7 +59,7 @@ pub struct SyntaxError {
impl PrintableError for MistiError {
fn get_error_str(&self, chars: &Vec<char>) -> String {
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::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();
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();
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;
use crate::error_handling::{ErrorContainer, ErrorLabel, LexError, MistiError};
use crate::error_handling::{ErrorContainer, ErrorLabel, MistiError};
use token::Token;
use self::token::TokenType;

View File

@ -1,6 +1,8 @@
use super::token::Token;
use crate::{
error_handling::LexError,
error_handling::{
error_messages::LEX_INCOMPLETE_MULTILINE_COMMENT, ErrorContainer, ErrorLabel,
},
lexic::{utils, LexResult},
};
@ -40,12 +42,21 @@ pub fn scan_multiline(chars: &Vec<char>, start_pos: usize) -> LexResult {
),
Err(last_position) => {
// Throw an error: Incomplete multiline comment
LexResult::Err(LexError {
position: start_pos,
// TODO: add an end_position
end_position: last_position,
reason: "Unfinished multiline commend".into(),
})
let label = ErrorLabel {
message: String::from("The code ends here without closing the multiline comment"),
// This is minus 1 so we are pointing at something, and not at EOF
start: last_position - 1,
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);
match result {
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")

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};
/// 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));
LexResult::Some(t, next)
}
_ => LexResult::Err(LexError {
position: start_pos,
end_position: start_pos + 1,
reason: String::from("Tried to scan an incomplete hex value"),
}),
_ => {
let label = ErrorLabel {
message: String::from("The hex number ends here, without any digit"),
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() {
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'
position: start_pos - 2,
end_position: current_pos,
reason: String::from("Found an incomplete octal number"),
})
error_offset: current_pos - 2,
labels: vec![label],
note: None,
help: None,
};
LexResult::Err(econtainer)
} else {
let octal_numbers = format!("0o{}", token_vec.iter().collect::<String>());
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() {
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'
position: start_pos - 2,
end_position: current_pos,
reason: String::from("Found an incomplete binary number"),
})
error_offset: current_pos - 2,
labels: vec![label],
note: None,
help: None,
};
LexResult::Err(econtainer)
} else {
let octal_numbers = format!("0b{}", token_vec.iter().collect::<String>());
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 {
match chars.get(start_pos) {
Some(c) if utils::is_digit(*c) => scan_double_impl(chars, start_pos, current),
Some(_) => LexResult::Err(LexError {
position: start_pos,
end_position: start_pos + 1,
reason: String::from(
"The character after the dot when scanning a double is not a number.",
),
}),
_ => LexResult::Err(LexError {
position: start_pos,
end_position: start_pos + 1,
reason: String::from("EOF when scanning a double number."),
}),
Some(_) => {
let label = ErrorLabel {
message: String::from("The floating number ends here, without any digit"),
start: start_pos,
end: start_pos + 1,
};
let econtainer = ErrorContainer {
error_code: LEX_INVALID_FLOATING_NUMBER,
// minus 2 to account for the opening '0b'
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)
}
_ => {
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);
LexResult::Some(t, next)
}
_ => LexResult::Err(LexError {
position: start_pos,
end_position: start_pos + 1,
reason: String::from(
"The characters after 'e' are not + or -, or are not followed by a number",
),
}),
_ => {
let label = ErrorLabel {
message: String::from("The scientific number ends here, incorrectly"),
start: start_pos,
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) {
LexResult::Err(reason) => {
assert_eq!("Tried to scan an incomplete hex value", reason.reason)
assert_eq!(reason.error_code, LEX_INVALID_HEX_NUMBER)
}
_ => panic!(),
}
@ -383,9 +454,8 @@ mod tests {
let result = scan(&input, 0);
match result {
LexResult::Err(error) => {
assert_eq!(error.position, 0);
assert_eq!(error.end_position, 2);
assert_eq!(error.reason, "Found an incomplete octal number");
assert_eq!(error.error_offset, 0);
assert_eq!(error.error_code, LEX_INVALID_OCTAL_NUMBER)
}
_ => panic!("Expected an error, got {:?}", result),
}
@ -412,9 +482,8 @@ mod tests {
let result = scan(&input, 0);
match result {
LexResult::Err(error) => {
assert_eq!(error.position, 0);
assert_eq!(error.end_position, 2);
assert_eq!(error.reason, "Found an incomplete binary number");
assert_eq!(error.error_offset, 0);
assert_eq!(error.error_code, LEX_INVALID_BINARY_NUMBER)
}
_ => panic!("Expected an error, got {:?}", result),
}
@ -453,10 +522,7 @@ mod tests {
let start_pos = 0;
match scan(&input, start_pos) {
LexResult::Err(reason) => assert_eq!(
"The character after the dot when scanning a double is not a number.",
reason.reason
),
LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
_ => panic!(),
}
@ -464,9 +530,7 @@ mod tests {
let start_pos = 0;
match scan(&input, start_pos) {
LexResult::Err(reason) => {
assert_eq!("EOF when scanning a double number.", reason.reason)
}
LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
_ => panic!(),
}
}
@ -567,10 +631,7 @@ mod tests {
match scan(&input, start_pos) {
LexResult::Err(reason) => {
assert_eq!(
"The characters after 'e' are not + or -, or are not followed by a number",
reason.reason
)
assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
}
_ => panic!("Expected an error"),
}
@ -583,10 +644,7 @@ mod tests {
match scan(&input, start_pos) {
LexResult::Err(reason) => {
assert_eq!(
"The characters after 'e' are not + or -, or are not followed by a number",
reason.reason
)
assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
}
_ => panic!("Expected an error"),
}

View File

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

View File

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

View File

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