From 69ef72334b5670bf5ec265648b4f653d6441df86 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Thu, 27 Jun 2024 16:19:13 +1200 Subject: [PATCH 1/4] Add a few basic tests around numerics --- core/expression/tests/isolate.rs | 36 +++++++++++++++++++++++++++++++ core/expression/tests/standard.rs | 8 +++++++ 2 files changed, 44 insertions(+) diff --git a/core/expression/tests/isolate.rs b/core/expression/tests/isolate.rs index 093b742d..452440b8 100644 --- a/core/expression/tests/isolate.rs +++ b/core/expression/tests/isolate.rs @@ -61,6 +61,42 @@ fn isolate_standard_test() { }, ]), }, + TestEnv { + env: json!({ + "a": 3.14f64, + "b": 2, + "c": 3.141592653589793f64, + "d": 18446744073709551615u64, + "e": 9_223_372_036_854_775_807i64, + + }), + cases: Vec::from([ + TestCase { + expr: "a", + result: json!(3.14), + }, + TestCase { + expr: "b", + result: json!(2), + }, + TestCase { + expr: "e", + result: json!(9_223_372_036_854_775_807i64), + }, + TestCase { + expr: "a + b", + result: json!(5.14), + }, + TestCase { + expr: "(b + c) - (c + b)", + result: json!(0), + }, + TestCase { + expr: "d", + result: json!(18446744073709551615u64), + }, + ]), + }, TestEnv { env: json!({ "a": 3, diff --git a/core/expression/tests/standard.rs b/core/expression/tests/standard.rs index 350c962e..d8908658 100644 --- a/core/expression/tests/standard.rs +++ b/core/expression/tests/standard.rs @@ -281,6 +281,14 @@ fn standard_test() { right: &Node::Array(&[]), }, }, + StandardTest { + src: "25 + 2.5", + result: &Node::Binary { + left: &Node::Number(D25), + right: &Node::Number(D2P5), + operator: Operator::Arithmetic(ArithmeticOperator::Add), + }, + }, ]); let mut lexer = Lexer::new(); From 6152a94dcfa146d4511cd323da9477e93b6d3f81 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Thu, 27 Jun 2024 17:27:24 +1200 Subject: [PATCH 2/4] Put arbitrary_precision behind a feature flag --- core/expression/Cargo.toml | 5 +++-- core/expression/src/variable/conv.rs | 28 +++++++++++++++++++++++++--- core/expression/src/variable/mod.rs | 26 +++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/core/expression/Cargo.toml b/core/expression/Cargo.toml index c1430d28..b17d1845 100644 --- a/core/expression/Cargo.toml +++ b/core/expression/Cargo.toml @@ -17,11 +17,11 @@ once_cell = { workspace = true } regex = { workspace = true, optional = true } regex-lite = { workspace = true, optional = true } serde = { workspace = true } -serde_json = { workspace = true, features = ["arbitrary_precision"] } +serde_json = { workspace = true, features = [] } strum = { workspace = true } strum_macros = { workspace = true } thiserror = { workspace = true } -rust_decimal = { workspace = true, features = ["maths-nopanic"] } +rust_decimal = { workspace = true, features = ["maths-nopanic", "serde-with-str"] } rust_decimal_macros = { workspace = true } nohash-hasher = "0.2.0" strsim = "0.11.1" @@ -36,6 +36,7 @@ serde_json5 = "0.1.0" default = ["regex-deprecated"] regex-deprecated = ["dep:regex"] regex-lite = ["dep:regex-lite"] +arbitrary_precision = ["serde_json/arbitrary_precision"] [[bench]] harness = false diff --git a/core/expression/src/variable/conv.rs b/core/expression/src/variable/conv.rs index b6bf4184..f3a4084d 100644 --- a/core/expression/src/variable/conv.rs +++ b/core/expression/src/variable/conv.rs @@ -1,6 +1,7 @@ use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump; use rust_decimal::Decimal; +use rust_decimal::prelude::FromPrimitive; use serde_json::Value; use crate::variable::map::BumpMap; @@ -19,9 +20,30 @@ impl<'arena> ToVariable<'arena> for Value { match self { Value::Null => Ok(Variable::Null), Value::Bool(v) => Ok(Variable::Bool(*v)), - Value::Number(n) => Ok(Variable::Number( - Decimal::from_str_exact(n.as_str()).map_err(|_| ())?, - )), + Value::Number(n) => { + #[cfg(feature = "arbitrary_precision")] + { + Decimal::from_str_exact(n.as_str()).map_err(|_| ()) + .map(Variable::Number) + } + + #[cfg(not(feature = "arbitrary_precision"))] + { + + let decimal = match n.as_u64() { + Some(n) => Decimal::from_u64(n).ok_or(())?, + None => match n.as_i64() { + Some(n) => Decimal::from(n), + None => match n.as_f64() { + Some(n) => Decimal::from_f64(n).ok_or(())?, + None => return Err(()), + }, + }, + }; + + Ok(Variable::Number(decimal)) + } + }, Value::String(s) => Ok(Variable::String(arena.alloc_str(s.as_str()))), Value::Array(arr) => { let mut vec = BumpVec::with_capacity_in(arr.len(), arena); diff --git a/core/expression/src/variable/mod.rs b/core/expression/src/variable/mod.rs index 6757f874..96c58b18 100644 --- a/core/expression/src/variable/mod.rs +++ b/core/expression/src/variable/mod.rs @@ -80,7 +80,31 @@ impl<'arena> Variable<'arena> { Variable::Null => Value::Null, Variable::Bool(b) => Value::Bool(*b), Variable::Number(n) => { - Value::Number(Number::from_string_unchecked(n.normalize().to_string())) + #[cfg(feature = "arbitrary_precision")] + { + Value::Number(Number::from_string_unchecked(n.normalize().to_string())) + } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Some(n_uint) = n.to_u64() { + if Decimal::from(n_uint) == *n { + return Value::Number(Number::from(n_uint)); + } + } + + if let Some(n_int) = n.to_i64() { + if Decimal::from(n_int) == *n { + return Value::Number(Number::from(n_int)); + } + } + + if let Some(n_float) = n.to_f64() { + return Value::Number(Number::from_f64(n_float).unwrap()) + } + + Value::Null + } } Variable::String(str) => Value::String(str.to_string()), Variable::Array(arr) => Value::Array(arr.iter().map(|i| i.to_value()).collect()), From 157ad2216663b6ece05f8ee7405dcf4d04dd86a9 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Mon, 1 Jul 2024 09:52:17 +1200 Subject: [PATCH 3/4] Remove unnecessary feature from rust_decimal --- core/expression/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/expression/Cargo.toml b/core/expression/Cargo.toml index b17d1845..94c146c4 100644 --- a/core/expression/Cargo.toml +++ b/core/expression/Cargo.toml @@ -21,7 +21,7 @@ serde_json = { workspace = true, features = [] } strum = { workspace = true } strum_macros = { workspace = true } thiserror = { workspace = true } -rust_decimal = { workspace = true, features = ["maths-nopanic", "serde-with-str"] } +rust_decimal = { workspace = true, features = ["maths-nopanic"] } rust_decimal_macros = { workspace = true } nohash-hasher = "0.2.0" strsim = "0.11.1" From 361c0a1cf7532ba5bfc4d7dc710d598cea0506e3 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Tue, 2 Jul 2024 10:52:01 +1200 Subject: [PATCH 4/4] fmt --- core/expression/src/variable/conv.rs | 8 ++++---- core/expression/src/variable/mod.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/expression/src/variable/conv.rs b/core/expression/src/variable/conv.rs index f3a4084d..68ae70bb 100644 --- a/core/expression/src/variable/conv.rs +++ b/core/expression/src/variable/conv.rs @@ -1,7 +1,7 @@ use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump; -use rust_decimal::Decimal; use rust_decimal::prelude::FromPrimitive; +use rust_decimal::Decimal; use serde_json::Value; use crate::variable::map::BumpMap; @@ -23,13 +23,13 @@ impl<'arena> ToVariable<'arena> for Value { Value::Number(n) => { #[cfg(feature = "arbitrary_precision")] { - Decimal::from_str_exact(n.as_str()).map_err(|_| ()) + Decimal::from_str_exact(n.as_str()) + .map_err(|_| ()) .map(Variable::Number) } #[cfg(not(feature = "arbitrary_precision"))] { - let decimal = match n.as_u64() { Some(n) => Decimal::from_u64(n).ok_or(())?, None => match n.as_i64() { @@ -43,7 +43,7 @@ impl<'arena> ToVariable<'arena> for Value { Ok(Variable::Number(decimal)) } - }, + } Value::String(s) => Ok(Variable::String(arena.alloc_str(s.as_str()))), Value::Array(arr) => { let mut vec = BumpVec::with_capacity_in(arr.len(), arena); diff --git a/core/expression/src/variable/mod.rs b/core/expression/src/variable/mod.rs index 96c58b18..f5bf79e4 100644 --- a/core/expression/src/variable/mod.rs +++ b/core/expression/src/variable/mod.rs @@ -100,7 +100,7 @@ impl<'arena> Variable<'arena> { } if let Some(n_float) = n.to_f64() { - return Value::Number(Number::from_f64(n_float).unwrap()) + return Value::Number(Number::from_f64(n_float).unwrap()); } Value::Null