Support scientific notation for Decimal parsing.

This commit is contained in:
Stephen Chung 2021-02-14 17:34:53 +08:00
parent fc21756785
commit 40e4a92401
2 changed files with 15 additions and 6 deletions

View File

@ -302,7 +302,7 @@ mod float_functions {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[export_module] #[export_module]
mod decimal_functions { mod decimal_functions {
use rust_decimal::Decimal; use rust_decimal::{prelude::FromStr, Decimal};
#[rhai_fn(name = "floor", get = "floor")] #[rhai_fn(name = "floor", get = "floor")]
pub fn floor(x: Decimal) -> Decimal { pub fn floor(x: Decimal) -> Decimal {
@ -433,8 +433,8 @@ mod decimal_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn parse_decimal(s: &str) -> Result<Dynamic, Box<EvalAltResult>> { pub fn parse_decimal(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
s.trim() Decimal::from_str(s)
.parse::<Decimal>() .or_else(|_| Decimal::from_scientific(s))
.map(Into::<Dynamic>::into) .map(Into::<Dynamic>::into)
.map_err(|err| { .map_err(|err| {
EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(

View File

@ -22,6 +22,10 @@ use rust_decimal::Decimal;
type LERR = LexError; type LERR = LexError;
/// Separator character for numbers.
const NUM_SEP: char = '_';
/// A stream of tokens.
pub type TokenStream<'a, 't> = Peekable<TokenIterator<'a, 't>>; pub type TokenStream<'a, 't> = Peekable<TokenIterator<'a, 't>>;
/// A location (line number + character position) in the input script. /// 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() { while let Some(next_char) = stream.peek_next() {
match next_char { match next_char {
ch if valid(ch) || ch == '_' => { ch if valid(ch) || ch == NUM_SEP => {
result.push(next_char); result.push(next_char);
eat_next(stream, pos); eat_next(stream, pos);
} }
@ -1174,7 +1178,7 @@ fn get_next_token_inner(
// Parse number // Parse number
if let Some(radix) = radix_base { 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(( return Some((
INT::from_str_radix(&out, radix) INT::from_str_radix(&out, radix)
@ -1185,7 +1189,7 @@ fn get_next_token_inner(
start_pos, start_pos,
)); ));
} else { } 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); let num = INT::from_str(&out).map(Token::IntegerConstant);
// If integer parsing is unnecessary, try float instead // If integer parsing is unnecessary, try float instead
@ -1197,6 +1201,11 @@ fn get_next_token_inner(
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
let num = num.or_else(|_| Decimal::from_str(&out).map(Token::DecimalConstant)); 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(( return Some((
num.unwrap_or_else(|_| { num.unwrap_or_else(|_| {
Token::LexError(LERR::MalformedNumber(result.into_iter().collect())) Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))