From d179552b3ca521cd7bc91d000218bd65bef814b7 Mon Sep 17 00:00:00 2001 From: stefan-gorules <127550877+stefan-gorules@users.noreply.github.com> Date: Wed, 14 May 2025 08:21:29 +0200 Subject: [PATCH] feat: trunc function and decimal places for round (optional) (#349) --- core/expression/src/functions/internal.rs | 47 +++++++++++++++++++++-- core/expression/src/parser/unary.rs | 1 + core/expression/tests/data/standard.csv | 35 ++++++++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/core/expression/src/functions/internal.rs b/core/expression/src/functions/internal.rs index 3763fe20..abce92ad 100644 --- a/core/expression/src/functions/internal.rs +++ b/core/expression/src/functions/internal.rs @@ -35,6 +35,7 @@ pub enum InternalFunction { Floor, Ceil, Round, + Trunc, // Type IsNumeric, @@ -173,9 +174,32 @@ impl From<&InternalFunction> for Rc { signature: FunctionSignature::single(VT::Number, VT::Number), }), - IF::Round => Rc::new(StaticFunction { + IF::Round => Rc::new(CompositeFunction { implementation: Rc::new(imp::round), - signature: FunctionSignature::single(VT::Number, VT::Number), + signatures: vec![ + FunctionSignature { + parameters: vec![VT::Number], + return_type: VT::Number, + }, + FunctionSignature { + parameters: vec![VT::Number, VT::Number], + return_type: VT::Number, + }, + ], + }), + + IF::Trunc => Rc::new(CompositeFunction { + implementation: Rc::new(imp::trunc), + signatures: vec![ + FunctionSignature { + parameters: vec![VT::Number], + return_type: VT::Number, + }, + FunctionSignature { + parameters: vec![VT::Number, VT::Number], + return_type: VT::Number, + }, + ], }), IF::Sum => Rc::new(StaticFunction { @@ -404,7 +428,24 @@ pub(crate) mod imp { pub fn round(args: Arguments) -> anyhow::Result { let a = args.number(0)?; - Ok(V::Number(a.round())) + let dp = args + .onumber(1)? + .map(|v| v.to_u32().context("Invalid number of decimal places")) + .transpose()? + .unwrap_or(0); + + Ok(V::Number(a.round_dp(dp))) + } + + pub fn trunc(args: Arguments) -> anyhow::Result { + let a = args.number(0)?; + let dp = args + .onumber(1)? + .map(|v| v.to_u32().context("Invalid number of decimal places")) + .transpose()? + .unwrap_or(0); + + Ok(V::Number(a.trunc_with_scale(dp))) } pub fn rand(args: Arguments) -> anyhow::Result { diff --git a/core/expression/src/parser/unary.rs b/core/expression/src/parser/unary.rs index 0658e725..db3eba4f 100644 --- a/core/expression/src/parser/unary.rs +++ b/core/expression/src/parser/unary.rs @@ -353,6 +353,7 @@ impl From<&Node<'_>> for UnaryNodeBehaviour { InternalFunction::Floor => CompareWithReference(Equal), InternalFunction::Ceil => CompareWithReference(Equal), InternalFunction::Round => CompareWithReference(Equal), + InternalFunction::Trunc => CompareWithReference(Equal), InternalFunction::String => CompareWithReference(Equal), InternalFunction::Number => CompareWithReference(Equal), InternalFunction::Bool => CompareWithReference(Equal), diff --git a/core/expression/tests/data/standard.csv b/core/expression/tests/data/standard.csv index 24c63573..dd6d087e 100644 --- a/core/expression/tests/data/standard.csv +++ b/core/expression/tests/data/standard.csv @@ -432,4 +432,37 @@ x not in [1..10];{"x":5};false # Interval iterator map([0..5], # * 2);;[0, 2, 4, 6, 8, 10] -filter([0..10], # % 2 == 0);;[0, 2, 4, 6, 8, 10] \ No newline at end of file +filter([0..10], # % 2 == 0);;[0, 2, 4, 6, 8, 10] + +# Rounding +round(7.4);; 7 +round(7.5);; 8 +round(7.6);; 8 +round(-7.4);; -7 +round(-7.5);; -8 +round(-7.6);; -8 + +round(7.444, 0);; 7 +round(7.444, 1);; 7.4 +round(7.444, 2);; 7.44 +round(7.555, 0);; 8 +round(7.555, 1);; 7.6 +round(7.555, 2);; 7.56 +round(-7.444, 2);; -7.44 +round(-7.555, 2);; -7.56 + +trunc(7.4);; 7 +trunc(7.5);; 7 +trunc(7.6);; 7 +trunc(-7.4);; -7 +trunc(-7.5);; -7 +trunc(-7.6);; -7 + +trunc(7.444, 0);; 7 +trunc(7.444, 1);; 7.4 +trunc(7.444, 2);; 7.44 +trunc(7.999, 0);; 7 +trunc(7.999, 1);; 7.9 +trunc(7.999, 2);; 7.99 +trunc(-7.444, 2);; -7.44 +trunc(-7.999, 2);; -7.99 \ No newline at end of file