Fix bugs and improve error messages

master
Araozu 2023-01-05 12:48:34 -05:00
parent 5efcabbfc3
commit 0a22391bae
12 changed files with 71 additions and 23 deletions

6
src/lexic/lex_error.rs Executable file
View File

@ -0,0 +1,6 @@
#[derive(Debug)]
pub struct LexError {
pub position: usize,
pub reason: String,
}

17
src/lexic/mod.rs Normal file → Executable file
View File

@ -1,6 +1,8 @@
mod utils; mod utils;
mod scanner; mod scanner;
mod lex_error;
use super::token::{self, Token}; use super::token::{self, Token};
use lex_error::LexError;
type Chars = Vec<char>; type Chars = Vec<char>;
@ -9,12 +11,12 @@ pub enum LexResult {
Some(Token, usize), Some(Token, usize),
// No token was found, but there was no error (EOF) // No token was found, but there was no error (EOF)
None(usize), None(usize),
Err(String), Err(LexError),
} }
/// Scans and returns all the tokens in the input String /// Scans and returns all the tokens in the input String
pub fn get_tokens(input: &String) -> Result<Vec<Token>, String> { pub fn get_tokens(input: &String) -> Result<Vec<Token>, LexError> {
let chars: Vec<char> = input.chars().into_iter().collect(); let chars: Vec<char> = input.chars().into_iter().collect();
let mut results = Vec::new(); let mut results = Vec::new();
let mut current_pos: usize = 0; let mut current_pos: usize = 0;
@ -44,6 +46,11 @@ fn next_token(chars: &Chars, current_pos: usize) -> LexResult {
return LexResult::None(current_pos) return LexResult::None(current_pos)
} }
// Ignore new lines for now...
if next_char == '\n' {
return next_token(chars, current_pos + 1)
}
// Handle whitespace recursively // Handle whitespace recursively
if next_char == ' ' { if next_char == ' ' {
return next_token(chars, current_pos + 1) return next_token(chars, current_pos + 1)
@ -57,7 +64,11 @@ fn next_token(chars: &Chars, current_pos: usize) -> LexResult {
.or_else(|| scanner::operator(next_char, chars, current_pos)) .or_else(|| scanner::operator(next_char, chars, current_pos))
.or_else(|| scanner::grouping_sign(next_char, chars, current_pos)) .or_else(|| scanner::grouping_sign(next_char, chars, current_pos))
.unwrap_or_else(|| { .unwrap_or_else(|| {
LexResult::Err(format!("Unrecognized character: {}", next_char)) let error = LexError {
position: current_pos,
reason: format!("Unrecognized character: {}", next_char),
};
LexResult::Err(error)
}) })
} }

0
src/lexic/scanner/identifier.rs Normal file → Executable file
View File

3
src/lexic/scanner/mod.rs Normal file → Executable file
View File

@ -40,7 +40,8 @@ pub fn grouping_sign(c: char, _: &Vec<char>, start_pos: usize) -> Option<LexResu
/// Attempts to scan an identifier. Returns None to be able to chain other scanner /// Attempts to scan an identifier. Returns None to be able to chain other scanner
pub fn identifier(c: char, chars: &Vec<char>, start_pos: usize) -> Option<LexResult> { pub fn identifier(c: char, chars: &Vec<char>, start_pos: usize) -> Option<LexResult> {
utils::is_lowercase(c).then(|| identifier::scan(c, chars, start_pos)) (utils::is_lowercase(c) || c == '_')
.then(|| identifier::scan(c, chars, start_pos))
} }

36
src/lexic/scanner/number.rs Normal file → Executable file
View File

@ -1,6 +1,6 @@
use crate::lexic::{ use crate::lexic::{
token::{self, Token}, token::{self, Token},
utils, LexResult, utils, LexResult, lex_error::LexError,
}; };
/// Function to scan a number /// Function to scan a number
@ -47,7 +47,10 @@ fn scan_hex(chars: &Vec<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(String::from("Tried to scan an incomplete hex value")), _ => LexResult::Err(LexError {
position: start_pos,
reason: String::from("Tried to scan an incomplete hex value"),
}),
} }
} }
@ -60,10 +63,16 @@ fn scan_hex(chars: &Vec<char>, start_pos: usize, current: String) -> 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(String::from( Some(_) => LexResult::Err(LexError {
"The character after the dot when scanning a double is not a number.", position: start_pos,
)), reason : String::from(
_ => LexResult::Err(String::from("EOF when scanning a double number.")), "The character after the dot when scanning a double is not a number.",
)
}),
_ => LexResult::Err(LexError {
position: start_pos,
reason: String::from("EOF when scanning a double number."),
}),
} }
} }
@ -98,9 +107,12 @@ 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(String::from( _ => LexResult::Err(LexError {
"The characters after 'e' are not + or -, or are not followed by a number", position: start_pos,
)), reason: String::from(
"The characters after 'e' are not + or -, or are not followed by a number",
)
}),
} }
} }
@ -217,7 +229,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!("Tried to scan an incomplete hex value", reason), LexResult::Err(reason) => assert_eq!("Tried to scan an incomplete hex value", reason.reason),
_ => panic!(), _ => panic!(),
} }
@ -277,7 +289,7 @@ mod tests {
match scan(&input, start_pos) { match scan(&input, start_pos) {
LexResult::Err(reason) => assert_eq!( LexResult::Err(reason) => assert_eq!(
"The character after the dot when scanning a double is not a number.", "The character after the dot when scanning a double is not a number.",
reason reason.reason
), ),
_ => panic!(), _ => panic!(),
} }
@ -286,7 +298,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!("EOF when scanning a double number.", reason), LexResult::Err(reason) => assert_eq!("EOF when scanning a double number.", reason.reason),
_ => panic!(), _ => panic!(),
} }
} }

