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
- [ ] Document code
## v0.0.4
- Explicit datatype of variables
@ -27,6 +26,11 @@
- Compilation of `val` and `var` bindings with a number, string or boolean as value.
- 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

View File

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