commit
013f8553a1
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -24,6 +24,8 @@ jobs:
|
||||
- "--features no_optimize"
|
||||
- "--features no_float"
|
||||
- "--features f32_float"
|
||||
- "--features decimal"
|
||||
- "--features no_float,decimal"
|
||||
- "--tests --features only_i32"
|
||||
- "--features only_i64"
|
||||
- "--features no_index"
|
||||
|
@ -37,6 +37,7 @@ no_float = [] # no floating-point
|
||||
f32_float = [] # set FLOAT=f32
|
||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||
decimal = [ "rust_decimal" ] # add the Decimal number type
|
||||
no_index = [] # no arrays and indexing
|
||||
no_object = [] # no custom objects
|
||||
no_function = [ "no_closure" ] # no script-defined functions (meaning no closures)
|
||||
@ -44,7 +45,7 @@ no_closure = [] # no automatic sharing and capture of anonymous functions to
|
||||
no_module = [] # no modules
|
||||
internals = [] # expose internal data structures
|
||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||
metadata = [ "serde", "serde_json"] # enables exporting functions metadata to JSON
|
||||
metadata = [ "serde", "serde_json" ] # enables exporting functions metadata to JSON
|
||||
|
||||
# compiling for no-std
|
||||
no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash/compile-time-rng" ]
|
||||
@ -98,6 +99,11 @@ version = "0.2"
|
||||
default_features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.rust_decimal]
|
||||
version = "1.10"
|
||||
default_features = false
|
||||
optional = true
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
instant= { version = "0.1" } # WASM implementation of std::time::Instant
|
||||
|
||||
|
@ -19,11 +19,13 @@ Breaking changes
|
||||
* trigonometry functions now take radians and return radians instead of degrees
|
||||
* `Dynamic::into_shared` is no longer available under `no_closure`. It used to panic.
|
||||
* `Token::is_operator` is renamed to `Token::is_symbol`.
|
||||
* `AST::clone_functions_only_filtered`, `AST::merge_filtered`, `AST::combine_filtered` and `AST::retain_functions` now take `Fn` instead of `FnMut` as the filter predicate.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
* Scientific notation is supported for floating-point number literals.
|
||||
* A new feature, `decimal`, enables the [`Decimal`](https://crates.io/crates/rust_decimal) data type. When both `no_float` and `decimal` features are enabled, floating-point literals parse to `Decimal`.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
37
src/ast.rs
37
src/ast.rs
@ -21,7 +21,7 @@ use crate::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
use crate::{stdlib::str::FromStr, FLOAT};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::Array;
|
||||
@ -345,10 +345,10 @@ impl AST {
|
||||
#[inline(always)]
|
||||
pub fn clone_functions_only_filtered(
|
||||
&self,
|
||||
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
) -> Self {
|
||||
let mut functions: Module = Default::default();
|
||||
functions.merge_filtered(&self.functions, &mut filter);
|
||||
functions.merge_filtered(&self.functions, &filter);
|
||||
Self {
|
||||
source: self.source.clone(),
|
||||
statements: Default::default(),
|
||||
@ -530,7 +530,7 @@ impl AST {
|
||||
pub fn merge_filtered(
|
||||
&self,
|
||||
other: &Self,
|
||||
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
) -> Self {
|
||||
let Self {
|
||||
statements,
|
||||
@ -552,7 +552,7 @@ impl AST {
|
||||
let source = other.source.clone().or_else(|| self.source.clone());
|
||||
|
||||
let mut functions = functions.as_ref().clone();
|
||||
functions.merge_filtered(&other.functions, &mut filter);
|
||||
functions.merge_filtered(&other.functions, &filter);
|
||||
|
||||
if let Some(source) = source {
|
||||
Self::new_with_source(ast, functions, source)
|
||||
@ -615,11 +615,11 @@ impl AST {
|
||||
pub fn combine_filtered(
|
||||
&mut self,
|
||||
other: Self,
|
||||
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
) -> &mut Self {
|
||||
self.statements.extend(other.statements.into_iter());
|
||||
if !other.functions.is_empty() {
|
||||
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &mut filter);
|
||||
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter);
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -652,7 +652,7 @@ impl AST {
|
||||
#[inline(always)]
|
||||
pub fn retain_functions(
|
||||
&mut self,
|
||||
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||
) -> &mut Self {
|
||||
if !self.functions.is_empty() {
|
||||
shared_make_mut(&mut self.functions).retain_script_functions(filter);
|
||||
@ -1126,7 +1126,7 @@ pub struct FnCallExpr {
|
||||
|
||||
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct FloatWrapper(FLOAT);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -1169,15 +1169,23 @@ impl crate::stdlib::ops::DerefMut for FloatWrapper {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl fmt::Debug for FloatWrapper {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl fmt::Display for FloatWrapper {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(feature = "no_std")]
|
||||
use num_traits::Float;
|
||||
|
||||
let abs = self.0.abs();
|
||||
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
||||
write!(f, "{:e}", self.0)
|
||||
} else {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -1187,6 +1195,15 @@ impl From<FLOAT> for FloatWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl FromStr for FloatWrapper {
|
||||
type Err = <FLOAT as FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
FLOAT::from_str(s).map(Into::<Self>::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl FloatWrapper {
|
||||
pub const fn new(value: FLOAT) -> Self {
|
||||
|
@ -15,6 +15,9 @@ use crate::{FnPtr, ImmutableString, INT};
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::{ast::FloatWrapper, FLOAT};
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::Array;
|
||||
|
||||
@ -25,6 +28,7 @@ use crate::Map;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::stdlib::time::Instant;
|
||||
|
||||
use fmt::Debug;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use instant::Instant;
|
||||
@ -155,6 +159,8 @@ pub enum Union {
|
||||
Int(INT, AccessMode),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Float(FloatWrapper, AccessMode),
|
||||
#[cfg(feature = "decimal")]
|
||||
Decimal(Box<Decimal>, AccessMode),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Array(Box<Array>, AccessMode),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -305,6 +311,8 @@ impl Dynamic {
|
||||
Union::Int(_, _) => TypeId::of::<INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_, _) => TypeId::of::<FLOAT>(),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, _) => TypeId::of::<Decimal>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _) => TypeId::of::<Array>(),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -338,6 +346,8 @@ impl Dynamic {
|
||||
Union::Int(_, _) => type_name::<INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_, _) => type_name::<FLOAT>(),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, _) => "decimal",
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _) => "array",
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -408,6 +418,10 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||
} else if name == type_name::<FnPtr>() {
|
||||
"Fn"
|
||||
} else {
|
||||
#[cfg(feature = "decimal")]
|
||||
if name == type_name::<Decimal>() {
|
||||
return "decimal";
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if name == type_name::<Array>() {
|
||||
return "array";
|
||||
@ -435,6 +449,8 @@ impl fmt::Display for Dynamic {
|
||||
Union::Int(value, _) => fmt::Display::fmt(value, f),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value, _) => fmt::Display::fmt(value, f),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(value, _) => fmt::Display::fmt(value, f),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -474,6 +490,8 @@ impl fmt::Debug for Dynamic {
|
||||
Union::Int(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -518,6 +536,10 @@ impl Clone for Dynamic {
|
||||
Union::Int(value, _) => Self(Union::Int(value, AccessMode::ReadWrite)),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value, _) => Self(Union::Float(value, AccessMode::ReadWrite)),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(ref value, _) => {
|
||||
Self(Union::Decimal(value.clone(), AccessMode::ReadWrite))
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref value, _) => Self(Union::Array(value.clone(), AccessMode::ReadWrite)),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -582,6 +604,8 @@ impl Dynamic {
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_, access) => access,
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, access) => access,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, access) => access,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -605,6 +629,8 @@ impl Dynamic {
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_, access) => *access = typ,
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, access) => *access = typ,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, access) => *access = typ,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -687,6 +713,13 @@ impl Dynamic {
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||
return <dyn Any>::downcast_ref::<Decimal>(&value)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return <dyn Any>::downcast_ref::<bool>(&value)
|
||||
.unwrap()
|
||||
@ -845,6 +878,14 @@ impl Dynamic {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||
return match self.0 {
|
||||
Union::Decimal(value, _) => unsafe_try_cast(*value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match self.0 {
|
||||
Union::Bool(value, _) => unsafe_try_cast(value),
|
||||
@ -1113,6 +1154,13 @@ impl Dynamic {
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||
return match &self.0 {
|
||||
Union::Decimal(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match &self.0 {
|
||||
Union::Bool(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
@ -1202,6 +1250,13 @@ impl Dynamic {
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||
return match &mut self.0 {
|
||||
Union::Decimal(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match &mut self.0 {
|
||||
Union::Bool(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
@ -1277,6 +1332,8 @@ impl Dynamic {
|
||||
}
|
||||
/// Cast the [`Dynamic`] as the system floating-point type [`FLOAT`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||
@ -1287,6 +1344,20 @@ impl Dynamic {
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
/// Cast the [`Dynamic`] as a [`Decimal`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// Available only under `decimal`.
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline(always)]
|
||||
pub fn as_decimal(self) -> Result<Decimal, &'static str> {
|
||||
match self.0 {
|
||||
Union::Decimal(n, _) => Ok(*n),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
/// Cast the [`Dynamic`] as a [`bool`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline(always)]
|
||||
@ -1312,7 +1383,7 @@ impl Dynamic {
|
||||
/// Cast the [`Dynamic`] as a [`String`] and return the string slice.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// Cast is failing if `self` is Shared Dynamic
|
||||
/// Fails if `self` is _shared_.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> Result<&str, &'static str> {
|
||||
match &self.0 {
|
||||
@ -1386,6 +1457,16 @@ impl From<FloatWrapper> for Dynamic {
|
||||
Self(Union::Float(value, AccessMode::ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
impl From<Decimal> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Decimal) -> Self {
|
||||
Self(Union::Decimal(
|
||||
Box::new(value.into()),
|
||||
AccessMode::ReadWrite,
|
||||
))
|
||||
}
|
||||
}
|
||||
impl From<char> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: char) -> Self {
|
||||
|
@ -699,11 +699,11 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
getter: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
setter: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_indexer_get(getter)
|
||||
.register_indexer_set(setter)
|
||||
self.register_indexer_get(get_fn)
|
||||
.register_indexer_set(set_fn)
|
||||
}
|
||||
/// Register a shared [`Module`] into the global namespace of [`Engine`].
|
||||
///
|
||||
|
@ -1334,12 +1334,12 @@ impl Module {
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
getter: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
setter: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> (NonZeroU64, NonZeroU64) {
|
||||
(
|
||||
self.set_indexer_get_fn(getter),
|
||||
self.set_indexer_set_fn(setter),
|
||||
self.set_indexer_get_fn(get_fn),
|
||||
self.set_indexer_set_fn(set_fn),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1570,7 +1570,7 @@ impl Module {
|
||||
pub(crate) fn merge_filtered(
|
||||
&mut self,
|
||||
other: &Self,
|
||||
mut _filter: &mut impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
_filter: &impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
other.modules.iter().for_each(|(k, v)| {
|
||||
@ -1625,7 +1625,7 @@ impl Module {
|
||||
#[inline]
|
||||
pub(crate) fn retain_script_functions(
|
||||
&mut self,
|
||||
mut filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||
) -> &mut Self {
|
||||
self.functions.retain(
|
||||
|_,
|
||||
|
@ -197,6 +197,10 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
combine_with_exported_module!(lib, "f32", f32_functions);
|
||||
combine_with_exported_module!(lib, "f64", f64_functions);
|
||||
}
|
||||
|
||||
// Decimal functions
|
||||
#[cfg(feature = "decimal")]
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
});
|
||||
|
||||
gen_arithmetic_functions!(arith_basic => INT);
|
||||
@ -257,7 +261,7 @@ mod f32_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: f32) -> f32 {
|
||||
-x
|
||||
x
|
||||
}
|
||||
pub fn abs(x: f32) -> f32 {
|
||||
x.abs()
|
||||
@ -320,7 +324,7 @@ mod f64_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: f64) -> f64 {
|
||||
-x
|
||||
x
|
||||
}
|
||||
pub fn abs(x: f64) -> f64 {
|
||||
x.abs()
|
||||
@ -346,3 +350,130 @@ mod f64_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
mod decimal_functions {
|
||||
use rust_decimal::{prelude::Zero, Decimal};
|
||||
|
||||
#[rhai_fn(name = "+", return_raw)]
|
||||
pub fn add_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_add(y)
|
||||
.ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y)))
|
||||
.map(Dynamic::from)
|
||||
} else {
|
||||
Ok(Dynamic::from(x + y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "+", return_raw)]
|
||||
pub fn add_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
add_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "+", return_raw)]
|
||||
pub fn add_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
add_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "-", return_raw)]
|
||||
pub fn subtract_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_sub(y)
|
||||
.ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y)))
|
||||
.map(Dynamic::from)
|
||||
} else {
|
||||
Ok(Dynamic::from(x - y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "-", return_raw)]
|
||||
pub fn subtract_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
subtract_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "-", return_raw)]
|
||||
pub fn subtract_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
subtract_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "*", return_raw)]
|
||||
pub fn multiply_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_mul(y)
|
||||
.ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y)))
|
||||
.map(Dynamic::from)
|
||||
} else {
|
||||
Ok(Dynamic::from(x * y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "*", return_raw)]
|
||||
pub fn multiply_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
multiply_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "*", return_raw)]
|
||||
pub fn multiply_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
multiply_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "/", return_raw)]
|
||||
pub fn divide_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
// Detect division by zero
|
||||
if y == Decimal::zero() {
|
||||
Err(make_err(format!("Division by zero: {} / {}", x, y)))
|
||||
} else {
|
||||
x.checked_div(y)
|
||||
.ok_or_else(|| make_err(format!("Division overflow: {} / {}", x, y)))
|
||||
.map(Dynamic::from)
|
||||
}
|
||||
} else {
|
||||
Ok(Dynamic::from(x / y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "/", return_raw)]
|
||||
pub fn divide_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
divide_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "/", return_raw)]
|
||||
pub fn divide_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
divide_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "%", return_raw)]
|
||||
pub fn modulo_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_rem(y)
|
||||
.ok_or_else(|| {
|
||||
make_err(format!(
|
||||
"Modulo division by zero or overflow: {} % {}",
|
||||
x, y
|
||||
))
|
||||
})
|
||||
.map(Dynamic::from)
|
||||
} else {
|
||||
Ok(Dynamic::from(x % y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "%", return_raw)]
|
||||
pub fn modulo_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
modulo_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "%", return_raw)]
|
||||
pub fn modulo_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
modulo_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "-")]
|
||||
pub fn neg(x: Decimal) -> Decimal {
|
||||
-x
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: Decimal) -> Decimal {
|
||||
x
|
||||
}
|
||||
pub fn abs(x: Decimal) -> Decimal {
|
||||
x.abs()
|
||||
}
|
||||
pub fn sign(x: Decimal) -> INT {
|
||||
if x == Decimal::zero() {
|
||||
0
|
||||
} else if x.is_sign_negative() {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ use num_traits::float::Float;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::stdlib::format;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(feature = "only_i32")]
|
||||
pub const MAX_INT: INT = i32::MAX;
|
||||
@ -23,7 +26,7 @@ pub const MAX_INT: INT = i32::MAX;
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
pub const MAX_INT: INT = i64::MAX;
|
||||
|
||||
macro_rules! gen_conversion_functions {
|
||||
macro_rules! gen_conversion_as_functions {
|
||||
($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
|
||||
pub mod $root { $(pub mod $arg_type {
|
||||
use super::super::*;
|
||||
@ -36,6 +39,20 @@ macro_rules! gen_conversion_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
macro_rules! gen_conversion_into_functions {
|
||||
($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
|
||||
pub mod $root { $(pub mod $arg_type {
|
||||
use super::super::*;
|
||||
|
||||
#[export_fn]
|
||||
pub fn $func_name(x: $arg_type) -> $result_type {
|
||||
x.into()
|
||||
}
|
||||
})* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_functions {
|
||||
($mod_name:ident += $root:ident :: $func_name:ident ( $($arg_type:ident),+ ) ) => { $(
|
||||
set_exported_fn!($mod_name, stringify!($func_name), $root::$arg_type::$func_name);
|
||||
@ -76,6 +93,18 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
||||
reg_functions!(lib += num_128_to_float::to_float(i128, u128));
|
||||
}
|
||||
}
|
||||
|
||||
// Decimal functions
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
|
||||
reg_functions!(lib += basic_to_decimal::to_decimal(INT));
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64));
|
||||
}
|
||||
});
|
||||
|
||||
#[export_module]
|
||||
@ -267,27 +296,75 @@ mod float_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
mod decimal_functions {
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[rhai_fn(name = "floor", get = "floor")]
|
||||
pub fn floor(x: Decimal) -> Decimal {
|
||||
x.floor()
|
||||
}
|
||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||
pub fn ceiling(x: Decimal) -> Decimal {
|
||||
x.ceil()
|
||||
}
|
||||
#[rhai_fn(name = "round", get = "round")]
|
||||
pub fn round(x: Decimal) -> Decimal {
|
||||
x.ceil()
|
||||
}
|
||||
#[rhai_fn(name = "int", get = "int")]
|
||||
pub fn int(x: Decimal) -> Decimal {
|
||||
x.trunc()
|
||||
}
|
||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||
pub fn fraction(x: Decimal) -> Decimal {
|
||||
x.fract()
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_decimal(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
s.trim()
|
||||
.parse::<Decimal>()
|
||||
.map(Into::<Dynamic>::into)
|
||||
.map_err(|err| {
|
||||
EvalAltResult::ErrorArithmetic(
|
||||
format!("Error parsing decimal number '{}': {}", s, err),
|
||||
Position::NONE,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
gen_conversion_functions!(basic_to_float => to_float (INT) -> FLOAT);
|
||||
gen_conversion_as_functions!(basic_to_float => to_float (INT) -> FLOAT);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32, u32, i64, u64) -> FLOAT);
|
||||
gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32, u32, i64, u64) -> FLOAT);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
gen_conversion_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
||||
gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
||||
|
||||
gen_conversion_functions!(basic_to_int => to_int (char) -> INT);
|
||||
gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, i64, u64) -> INT);
|
||||
gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, i64, u64) -> INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
gen_conversion_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||
gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_conversion_into_functions!(basic_to_decimal => to_decimal (INT) -> Decimal);
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_into_functions!(numbers_to_decimal => to_decimal (i8, u8, i16, u16, i32, u32, i64, u64) -> Decimal);
|
||||
|
@ -15,6 +15,9 @@ use crate::Array;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::Map;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
const FUNC_TO_STRING: &'static str = "to_string";
|
||||
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||
|
||||
@ -68,8 +71,16 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_print_functions!(lib += print_float; f32, f64);
|
||||
reg_debug_functions!(lib += debug_float; f32, f64);
|
||||
reg_print_functions!(lib += print_float_64; f64);
|
||||
reg_debug_functions!(lib += print_float_64; f64);
|
||||
reg_print_functions!(lib += print_float_32; f32);
|
||||
reg_debug_functions!(lib += print_float_32; f32);
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
reg_print_functions!(lib += print_decimal; Decimal);
|
||||
reg_debug_functions!(lib += debug_decimal; Decimal);
|
||||
}
|
||||
});
|
||||
|
||||
@ -79,6 +90,30 @@ fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
||||
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
||||
format!("{:?}", x).into()
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn print_f64(x: &mut f64) -> ImmutableString {
|
||||
#[cfg(feature = "no_std")]
|
||||
use num_traits::Float;
|
||||
|
||||
let abs = x.abs();
|
||||
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
||||
format!("{:e}", x).into()
|
||||
} else {
|
||||
x.to_string().into()
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn print_f32(x: &mut f32) -> ImmutableString {
|
||||
#[cfg(feature = "no_std")]
|
||||
use num_traits::Float;
|
||||
|
||||
let abs = x.abs();
|
||||
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
||||
format!("{:e}", x).into()
|
||||
} else {
|
||||
x.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
gen_functions!(print_basic => to_string(INT, bool, char, FnPtr));
|
||||
gen_functions!(debug_basic => to_debug(INT, bool, Unit, char, ImmutableString));
|
||||
@ -102,10 +137,16 @@ gen_functions!(print_num_128 => to_string(i128, u128));
|
||||
gen_functions!(debug_num_128 => to_debug(i128, u128));
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
gen_functions!(print_float => to_string(f32, f64));
|
||||
gen_functions!(print_float_64 => print_f64(f64));
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
gen_functions!(debug_float => to_debug(f32, f64));
|
||||
gen_functions!(print_float_32 => print_f32(f32));
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_functions!(print_decimal => to_string(Decimal));
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_functions!(debug_decimal => to_debug(Decimal));
|
||||
|
||||
// Register print and debug
|
||||
|
||||
|
@ -958,6 +958,12 @@ fn parse_primary(
|
||||
input.next().unwrap();
|
||||
Expr::FloatConstant(x, settings.pos)
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
Token::DecimalConstant(x) => {
|
||||
let x = (*x).into();
|
||||
input.next().unwrap();
|
||||
Expr::DynamicConstant(Box::new(x), settings.pos)
|
||||
}
|
||||
|
||||
// { - block statement as expression
|
||||
Token::LeftBrace if settings.allow_stmt_expr => {
|
||||
|
@ -128,12 +128,26 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
Union::Bool(_, _) => self.deserialize_bool(visitor),
|
||||
Union::Str(_, _) => self.deserialize_str(visitor),
|
||||
Union::Char(_, _) => self.deserialize_char(visitor),
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
Union::Int(_, _) => self.deserialize_i64(visitor),
|
||||
#[cfg(feature = "only_i32")]
|
||||
Union::Int(_, _) => self.deserialize_i32(visitor),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
Union::Float(_, _) => self.deserialize_f64(visitor),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(feature = "f32_float")]
|
||||
Union::Float(_, _) => self.deserialize_f32(visitor),
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
Union::Decimal(_, _) => self.deserialize_f64(visitor),
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(feature = "f32_float")]
|
||||
Union::Decimal(_, _) => self.deserialize_f32(visitor),
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _) => self.deserialize_seq(visitor),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -278,6 +292,19 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.value
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f32())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(not(feature = "decimal"))]
|
||||
return self.type_error_str("f32");
|
||||
}
|
||||
|
||||
@ -289,6 +316,19 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.value
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f64())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(not(feature = "decimal"))]
|
||||
return self.type_error_str("f64");
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,27 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
return self.visit_f32(v as f32);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||
use crate::stdlib::convert::TryFrom;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||
use crate::stdlib::convert::TryFrom;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
|
||||
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
|
||||
self.visit_string(v.to_string())
|
||||
}
|
||||
|
@ -214,11 +214,35 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Box<EvalAltResult>> {
|
||||
Ok(Dynamic::from(v))
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use crate::stdlib::convert::TryFrom;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Box<EvalAltResult>> {
|
||||
Ok(Dynamic::from(v))
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use crate::stdlib::convert::TryFrom;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Self::Ok, Box<EvalAltResult>> {
|
||||
|
@ -12,16 +12,42 @@ impl Serialize for Dynamic {
|
||||
Union::Bool(x, _) => ser.serialize_bool(*x),
|
||||
Union::Str(s, _) => ser.serialize_str(s.as_str()),
|
||||
Union::Char(c, _) => ser.serialize_str(&c.to_string()),
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
Union::Int(x, _) => ser.serialize_i64(*x),
|
||||
#[cfg(feature = "only_i32")]
|
||||
Union::Int(x, _) => ser.serialize_i32(*x),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
Union::Float(x, _) => ser.serialize_f64(**x),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(feature = "f32_float")]
|
||||
Union::Float(x, _) => ser.serialize_f32(*x),
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
Union::Decimal(x, _) => {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
if let Some(v) = x.to_f64() {
|
||||
ser.serialize_f64(v)
|
||||
} else {
|
||||
ser.serialize_str(&x.to_string())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(feature = "f32_float")]
|
||||
Union::Decimal(x, _) => {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
if let Some(v) = x.to_f32() {
|
||||
ser.serialize_f32(v)
|
||||
} else {
|
||||
ser.serialize_str(&x.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(a, _) => (**a).serialize(ser),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
|
25
src/token.rs
25
src/token.rs
@ -15,7 +15,10 @@ use crate::stdlib::{
|
||||
use crate::{Engine, LexError, StaticVec, INT};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
use crate::ast::FloatWrapper;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
type LERR = LexError;
|
||||
|
||||
@ -153,7 +156,7 @@ impl fmt::Debug for Position {
|
||||
/// # Volatile Data Structure
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub enum Token {
|
||||
/// An `INT` constant.
|
||||
IntegerConstant(INT),
|
||||
@ -161,7 +164,12 @@ pub enum Token {
|
||||
///
|
||||
/// Reserved under the `no_float` feature.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(FLOAT),
|
||||
FloatConstant(FloatWrapper),
|
||||
/// A [`Decimal`] constant.
|
||||
///
|
||||
/// Requires the `decimal` feature.
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(Decimal),
|
||||
/// An identifier.
|
||||
Identifier(String),
|
||||
/// A character constant.
|
||||
@ -348,6 +356,8 @@ impl Token {
|
||||
IntegerConstant(i) => i.to_string().into(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(f) => f.to_string().into(),
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(d) => d.to_string().into(),
|
||||
StringConstant(_) => "string".into(),
|
||||
CharConstant(c) => c.to_string().into(),
|
||||
Identifier(s) => s.clone().into(),
|
||||
@ -1073,7 +1083,7 @@ fn get_next_token_inner(
|
||||
result.push(next_char);
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
||||
'.' => {
|
||||
stream.get_next().unwrap();
|
||||
|
||||
@ -1180,7 +1190,12 @@ fn get_next_token_inner(
|
||||
|
||||
// If integer parsing is unnecessary, try float instead
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
let num = num.or_else(|_| FLOAT::from_str(&out).map(Token::FloatConstant));
|
||||
let num =
|
||||
num.or_else(|_| FloatWrapper::from_str(&out).map(Token::FloatConstant));
|
||||
|
||||
// Then try decimal
|
||||
#[cfg(feature = "decimal")]
|
||||
let num = num.or_else(|_| Decimal::from_str(&out).map(Token::DecimalConstant));
|
||||
|
||||
return Some((
|
||||
num.unwrap_or_else(|_| {
|
||||
|
@ -5,11 +5,14 @@ use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, ImmutableString, INT,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use rhai::Array;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use rhai::Map;
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[test]
|
||||
fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -25,6 +28,13 @@ fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
|
||||
assert!(to_dynamic(123.456_f32)?.is::<f32>());
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
assert!(to_dynamic(123.456_f64)?.is::<Decimal>());
|
||||
assert!(to_dynamic(123.456_f32)?.is::<Decimal>());
|
||||
}
|
||||
|
||||
assert!(to_dynamic("hello".to_string())?.is::<String>());
|
||||
|
||||
Ok(())
|
||||
@ -301,6 +311,15 @@ fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(123.456, from_dynamic::<f32>(&Dynamic::from(123.456_f32))?);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
let d: Dynamic = Decimal::from_str("123.456").unwrap().into();
|
||||
|
||||
assert_eq!(123.456, from_dynamic::<f64>(&d)?);
|
||||
assert_eq!(123.456, from_dynamic::<f32>(&d)?);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
"hello",
|
||||
from_dynamic::<String>(&"hello".to_string().into())?
|
||||
|
Loading…
Reference in New Issue
Block a user