diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index ff485154..95806725 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -302,7 +302,7 @@ mod float_functions { #[cfg(feature = "decimal")] #[export_module] mod decimal_functions { - use rust_decimal::Decimal; + use rust_decimal::{prelude::FromStr, Decimal}; #[rhai_fn(name = "floor", get = "floor")] pub fn floor(x: Decimal) -> Decimal { @@ -433,8 +433,8 @@ mod decimal_functions { } #[rhai_fn(return_raw)] pub fn parse_decimal(s: &str) -> Result> { - s.trim() - .parse::() + Decimal::from_str(s) + .or_else(|_| Decimal::from_scientific(s)) .map(Into::::into) .map_err(|err| { EvalAltResult::ErrorArithmetic( diff --git a/src/token.rs b/src/token.rs index 3aaa7f72..0717c632 100644 --- a/src/token.rs +++ b/src/token.rs @@ -22,6 +22,10 @@ use rust_decimal::Decimal; type LERR = LexError; +/// Separator character for numbers. +const NUM_SEP: char = '_'; + +/// A stream of tokens. pub type TokenStream<'a, 't> = Peekable>; /// A location (line number + character position) in the input script. @@ -1079,7 +1083,7 @@ fn get_next_token_inner( while let Some(next_char) = stream.peek_next() { match next_char { - ch if valid(ch) || ch == '_' => { + ch if valid(ch) || ch == NUM_SEP => { result.push(next_char); eat_next(stream, pos); } @@ -1174,7 +1178,7 @@ fn get_next_token_inner( // Parse number if let Some(radix) = radix_base { - let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect(); + let out: String = result.iter().skip(2).filter(|&&c| c != NUM_SEP).collect(); return Some(( INT::from_str_radix(&out, radix) @@ -1185,7 +1189,7 @@ fn get_next_token_inner( start_pos, )); } else { - let out: String = result.iter().filter(|&&c| c != '_').collect(); + let out: String = result.iter().filter(|&&c| c != NUM_SEP).collect(); let num = INT::from_str(&out).map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead @@ -1197,6 +1201,11 @@ fn get_next_token_inner( #[cfg(feature = "decimal")] let num = num.or_else(|_| Decimal::from_str(&out).map(Token::DecimalConstant)); + // Then try decimal in scientific notation + #[cfg(feature = "decimal")] + let num = + num.or_else(|_| Decimal::from_scientific(&out).map(Token::DecimalConstant)); + return Some(( num.unwrap_or_else(|_| { Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))