Add scientific notation to floats.
This commit is contained in:
parent
d49581356d
commit
8ffb6c03b5
@ -20,6 +20,11 @@ Breaking changes
|
||||
* `Dynamic::into_shared` is no longer available under `no_closure`. It used to panic.
|
||||
* `Token::is_operator` is renamed to `Token::is_symbol`.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
* Scientific notation is supported for floating-point number literals.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
|
72
src/token.rs
72
src/token.rs
@ -1000,7 +1000,7 @@ pub fn get_next_token(
|
||||
|
||||
/// Test if the given character is a hex character.
|
||||
#[inline(always)]
|
||||
fn is_hex_char(c: char) -> bool {
|
||||
fn is_hex_digit(c: char) -> bool {
|
||||
match c {
|
||||
'a'..='f' => true,
|
||||
'A'..='F' => true,
|
||||
@ -1009,20 +1009,11 @@ fn is_hex_char(c: char) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if the given character is an octal character.
|
||||
/// Test if the given character is a numeric digit.
|
||||
#[inline(always)]
|
||||
fn is_octal_char(c: char) -> bool {
|
||||
fn is_numeric_digit(c: char) -> bool {
|
||||
match c {
|
||||
'0'..='7' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if the given character is a binary character.
|
||||
#[inline(always)]
|
||||
fn is_binary_char(c: char) -> bool {
|
||||
match c {
|
||||
'0' | '1' => true,
|
||||
'0'..='9' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1073,11 +1064,12 @@ fn get_next_token_inner(
|
||||
('0'..='9', _) => {
|
||||
let mut result: StaticVec<char> = Default::default();
|
||||
let mut radix_base: Option<u32> = None;
|
||||
let mut valid: fn(char) -> bool = is_numeric_digit;
|
||||
result.push(c);
|
||||
|
||||
while let Some(next_char) = stream.peek_next() {
|
||||
match next_char {
|
||||
'0'..='9' | '_' => {
|
||||
ch if valid(ch) || ch == '_' => {
|
||||
result.push(next_char);
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
@ -1090,7 +1082,7 @@ fn get_next_token_inner(
|
||||
// digits after period - accept the period
|
||||
'0'..='9' => {
|
||||
result.push(next_char);
|
||||
pos.advance()
|
||||
pos.advance();
|
||||
}
|
||||
// _ - cannot follow a decimal point
|
||||
'_' => {
|
||||
@ -1114,28 +1106,43 @@ fn get_next_token_inner(
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
'e' => {
|
||||
stream.get_next().unwrap();
|
||||
|
||||
while let Some(next_char_in_float) = stream.peek_next() {
|
||||
match next_char_in_float {
|
||||
'0'..='9' | '_' => {
|
||||
result.push(next_char_in_float);
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
_ => break,
|
||||
// Check if followed by digits or +/-
|
||||
match stream.peek_next().unwrap_or('\0') {
|
||||
// digits after e - accept the e
|
||||
'0'..='9' => {
|
||||
result.push(next_char);
|
||||
pos.advance();
|
||||
}
|
||||
// +/- after e - accept the e and the sign
|
||||
'+' | '-' => {
|
||||
result.push(next_char);
|
||||
pos.advance();
|
||||
result.push(stream.get_next().unwrap());
|
||||
pos.advance();
|
||||
}
|
||||
// Not a floating-point number
|
||||
_ => {
|
||||
stream.unget(next_char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 0x????, 0o????, 0b????
|
||||
// 0x????, 0o????, 0b???? at beginning
|
||||
ch @ 'x' | ch @ 'o' | ch @ 'b' | ch @ 'X' | ch @ 'O' | ch @ 'B'
|
||||
if c == '0' =>
|
||||
if c == '0' && result.len() <= 1 =>
|
||||
{
|
||||
result.push(next_char);
|
||||
eat_next(stream, pos);
|
||||
|
||||
let valid = match ch {
|
||||
'x' | 'X' => is_hex_char,
|
||||
'o' | 'O' => is_octal_char,
|
||||
'b' | 'B' => is_binary_char,
|
||||
valid = match ch {
|
||||
'x' | 'X' => is_hex_digit,
|
||||
'o' | 'O' => is_numeric_digit,
|
||||
'b' | 'B' => is_numeric_digit,
|
||||
_ => unreachable!("expecting 'x', 'o' or 'b', but gets {}", ch),
|
||||
};
|
||||
|
||||
@ -1145,15 +1152,6 @@ fn get_next_token_inner(
|
||||
'b' | 'B' => 2,
|
||||
_ => unreachable!("expecting 'x', 'o' or 'b', but gets {}", ch),
|
||||
});
|
||||
|
||||
while let Some(next_char_in_escape_seq) = stream.peek_next() {
|
||||
if !valid(next_char_in_escape_seq) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.push(next_char_in_escape_seq);
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
}
|
||||
|
||||
_ => break,
|
||||
|
@ -7,20 +7,25 @@ const EPSILON: FLOAT = 0.000_000_000_1;
|
||||
fn test_float() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y")?,
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<bool>("let x = 0.0; let y = 1.0; x > y")?,
|
||||
false
|
||||
);
|
||||
assert!(engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y")?,);
|
||||
assert!(!engine.eval::<bool>("let x = 0.0; let y = 1.0; x > y")?,);
|
||||
assert_eq!(engine.eval::<bool>("let x = 0.; let y = 1.; x > y")?, false);
|
||||
assert!((engine.eval::<FLOAT>("let x = 9.9999; x")? - 9.9999 as FLOAT).abs() < EPSILON);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_scientific() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert!(engine.eval::<bool>("123.456 == 1.23456e2")?);
|
||||
assert!(engine.eval::<bool>("123.456 == 1.23456e+2")?);
|
||||
assert!(engine.eval::<bool>("123.456 == 123456e-3")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_parse() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
Loading…
Reference in New Issue
Block a user