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 scanner;
mod lex_error;
use super::token::{self, Token};
use lex_error::LexError;
type Chars = Vec<char>;
@ -9,12 +11,12 @@ pub enum LexResult {
Some(Token, usize),
// No token was found, but there was no error (EOF)
None(usize),
Err(String),
Err(LexError),
}
/// 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 mut results = Vec::new();
let mut current_pos: usize = 0;
@ -44,6 +46,11 @@ fn next_token(chars: &Chars, current_pos: usize) -> LexResult {
return LexResult::None(current_pos)
}
// Ignore new lines for now...
if next_char == '\n' {
return next_token(chars, current_pos + 1)
}
// Handle whitespace recursively
if next_char == ' ' {
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::grouping_sign(next_char, chars, current_pos))
.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
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::{
token::{self, Token},
utils, LexResult,
utils, LexResult, lex_error::LexError,
};
/// 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));
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 {
match chars.get(start_pos) {
Some(c) if utils::is_digit(*c) => scan_double_impl(chars, start_pos, current),
Some(_) => LexResult::Err(String::from(
"The character after the dot when scanning a double is not a number.",
)),
_ => LexResult::Err(String::from("EOF when scanning a double number.")),
Some(_) => LexResult::Err(LexError {
position: start_pos,
reason : String::from(
"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);
LexResult::Some(t, next)
}
_ => LexResult::Err(String::from(
"The characters after 'e' are not + or -, or are not followed by a number",
)),
_ => LexResult::Err(LexError {
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;
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!(),
}
@ -277,7 +289,7 @@ mod tests {
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.reason
),
_ => panic!(),
}
@ -286,7 +298,7 @@ mod tests {
let start_pos = 0;
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!(),
}
}

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

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

@ -1,6 +1,6 @@
use crate::lexic::{
token::{self, Token},
utils, LexResult,
token,
utils, LexResult, lex_error::LexError,
};
/// 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)
}
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 == '\\' => {
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 => {
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 start_pos = 1;
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!()}
}

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) {
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<()> {
@ -23,7 +36,6 @@ pub fn run() -> io::Result<()> {
break Ok(())
},
Ok(_) => {
println!("{}", buffer);
compile(&buffer);
},
Err(error) => {

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

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