2
src/lexic/scanner/operator.rs Normal file → Executable file
View File

@ -1,4 +1,4 @@
use crate::lexic::{token::{Token, self}, utils, LexResult}; use crate::lexic::{token, utils, LexResult};
/// Function to scan an operator /// Function to scan an operator

16
src/lexic/scanner/string.rs Normal file → Executable file
View File

@ -1,6 +1,6 @@
use crate::lexic::{ use crate::lexic::{
token::{self, Token}, token,
utils, LexResult, utils, LexResult, lex_error::LexError,
}; };
/// Function to scan a string /// Function to scan a string
@ -17,7 +17,10 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
LexResult::Some(token::new_string(current, start_pos as i32), start_pos + 1) LexResult::Some(token::new_string(current, start_pos as i32), start_pos + 1)
} }
Some(c) if *c == '\n' => { Some(c) if *c == '\n' => {
LexResult::Err(String::from("Unexpected new line inside a string.")) LexResult::Err(LexError {
position: start_pos,
reason: String::from("Unexpected new line inside a string.")
})
} }
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) {
@ -44,7 +47,10 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
) )
} }
None => { None => {
LexResult::Err(String::from("Incomplete string found")) LexResult::Err(LexError {
position: start_pos,
reason: String::from("Incomplete string found")
})
} }
} }
} }
@ -108,7 +114,7 @@ mod tests {
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(reason) = scan(&input, start_pos) {
assert_eq!("Unexpected new line inside a string.", reason) assert_eq!("Unexpected new line inside a string.", reason.reason)
} }
else {panic!()} else {panic!()}
} }

0
src/lexic/utils.rs Normal file → Executable file
View File

0
src/main.rs Normal file → Executable file
View File

14
src/repl/mod.rs Normal file → Executable file
View File

@ -4,6 +4,19 @@ use super::lexic;
fn compile(input: &String) { fn compile(input: &String) {
let _tokens = lexic::get_tokens(input); let _tokens = lexic::get_tokens(input);
match _tokens {
Ok(tokens) => {
for token in tokens {
print!("[{}] ", token.value);
}
println!("");
},
Err(error) => {
eprintln!("Error scanning.\n{} at pos {}", error.reason, error.position)
}
}
} }
pub fn run() -> io::Result<()> { pub fn run() -> io::Result<()> {
@ -23,7 +36,6 @@ pub fn run() -> io::Result<()> {
break Ok(()) break Ok(())
}, },
Ok(_) => { Ok(_) => {
println!("{}", buffer);
compile(&buffer); compile(&buffer);
}, },
Err(error) => { Err(error) => {

0
src/syntax/mod.rs Normal file → Executable file
View File

0
src/token.rs Normal file → Executable file
View File