difftreelog
feat more math operations for bigint (#198)
in: master
* 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
1 file changed
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth1use std::cmp::Ordering;23use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType};45use crate::{6 arr::ArrValue,7 bail,8 error::ErrorKind::*,9 evaluate,10 stdlib::std_format,11 typed::Typed,12 val::{equals, StrValue},13 Context, Result, Val,14};1516pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {17 use UnaryOpType::*;18 use Val::*;19 Ok(match (op, b) {20 (Plus, Num(n)) => Val::Num(*n),21 (Minus, Num(n)) => Val::try_num(-n.get())?,22 (Not, Bool(v)) => Bool(!v),23 (BitNot, Num(n)) => Val::try_num(!(n.get() as i64) as f64)?,24 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),25 })26}2728pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {29 use Val::*;30 Ok(match (a, b) {31 (Str(v1), Str(v2)) => Str(StrValue::concat(v1.clone(), v2.clone())),3233 (Num(a), Str(b)) => Val::string(format!("{a}{b}")),34 (Str(a), Num(b)) => Val::string(format!("{a}{b}")),3536 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::string(o.clone().to_string()?),37 (Str(a), o) => Val::string(format!("{a}{}", o.clone().to_string()?)),38 (o, Str(a)) => Val::string(format!("{}{a}", o.clone().to_string()?)),3940 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),41 (Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),4243 (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?,44 #[cfg(feature = "exp-bigint")]45 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),46 _ => bail!(BinaryOperatorDoesNotOperateOnValues(47 BinaryOpType::Add,48 a.value_type(),49 b.value_type(),50 )),51 })52}5354pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {55 use Val::*;56 match (a, b) {57 (Num(a), Num(b)) => {58 if b.get() == 0.0 {59 bail!(DivisionByZero)60 }61 Ok(Val::try_num(a.get() % b.get())?)62 }63 (Str(str), vals) => {64 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)65 }66 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(67 BinaryOpType::Mod,68 a.value_type(),69 b.value_type()70 )),71 }72}7374pub fn evaluate_binary_op_special(75 ctx: Context,76 a: &LocExpr,77 op: BinaryOpType,78 b: &LocExpr,79) -> Result<Val> {80 use BinaryOpType::*;81 use Val::*;82 Ok(match (evaluate(ctx.clone(), a)?, op, b) {83 (Bool(true), Or, _o) => Val::Bool(true),84 (Bool(false), And, _o) => Val::Bool(false),85 #[cfg(feature = "exp-null-coaelse")]86 (Null, NullCoaelse, eb) => evaluate(ctx, eb)?,87 #[cfg(feature = "exp-null-coaelse")]88 (a, NullCoaelse, _o) => a,89 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,90 })91}9293pub fn evaluate_compare_op(a: &Val, b: &Val, op: BinaryOpType) -> Result<Ordering> {94 use Val::*;95 Ok(match (a, b) {96 (Str(a), Str(b)) => a.cmp(b),97 (Num(a), Num(b)) => a.cmp(b),98 #[cfg(feature = "exp-bigint")]99 (BigInt(a), BigInt(b)) => a.cmp(b),100 (Arr(a), Arr(b)) => {101 let ai = a.iter();102 let bi = b.iter();103104 for (a, b) in ai.zip(bi) {105 let ord = evaluate_compare_op(&a?, &b?, op)?;106 if !ord.is_eq() {107 return Ok(ord);108 }109 }110 a.len().cmp(&b.len())111 }112 (_, _) => bail!(BinaryOperatorDoesNotOperateOnValues(113 op,114 a.value_type(),115 b.value_type()116 )),117 })118}119120pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {121 use BinaryOpType::*;122 use Val::*;123 Ok(match (a, op, b) {124 (a, Add, b) => evaluate_add_op(a, b)?,125126 (a, Eq, b) => Bool(equals(a, b)?),127 (a, Neq, b) => Bool(!equals(a, b)?),128129 (a, Lt, b) => Bool(evaluate_compare_op(a, b, Lt)?.is_lt()),130 (a, Gt, b) => Bool(evaluate_compare_op(a, b, Gt)?.is_gt()),131 (a, Lte, b) => Bool(evaluate_compare_op(a, b, Lte)?.is_le()),132 (a, Gte, b) => Bool(evaluate_compare_op(a, b, Gte)?.is_ge()),133134 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),135 (a, Mod, b) => evaluate_mod_op(a, b)?,136137 (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(v2.get() as usize)),138139 // Bool X Bool140 (Bool(a), And, Bool(b)) => Bool(*a && *b),141 (Bool(a), Or, Bool(b)) => Bool(*a || *b),142143 // Num X Num144 (Num(v1), Mul, Num(v2)) => Val::try_num(v1.get() * v2.get())?,145 (Num(v1), Div, Num(v2)) => {146 if v2.get() == 0.0 {147 bail!(DivisionByZero)148 }149 Val::try_num(v1.get() / v2.get())?150 }151152 (Num(v1), Sub, Num(v2)) => Val::try_num(v1.get() - v2.get())?,153154 (Num(v1), BitAnd, Num(v2)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?,155 (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?,156 (Num(v1), BitXor, Num(v2)) => Val::try_num((v1.get() as i64 ^ v2.get() as i64) as f64)?,157 (Num(v1), Lhs, Num(v2)) => {158 if v2.get() < 0.0 {159 bail!("shift by negative exponent")160 }161 let exp = ((v2.get() as i64) & 63) as u32;162 Val::try_num((v1.get() as i64).wrapping_shl(exp) as f64)?163 }164 (Num(v1), Rhs, Num(v2)) => {165 if v2.get() < 0.0 {166 bail!("shift by negative exponent")167 }168 let exp = ((v2.get() as i64) & 63) as u32;169 Val::try_num((v1.get() as i64).wrapping_shr(exp) as f64)?170 }171172 // Bigint X Bigint173 #[cfg(feature = "exp-bigint")]174 (BigInt(a), Mul, BigInt(b)) => BigInt(Box::new(&**a * &**b)),175 #[cfg(feature = "exp-bigint")]176 (BigInt(a), Sub, BigInt(b)) => BigInt(Box::new(&**a - &**b)),177178 _ => bail!(BinaryOperatorDoesNotOperateOnValues(179 op,180 a.value_type(),181 b.value_type(),182 )),183 })184}1use std::cmp::Ordering;23use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType};45#[cfg(feature = "exp-bigint")]6use num_traits::{FromPrimitive, ToPrimitive};78#[cfg(feature = "exp-bigint")]9use crate::val::NumValue;10use crate::{11 arr::ArrValue,12 bail,13 error::ErrorKind::*,14 evaluate,15 stdlib::std_format,16 typed::Typed,17 val::{equals, StrValue},18 Context, Result, Val,19};2021pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {22 use UnaryOpType::*;23 use Val::*;24 Ok(match (op, b) {25 (Plus, Num(n)) => Val::Num(*n),26 (Minus, Num(n)) => Val::try_num(-n.get())?,27 (Not, Bool(v)) => Bool(!v),28 (BitNot, Num(n)) => Val::try_num(!(n.get() as i64) as f64)?,29 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),30 })31}3233pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {34 use Val::*;35 Ok(match (a, b) {36 (Str(v1), Str(v2)) => Str(StrValue::concat(v1.clone(), v2.clone())),3738 (Num(a), Str(b)) => Val::string(format!("{a}{b}")),39 (Str(a), Num(b)) => Val::string(format!("{a}{b}")),4041 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::string(o.clone().to_string()?),42 (Str(a), o) => Val::string(format!("{a}{}", o.clone().to_string()?)),43 (o, Str(a)) => Val::string(format!("{}{a}", o.clone().to_string()?)),4445 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),46 (Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),4748 (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?,4950 #[cfg(feature = "exp-bigint")]51 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),5253 _ => bail!(BinaryOperatorDoesNotOperateOnValues(54 BinaryOpType::Add,55 a.value_type(),56 b.value_type(),57 )),58 })59}6061pub fn evaluate_sub_op(a: &Val, b: &Val) -> Result<Val> {62 use Val::*;63 Ok(match (a, b) {64 (Num(v1), Num(v2)) => Val::try_num(v1.get() - v2.get())?,6566 #[cfg(feature = "exp-bigint")]67 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a - &**b)),6869 // TODO: Support objects and arrays70 _ => bail!(BinaryOperatorDoesNotOperateOnValues(71 BinaryOpType::Sub,72 a.value_type(),73 b.value_type(),74 )),75 })76}7778pub fn evaluate_mul_op(a: &Val, b: &Val) -> Result<Val> {79 use Val::*;80 Ok(match (a, b) {81 (Str(s), Num(c)) => Val::string(s.to_string().repeat(c.get() as usize)),82 (Num(c), Str(s)) => Val::string(s.to_string().repeat(c.get() as usize)),8384 (Num(v1), Num(v2)) => Val::try_num(v1.get() * v2.get())?,8586 #[cfg(feature = "exp-bigint")]87 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a * &**b)),8889 _ => bail!(BinaryOperatorDoesNotOperateOnValues(90 BinaryOpType::Mul,91 a.value_type(),92 b.value_type(),93 )),94 })95}9697fn is_attempt_to_divide_by_zero(a: &Val, b: &Val) -> bool {98 use Val::*;99 match (a, b) {100 // string format101 (Str(_), _) => false,102103 (_, Num(b)) => return **b == 0.,104 #[cfg(feature = "exp-bigint")]105 (_, BigInt(b)) => return **b == num_bigint::BigInt::ZERO,106107 // something else108 _ => false,109 }110}111112pub fn evaluate_div_op(a: &Val, b: &Val) -> Result<Val> {113 use Val::*;114115 if is_attempt_to_divide_by_zero(a, b) {116 bail!(DivisionByZero);117 }118119 Ok(match (a, b) {120 (Num(a), Num(b)) => Val::try_num(a.get() / b.get())?,121 #[cfg(feature = "exp-bigint")]122 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a / &**b)),123 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(124 BinaryOpType::Div,125 a.value_type(),126 b.value_type()127 )),128 })129}130131pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {132 use Val::*;133134 if is_attempt_to_divide_by_zero(a, b) {135 bail!(DivisionByZero);136 }137138 Ok(match (a, b) {139 (Num(a), Num(b)) => Val::try_num(a.get() % b.get())?,140 #[cfg(feature = "exp-bigint")]141 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a % &**b)),142 (Str(str), vals) => {143 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)?144 }145 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(146 BinaryOpType::Mod,147 a.value_type(),148 b.value_type()149 )),150 })151}152153pub fn evaluate_binary_op_special(154 ctx: Context,155 a: &LocExpr,156 op: BinaryOpType,157 b: &LocExpr,158) -> Result<Val> {159 use BinaryOpType::*;160 use Val::*;161 Ok(match (evaluate(ctx.clone(), a)?, op, b) {162 (Bool(true), Or, _o) => Val::Bool(true),163 (Bool(false), And, _o) => Val::Bool(false),164 #[cfg(feature = "exp-null-coaelse")]165 (Null, NullCoaelse, eb) => evaluate(ctx, eb)?,166 #[cfg(feature = "exp-null-coaelse")]167 (a, NullCoaelse, _o) => a,168 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,169 })170}171172pub fn evaluate_compare_op(a: &Val, b: &Val, op: BinaryOpType) -> Result<Ordering> {173 use Val::*;174 Ok(match (a, b) {175 (Str(a), Str(b)) => a.cmp(b),176177 (Num(a), Num(b)) => a.cmp(b),178179 #[cfg(feature = "exp-bigint")]180 (BigInt(a), BigInt(b)) => a.cmp(b),181182 (Arr(a), Arr(b)) => {183 let ai = a.iter();184 let bi = b.iter();185186 for (a, b) in ai.zip(bi) {187 let ord = evaluate_compare_op(&a?, &b?, op)?;188 if !ord.is_eq() {189 return Ok(ord);190 }191 }192 a.len().cmp(&b.len())193 }194 (_, _) => bail!(BinaryOperatorDoesNotOperateOnValues(195 op,196 a.value_type(),197 b.value_type()198 )),199 })200}201202pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {203 use BinaryOpType::*;204 use Val::*;205 Ok(match (a, op, b) {206 (a, Eq, b) => Bool(equals(a, b)?),207 (a, Neq, b) => Bool(!equals(a, b)?),208209 (a, Lt, b) => Bool(evaluate_compare_op(a, b, Lt)?.is_lt()),210 (a, Gt, b) => Bool(evaluate_compare_op(a, b, Gt)?.is_gt()),211 (a, Lte, b) => Bool(evaluate_compare_op(a, b, Lte)?.is_le()),212 (a, Gte, b) => Bool(evaluate_compare_op(a, b, Gte)?.is_ge()),213214 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),215216 // Bool X Bool217 (Bool(a), And, Bool(b)) => Bool(*a && *b),218 (Bool(a), Or, Bool(b)) => Bool(*a || *b),219220 (a, Add, b) => evaluate_add_op(a, b)?,221 (a, Sub, b) => evaluate_sub_op(a, b)?,222 (a, Mul, b) => evaluate_mul_op(a, b)?,223 (a, Div, b) => evaluate_div_op(a, b)?,224 (a, Mod, b) => evaluate_mod_op(a, b)?,225226 (Num(v1), BitAnd, Num(v2)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?,227 (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?,228 (Num(v1), BitXor, Num(v2)) => Val::try_num((v1.get() as i64 ^ v2.get() as i64) as f64)?,229 (Num(v1), Lhs, Num(v2)) => {230 if v2.get() < 0.0 {231 bail!("shift by negative exponent")232 }233 let exp = ((v2.get() as i64) & 63) as u32;234 Val::try_num((v1.get() as i64).wrapping_shl(exp) as f64)?235 }236 (Num(v1), Rhs, Num(v2)) => {237 if v2.get() < 0.0 {238 bail!("shift by negative exponent")239 }240 let exp = ((v2.get() as i64) & 63) as u32;241 Val::try_num((v1.get() as i64).wrapping_shr(exp) as f64)?242 }243244 // Bigint X Bigint245 _ => bail!(BinaryOperatorDoesNotOperateOnValues(246 op,247 a.value_type(),248 b.value_type(),249 )),250 })251}