1use std::cmp::Ordering;23use jrsonnet_ir::{BinaryOpType, UnaryOpType};45use crate::{6 Context, Result, Val,7 analyze::LExpr,8 arr::ArrValue,9 bail, error,10 error::ErrorKind::*,11 evaluate::evaluate,12 stdlib::std_format,13 typed::IntoUntyped as _,14 val::{StrValue, equals},15};1617pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {18 use UnaryOpType::*;19 use Val::*;20 Ok(match (op, b) {21 (Plus, Num(n)) => Val::Num(*n),22 (Minus, Num(n)) => Val::try_num(-n.get())?,23 (Not, Bool(v)) => Bool(!v),24 #[expect(clippy::cast_precision_loss, reason = "as spec")]25 (BitNot, Num(n)) => Val::try_num(!n.truncate_for_bitwise()? as f64)?,26 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),27 })28}2930pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {31 use Val::*;32 Ok(match (a, b) {33 (Str(v1), Str(v2)) => Str(StrValue::concat(v1.clone(), v2.clone())),3435 (Num(a), Str(b)) => Val::string(format!("{a}{b}")),36 (Str(a), Num(b)) => Val::string(format!("{a}{b}")),3738 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::string(o.clone().to_string()?),39 (Str(a), o) => Val::string(format!("{a}{}", o.clone().to_string()?)),40 (o, Str(a)) => Val::string(format!("{}{a}", o.clone().to_string()?)),4142 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),43 (Arr(a), Arr(b)) => Val::Arr(44 ArrValue::extended(a.clone(), b.clone()).ok_or_else(|| error!("array is too large"))?,45 ),4647 (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?,4849 #[cfg(feature = "exp-bigint")]50 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),5152 _ => bail!(BinaryOperatorDoesNotOperateOnValues(53 BinaryOpType::Add,54 a.value_type(),55 b.value_type(),56 )),57 })58}5960pub fn evaluate_sub_op(a: &Val, b: &Val) -> Result<Val> {61 use Val::*;62 Ok(match (a, b) {63 (Num(v1), Num(v2)) => Val::try_num(v1.get() - v2.get())?,6465 #[cfg(feature = "exp-bigint")]66 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a - &**b)),6768 69 _ => bail!(BinaryOperatorDoesNotOperateOnValues(70 BinaryOpType::Sub,71 a.value_type(),72 b.value_type(),73 )),74 })75}7677pub fn evaluate_mul_op(a: &Val, b: &Val) -> Result<Val> {78 use Val::*;79 Ok(match (a, b) {80 #[expect(81 clippy::cast_possible_truncation,82 clippy::cast_sign_loss,83 reason = "should not be used with values too large, negative == 0"84 )]85 (Str(s), Num(c)) => Val::string(s.to_string().repeat(c.get() as usize)),86 #[expect(87 clippy::cast_possible_truncation,88 clippy::cast_sign_loss,89 reason = "should not be used with values too large"90 )]91 (Num(c), Str(s)) => Val::string(s.to_string().repeat(c.get() as usize)),9293 (Num(v1), Num(v2)) => Val::try_num(v1.get() * v2.get())?,9495 #[cfg(feature = "exp-bigint")]96 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a * &**b)),9798 _ => bail!(BinaryOperatorDoesNotOperateOnValues(99 BinaryOpType::Mul,100 a.value_type(),101 b.value_type(),102 )),103 })104}105106fn is_attempt_to_divide_by_zero(a: &Val, b: &Val) -> bool {107 use Val::*;108 match (a, b) {109 110 (Str(_), _) => false,111112 (_, Num(b)) => **b == 0.,113 #[cfg(feature = "exp-bigint")]114 (_, BigInt(b)) => **b == num_bigint::BigInt::ZERO,115116 117 _ => false,118 }119}120121pub fn evaluate_div_op(a: &Val, b: &Val) -> Result<Val> {122 use Val::*;123124 if is_attempt_to_divide_by_zero(a, b) {125 bail!(DivisionByZero);126 }127128 Ok(match (a, b) {129 (Num(a), Num(b)) => Val::try_num(a.get() / b.get())?,130 #[cfg(feature = "exp-bigint")]131 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a / &**b)),132 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(133 BinaryOpType::Div,134 a.value_type(),135 b.value_type()136 )),137 })138}139140pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {141 use Val::*;142143 if is_attempt_to_divide_by_zero(a, b) {144 bail!(DivisionByZero);145 }146147 Ok(match (a, b) {148 (Num(a), Num(b)) => Val::try_num(a.get() % b.get())?,149 #[cfg(feature = "exp-bigint")]150 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a % &**b)),151 (Str(str), vals) => {152 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)?153 }154 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(155 BinaryOpType::Mod,156 a.value_type(),157 b.value_type()158 )),159 })160}161162pub fn evaluate_binary_op_special(163 ctx: Context,164 a: &LExpr,165 op: BinaryOpType,166 b: &LExpr,167) -> Result<Val> {168 use BinaryOpType::*;169 use Val::*;170171 Ok(match (evaluate(ctx.clone(), a)?, op, b) {172 (Bool(true), Or, _) => Val::Bool(true),173 (Bool(false), And, _) => Val::Bool(false),174 #[cfg(feature = "exp-null-coaelse")]175 (Null, NullCoaelse, eb) => evaluate(ctx, eb)?,176 #[cfg(feature = "exp-null-coaelse")]177 (a, NullCoaelse, _) => a,178 (a, In, LExpr::Super) => {179 let sup_this = ctx.try_sup_this()?;180 if !sup_this.has_super() {181 return Ok(Val::Bool(false));182 }183 return Ok(Val::Bool(sup_this.field_in_super(a.to_string()?)));184 }185 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,186 })187}188189pub fn evaluate_compare_op(a: &Val, b: &Val, op: BinaryOpType) -> Result<Ordering> {190 use Val::*;191 Ok(match (a, b) {192 (Str(a), Str(b)) => a.cmp(b),193194 (Num(a), Num(b)) => a.cmp(b),195196 #[cfg(feature = "exp-bigint")]197 (BigInt(a), BigInt(b)) => a.cmp(b),198199 (Arr(a), Arr(b)) => {200 let ai = a.iter();201 let bi = b.iter();202203 for (a, b) in ai.zip(bi) {204 let ord = evaluate_compare_op(&a?, &b?, op)?;205 if !ord.is_eq() {206 return Ok(ord);207 }208 }209 a.len().cmp(&b.len())210 }211 (_, _) => bail!(BinaryOperatorDoesNotOperateOnValues(212 op,213 a.value_type(),214 b.value_type()215 )),216 })217}218219pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {220 use BinaryOpType::*;221 use Val::*;222 Ok(match (a, op, b) {223 (a, Eq, b) => Bool(equals(a, b)?),224 (a, Neq, b) => Bool(!equals(a, b)?),225226 (a, Lt, b) => Bool(evaluate_compare_op(a, b, Lt)?.is_lt()),227 (a, Gt, b) => Bool(evaluate_compare_op(a, b, Gt)?.is_gt()),228 (a, Lte, b) => Bool(evaluate_compare_op(a, b, Lte)?.is_le()),229 (a, Gte, b) => Bool(evaluate_compare_op(a, b, Gte)?.is_ge()),230231 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),232233 234 (Bool(a), And, Bool(b)) => Bool(*a && *b),235 (Bool(a), Or, Bool(b)) => Bool(*a || *b),236237 (a, Add, b) => evaluate_add_op(a, b)?,238 (a, Sub, b) => evaluate_sub_op(a, b)?,239 (a, Mul, b) => evaluate_mul_op(a, b)?,240 (a, Div, b) => evaluate_div_op(a, b)?,241 (a, Mod, b) => evaluate_mod_op(a, b)?,242243 (Num(v1), BitAnd, Num(v2)) =>244 {245 #[expect(246 clippy::cast_precision_loss,247 reason = "values are within safe integer ranges"248 )]249 Val::try_num((v1.truncate_for_bitwise()? & v2.truncate_for_bitwise()?) as f64)?250 }251 (Num(v1), BitOr, Num(v2)) =>252 {253 #[expect(254 clippy::cast_precision_loss,255 reason = "values are within safe integer ranges"256 )]257 Val::try_num((v1.truncate_for_bitwise()? | v2.truncate_for_bitwise()?) as f64)?258 }259 (Num(v1), BitXor, Num(v2)) =>260 {261 #[expect(262 clippy::cast_precision_loss,263 reason = "values are within safe integer ranges"264 )]265 Val::try_num((v1.truncate_for_bitwise()? ^ v2.truncate_for_bitwise()?) as f64)?266 }267 (Num(v1), Lhs, Num(v2)) => {268 if v2.get() < 0.0 {269 bail!("shift by negative exponent")270 }271 let base = v1.truncate_for_bitwise()?;272 let exp = v2.truncate_for_bitwise()? % 64;273274 #[expect(clippy::cast_sign_loss, reason = "exp is positive")]275 if exp >= 1 && base >= (1i64 << (63 - exp as u32)) {276 bail!("left shift would overflow")277 }278 #[expect(279 clippy::cast_precision_loss,280 clippy::cast_sign_loss,281 reason = "checked as original impl"282 )]283 Val::try_num(base.wrapping_shl(exp as u32) as f64)?284 }285 (Num(v1), Rhs, Num(v2)) => {286 if v2.get() < 0.0 {287 bail!("shift by negative exponent")288 }289 #[expect(290 clippy::cast_sign_loss,291 clippy::cast_possible_truncation,292 reason = "checked as original impl"293 )]294 let exp = ((v2.get() as i64) & 63) as u32;295 #[expect(clippy::cast_precision_loss, reason = "checked as upstream impl")]296 Val::try_num(v1.truncate_for_bitwise()?.wrapping_shr(exp) as f64)?297 }298299 300 _ => bail!(BinaryOperatorDoesNotOperateOnValues(301 op,302 a.value_type(),303 b.value_type(),304 )),305 })306}