Simple scientific notation scanning
This commit is contained in:
parent
2c17557aad
commit
53fd824e8d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
.vscode
|
.vscode
|
||||||
|
*.profraw
|
@ -131,8 +131,6 @@ mod tests {
|
|||||||
assert_eq!(TokenType::Number, t3.token_type);
|
assert_eq!(TokenType::Number, t3.token_type);
|
||||||
assert_eq!("0.282398", t3.value);
|
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!("1798e+1", tokens.get(3).unwrap().value);
|
||||||
assert_eq!("239.3298e-103", tokens.get(4).unwrap().value);
|
assert_eq!("239.3298e-103", tokens.get(4).unwrap().value);
|
||||||
assert_eq!(TokenType::EOF, tokens.get(5).unwrap().token_type);
|
assert_eq!(TokenType::EOF, tokens.get(5).unwrap().token_type);
|
||||||
|
@ -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.
|
/// Recursively scans an integer. If a dot `.` is found, scans a double,
|
||||||
///
|
/// if a `e` is found, scans a number in scientific notation
|
||||||
/// It may fail due to scanning a double.
|
|
||||||
fn scan_decimal(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
|
fn scan_decimal(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
|
||||||
let next_char = chars.get(start_pos);
|
let next_char = chars.get(start_pos);
|
||||||
|
|
||||||
|
|
||||||
// If a char is found
|
// If a char is found
|
||||||
if let Some(c) = next_char {
|
if let Some(c) = next_char {
|
||||||
let c = *c;
|
let c = *c;
|
||||||
@ -39,12 +39,14 @@ fn scan_decimal(chars: &Vec<char>, start_pos: usize, current: String) -> Result<
|
|||||||
if c == '.' {
|
if c == '.' {
|
||||||
return scan_double(chars, start_pos + 1, utils::str_append(current, 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
|
// Scan a decimal number
|
||||||
else if utils::is_digit(c) {
|
else if utils::is_digit(c) {
|
||||||
let new_value = format!("{}{}", current, c);
|
return scan_decimal(chars, start_pos + 1, utils::str_append(current, c))
|
||||||
return scan_decimal(chars, start_pos + 1, new_value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the current value
|
// 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
|
/// Scans a floating point number
|
||||||
///
|
///
|
||||||
/// This function expects the following on the first call:
|
/// This function expects the following:
|
||||||
/// - The char at `start_pos` is a value between [0-9]. If not will return an error.
|
|
||||||
/// - `start_pos` is the position after the dot. E.g., if the input is `3.22` then `start_pos == 2`.
|
/// - `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> {
|
fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> Result<(Token, usize), String> {
|
||||||
let next_char = chars.get(start_pos);
|
let next_char = chars.get(start_pos);
|
||||||
|
|
||||||
// Check that the first characters exists and is a number
|
// Check that the first characters exists and is a number
|
||||||
if let Some(c) = next_char {
|
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 {Err(String::from("The character after the dot when scanning a double is not a number."))}
|
||||||
}
|
}
|
||||||
else {
|
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);
|
let next_char = chars.get(start_pos);
|
||||||
|
|
||||||
if let Some(c) = next_char {
|
if let Some(c) = next_char {
|
||||||
let c = *c;
|
let c = *c;
|
||||||
|
|
||||||
if utils::is_digit(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)
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user