From 8954130fcd4f07134b6ff459936b0678f2217aee Mon Sep 17 00:00:00 2001 From: Valera Klachkov Date: Tue, 03 Feb 2026 02:35:12 +0000 Subject: [PATCH] feat: more math operations for bigint (#198) * feat: properly implement math ops for bigint * feat: remove bigint x string and bigint x num ops every op now requires explicit conversion * fix: remove dead code --- --- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs @@ -2,6 +2,11 @@ use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType}; +#[cfg(feature = "exp-bigint")] +use num_traits::{FromPrimitive, ToPrimitive}; + +#[cfg(feature = "exp-bigint")] +use crate::val::NumValue; use crate::{ arr::ArrValue, bail, @@ -41,8 +46,10 @@ (Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())), (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?, + #[cfg(feature = "exp-bigint")] (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)), + _ => bail!(BinaryOperatorDoesNotOperateOnValues( BinaryOpType::Add, a.value_type(), @@ -51,24 +58,96 @@ }) } -pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result { +pub fn evaluate_sub_op(a: &Val, b: &Val) -> Result { + use Val::*; + Ok(match (a, b) { + (Num(v1), Num(v2)) => Val::try_num(v1.get() - v2.get())?, + + #[cfg(feature = "exp-bigint")] + (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a - &**b)), + + // TODO: Support objects and arrays + _ => bail!(BinaryOperatorDoesNotOperateOnValues( + BinaryOpType::Sub, + a.value_type(), + b.value_type(), + )), + }) +} + +pub fn evaluate_mul_op(a: &Val, b: &Val) -> Result { use Val::*; + Ok(match (a, b) { + (Str(s), Num(c)) => Val::string(s.to_string().repeat(c.get() as usize)), + (Num(c), Str(s)) => Val::string(s.to_string().repeat(c.get() as usize)), + + (Num(v1), Num(v2)) => Val::try_num(v1.get() * v2.get())?, + + #[cfg(feature = "exp-bigint")] + (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a * &**b)), + + _ => bail!(BinaryOperatorDoesNotOperateOnValues( + BinaryOpType::Mul, + a.value_type(), + b.value_type(), + )), + }) +} + +fn is_attempt_to_divide_by_zero(a: &Val, b: &Val) -> bool { + use Val::*; match (a, b) { - (Num(a), Num(b)) => { - if b.get() == 0.0 { - bail!(DivisionByZero) - } - Ok(Val::try_num(a.get() % b.get())?) - } + // string format + (Str(_), _) => false, + + (_, Num(b)) => return **b == 0., + #[cfg(feature = "exp-bigint")] + (_, BigInt(b)) => return **b == num_bigint::BigInt::ZERO, + + // something else + _ => false, + } +} + +pub fn evaluate_div_op(a: &Val, b: &Val) -> Result { + use Val::*; + + if is_attempt_to_divide_by_zero(a, b) { + bail!(DivisionByZero); + } + + Ok(match (a, b) { + (Num(a), Num(b)) => Val::try_num(a.get() / b.get())?, + #[cfg(feature = "exp-bigint")] + (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a / &**b)), + (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues( + BinaryOpType::Div, + a.value_type(), + b.value_type() + )), + }) +} + +pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result { + use Val::*; + + if is_attempt_to_divide_by_zero(a, b) { + bail!(DivisionByZero); + } + + Ok(match (a, b) { + (Num(a), Num(b)) => Val::try_num(a.get() % b.get())?, + #[cfg(feature = "exp-bigint")] + (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a % &**b)), (Str(str), vals) => { - String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?) + String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)? } (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues( BinaryOpType::Mod, a.value_type(), b.value_type() )), - } + }) } pub fn evaluate_binary_op_special( @@ -94,9 +173,12 @@ use Val::*; Ok(match (a, b) { (Str(a), Str(b)) => a.cmp(b), + (Num(a), Num(b)) => a.cmp(b), + #[cfg(feature = "exp-bigint")] (BigInt(a), BigInt(b)) => a.cmp(b), + (Arr(a), Arr(b)) => { let ai = a.iter(); let bi = b.iter(); @@ -121,8 +203,6 @@ use BinaryOpType::*; use Val::*; Ok(match (a, op, b) { - (a, Add, b) => evaluate_add_op(a, b)?, - (a, Eq, b) => Bool(equals(a, b)?), (a, Neq, b) => Bool(!equals(a, b)?), @@ -132,24 +212,16 @@ (a, Gte, b) => Bool(evaluate_compare_op(a, b, Gte)?.is_ge()), (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)), - (a, Mod, b) => evaluate_mod_op(a, b)?, - - (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(v2.get() as usize)), // Bool X Bool (Bool(a), And, Bool(b)) => Bool(*a && *b), (Bool(a), Or, Bool(b)) => Bool(*a || *b), - // Num X Num - (Num(v1), Mul, Num(v2)) => Val::try_num(v1.get() * v2.get())?, - (Num(v1), Div, Num(v2)) => { - if v2.get() == 0.0 { - bail!(DivisionByZero) - } - Val::try_num(v1.get() / v2.get())? - } - - (Num(v1), Sub, Num(v2)) => Val::try_num(v1.get() - v2.get())?, + (a, Add, b) => evaluate_add_op(a, b)?, + (a, Sub, b) => evaluate_sub_op(a, b)?, + (a, Mul, b) => evaluate_mul_op(a, b)?, + (a, Div, b) => evaluate_div_op(a, b)?, + (a, Mod, b) => evaluate_mod_op(a, b)?, (Num(v1), BitAnd, Num(v2)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?, (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?, @@ -170,11 +242,6 @@ } // Bigint X Bigint - #[cfg(feature = "exp-bigint")] - (BigInt(a), Mul, BigInt(b)) => BigInt(Box::new(&**a * &**b)), - #[cfg(feature = "exp-bigint")] - (BigInt(a), Sub, BigInt(b)) => BigInt(Box::new(&**a - &**b)), - _ => bail!(BinaryOperatorDoesNotOperateOnValues( op, a.value_type(), -- gitstuff