Simple scientific notation scanning

master
Araozu 2022-11-29 20:13:48 -05:00
parent 2c17557aad
commit 53fd824e8d
3 changed files with 72 additions and 14 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target
.vscode
*.profraw

View File

@ -131,8 +131,6 @@ mod tests {
assert_eq!(TokenType::Number, t3.token_type);
assert_eq!("0.282398", t3.value);
/*
assert_eq!("278.98", tokens.get(1).unwrap().value);
assert_eq!("0.282398", tokens.get(2).unwrap().value);
assert_eq!("1798e+1", tokens.get(3).unwrap().value);
assert_eq!("239.3298e-103", tokens.get(4).unwrap().value);
assert_eq!(TokenType::EOF, tokens.get(5).unwrap().token_type);

View File

@ -25,12 +25,12 @@ pub fn scan(chars: &Vec<char>, start_pos: usize) -> Result<(Token, usize), Strin
}
/// Recursively scans an integer, and if a dot `.` is found, scans a double.
///
/// It may fail due to scanning a double.
/// Recursively scans an integer. If a dot `.` is found, scans a double,
/// if a `e` is found, scans a number in scientific notation
fn scan_decimal(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
let next_char = chars.get(start_pos);
// If a char is found
if let Some(c) = next_char {
let c = *c;
@ -39,12 +39,14 @@ fn scan_decimal(chars: &Vec<char>, start_pos: usize, current: String) -> Result<
if c == '.' {
return scan_double(chars, start_pos + 1, utils::str_append(current, c))
}
// If a `e` is found scan a double with exponent
else if c == 'e' {
return scan_scientific(chars, start_pos + 1, utils::str_append(current, c))
}
// Scan a decimal number
else if utils::is_digit(c) {
let new_value = format!("{}{}", current, c);
return scan_decimal(chars, start_pos + 1, new_value)
return scan_decimal(chars, start_pos + 1, utils::str_append(current, c))
}
}
// Return the current value
@ -80,15 +82,16 @@ fn scan_hex(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Tok
/// Scans a floating point number
///
/// This function expects the following on the first call:
/// - The char at `start_pos` is a value between [0-9]. If not will return an error.
/// This function expects the following:
/// - `start_pos` is the position after the dot. E.g., if the input is `3.22` then `start_pos == 2`.
///
/// Returns a syntax error if the char at `start_pos` is not a value between [0-9]
fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
let next_char = chars.get(start_pos);
// Check that the first characters exists and is a number
if let Some(c) = next_char {
if utils::is_digit(*c) { Ok(scan_double_impl(chars, start_pos, current)) }
if utils::is_digit(*c) { Ok(scan_digits(chars, start_pos, current)) }
else {Err(String::from("The character after the dot when scanning a double is not a number."))}
}
else {
@ -96,15 +99,38 @@ fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(
}
}
// Implementation of scan_double
fn scan_double_impl(chars: &Vec<char>, start_pos: usize, current: String) -> (Token, usize) {
/// Scans a double in scientific notation
///
/// This function expects the following:
/// - `start_pos` is the position after the `e`. E.g., if the input is `3e+10` then `start_pos == 2`
///
/// Returns a syntax error if:
/// - The char at `start_pos` is not `+` or `-`
/// - The char at `start_pos + 1` is not between [0-9]
fn scan_scientific(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
let next_char_1 = chars.get(start_pos);
let next_char_2 = chars.get(start_pos + 1);
match (next_char_1, next_char_2) {
(Some(c1), Some(c2)) if (*c1 == '+' || *c1 == '-') && utils::is_digit(*c2) => {
let new_value = format!("{}{}{}", current, *c1, *c2);
Ok(scan_digits(chars, start_pos + 2, new_value))
},
_ => Err(String::from("The characters after 'e' are not + or -, or are not followed by a number"))
}
}
/// Scans chars between [0-9], returns when none is found
fn scan_digits(chars: &Vec<char>, start_pos: usize, current: String) -> (Token, usize) {
let next_char = chars.get(start_pos);
if let Some(c) = next_char {
let c = *c;
if utils::is_digit(c) {
return scan_double_impl(chars, start_pos + 1, utils::str_append(current, c))
return scan_digits(chars, start_pos + 1, utils::str_append(current, c))
}
}
@ -255,4 +281,37 @@ mod tests {
Err(reason) => assert_eq!("EOF when scanning a double number.", reason)
}
}
// Should scan a double without decimal part, with exponent
#[test]
fn test_exp_1() {
let input = str_to_vec("1e+0");
let start_pos = 0;
let (token, next) = scan(&input, start_pos).unwrap();
assert_eq!("1e+0", token.value);
assert_eq!(4, next);
assert_eq!(TokenType::Number, token.token_type);
let input = str_to_vec("1e-0");
let start_pos = 0;
let (token, next) = scan(&input, start_pos).unwrap();
assert_eq!(4, next);
assert_eq!(TokenType::Number, token.token_type);
assert_eq!("1e-0", token.value);
let input = str_to_vec("0e+0");
let start_pos = 0;
let (token, next) = scan(&input, start_pos).unwrap();
assert_eq!(4, next);
assert_eq!(TokenType::Number, token.token_type);
assert_eq!("0e+0", token.value);
let input = str_to_vec("123498790e+12349870");
let start_pos = 0;
let (token, next) = scan(&input, start_pos).unwrap();
assert_eq!(19, next);
assert_eq!(TokenType::Number, token.token_type);
assert_eq!("123498790e+12349870", token.value);
}
}