Add simple error reporting for lexical errors (rebase)

master
Araozu 2023-01-24 10:18:14 -05:00
parent c83cd23d49
commit d8ea08b28b
2 changed files with 45 additions and 17 deletions

View File

@ -11,7 +11,6 @@
- [ ] Stdlib - [ ] Stdlib
- [ ] Document code - [ ] Document code
## v0.0.4 ## v0.0.4
- Explicit datatype of variables - Explicit datatype of variables
@ -27,6 +26,11 @@
- Compilation of `val` and `var` bindings with a number, string or boolean as value. - Compilation of `val` and `var` bindings with a number, string or boolean as value.
- Register symbols and datatypes in the Symbol table. - Register symbols and datatypes in the Symbol table.
- Add better error messages for lexical errors. Show:
- Offending line
- Pointer to offending character
- Error message
## v0.0.1 ## v0.0.1

View File

@ -1,29 +1,47 @@
use std::collections::VecDeque; use std::{collections::VecDeque, fmt::format};
use super::{PrintableError, LexError}; use super::{PrintableError, LexError};
impl PrintableError for LexError { impl PrintableError for LexError {
// TODO: Count and show line number
fn get_error_str(&self, chars: &Vec<char>) -> String { fn get_error_str(&self, chars: &Vec<char>) -> String {
String::from("D:")
let (erroneous_code, back_count) = get_line(chars, self.position);
let mut whitespace = Vec::<char>::new();
for i in 0..back_count {
whitespace.push(' ');
}
let whitespace = whitespace.iter().collect::<String>();
format!(
"\n{}\n{}^\n\n{}{}",
erroneous_code,
whitespace,
"Invalid character at pos ",
self.position + 1,
)
} }
} }
/// Extracts a line of code from `chars`. `pos` indicates a position, /// Extracts a line of code from `chars` and the number of characters in the back.
/// from where to extract the line. /// `pos` indicates a position, from where to extract the line.
/// ///
/// Ex. Given: /// Ex. Given:
/// - `input = "first line\nsecond line\nthird line"` /// - `input = "first line\nsecond line\nthird line"`
/// - `pos = 15` /// - `pos = 15`
/// ///
/// this function should return `"second line"` /// this function should return `("second line", 4)`
fn get_line(chars: &Vec<char>, pos: usize) -> String { fn get_line(chars: &Vec<char>, pos: usize) -> (String, usize) {
let mut result_chars = VecDeque::<char>::new(); let mut result_chars = VecDeque::<char>::new();
// Push chars to the front until a new line is found // Push chars to the front until a new line is found
// TODO: refactor
let mut before_pos = pos; let mut before_pos = pos;
loop { loop {
let current_char = chars[before_pos]; let current_char = chars[before_pos];
if current_char == '\n' { if current_char == '\n' {
before_pos += 1;
break; break;
} }
@ -50,7 +68,7 @@ fn get_line(chars: &Vec<char>, pos: usize) -> String {
after_pos += 1; after_pos += 1;
} }
result_chars.iter().collect::<String>() (result_chars.iter().collect::<String>(), pos - before_pos)
} }
@ -61,7 +79,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn test() { fn test_error_msg() {
let input = String::from("val name' = 20"); let input = String::from("val name' = 20");
let result = lexic::get_tokens(&input); let result = lexic::get_tokens(&input);
@ -70,12 +88,16 @@ mod tests {
Err(err_data) => { Err(err_data) => {
let chars: Vec<char> = input.chars().into_iter().collect(); let chars: Vec<char> = input.chars().into_iter().collect();
let err_str = err_data.get_error_str(&chars); let err_str = err_data.get_error_str(&chars);
assert_ne!(
"\n\ // TODO: check for line number
val name' = 20\n\ let expected_str = format!(
. ^\n\ "\n{}\n{}^\n\nInvalid character at pos 9",
\n\ "val name' = 20",
Invalid character at line 1, pos 9", " "
);
assert_eq!(
expected_str,
err_str, err_str,
); );
} }
@ -87,16 +109,18 @@ mod tests {
let input = String::from("first line\nsecond line\nthird line"); let input = String::from("first line\nsecond line\nthird line");
let chars: Vec<char> = input.chars().into_iter().collect(); let chars: Vec<char> = input.chars().into_iter().collect();
let result = get_line(&chars, 15); let (result, back_count) = get_line(&chars, 15);
assert_eq!("second line", result); assert_eq!("second line", result);
assert_eq!(4, back_count);
let input = String::from("val binding = 322"); let input = String::from("val binding = 322");
let chars: Vec<char> = input.chars().into_iter().collect(); let chars: Vec<char> = input.chars().into_iter().collect();
let result = get_line(&chars, 6); let (result, back_count) = get_line(&chars, 6);
assert_eq!("val binding = 322", result); assert_eq!("val binding = 322", result);
assert_eq!(6, back_count);
} }
} }