git.delta.rocks / jrsonnet / refs/heads / master

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/operator.rs7.9 KiBsourcehistory
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		// TODO: Support objects and arrays69		_ => 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		// string format110		(Str(_), _) => false,111112		(_, Num(b)) => **b == 0.,113		#[cfg(feature = "exp-bigint")]114		(_, BigInt(b)) => **b == num_bigint::BigInt::ZERO,115116		// something else117		_ => 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		// Bool X Bool234		(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		// Bigint X Bigint300		_ => bail!(BinaryOperatorDoesNotOperateOnValues(301			op,302			a.value_type(),303			b.value_type(),304		)),305	})306}