Fix bugs and improve error messages
This commit is contained in:
parent
5efcabbfc3
commit
0a22391bae
6
src/lexic/lex_error.rs
Executable file
6
src/lexic/lex_error.rs
Executable 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
17
src/lexic/mod.rs
Normal file → Executable 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
0
src/lexic/scanner/identifier.rs
Normal file → Executable file
3
src/lexic/scanner/mod.rs
Normal file → Executable file
3
src/lexic/scanner/mod.rs
Normal file → Executable 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
36
src/lexic/scanner/number.rs
Normal file → Executable 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
2
src/lexic/scanner/operator.rs
Normal file → Executable 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
16
src/lexic/scanner/string.rs
Normal file → Executable 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
0
src/lexic/utils.rs
Normal file → Executable file
0
src/main.rs
Normal file → Executable file
0
src/main.rs
Normal file → Executable file
14
src/repl/mod.rs
Normal file → Executable file
14
src/repl/mod.rs
Normal file → Executable 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
0
src/syntax/mod.rs
Normal file → Executable file
0
src/token.rs
Normal file → Executable file
0
src/token.rs
Normal file → Executable file
Loading…
Reference in New Issue
Block a user