git.delta.rocks / jrsonnet / refs/commits / b3f009bb7b2e

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/operator.rs6.6 KiBsourcehistory
1use std::cmp::Ordering;23use jrsonnet_parser::{BinaryOpType, Expr, Spanned, 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())?,4445		#[cfg(feature = "exp-bigint")]46		(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),4748		_ => bail!(BinaryOperatorDoesNotOperateOnValues(49			BinaryOpType::Add,50			a.value_type(),51			b.value_type(),52		)),53	})54}5556pub fn evaluate_sub_op(a: &Val, b: &Val) -> Result<Val> {57	use Val::*;58	Ok(match (a, b) {59		(Num(v1), Num(v2)) => Val::try_num(v1.get() - v2.get())?,6061		#[cfg(feature = "exp-bigint")]62		(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a - &**b)),6364		// TODO: Support objects and arrays65		_ => bail!(BinaryOperatorDoesNotOperateOnValues(66			BinaryOpType::Sub,67			a.value_type(),68			b.value_type(),69		)),70	})71}7273pub fn evaluate_mul_op(a: &Val, b: &Val) -> Result<Val> {74	use Val::*;75	Ok(match (a, b) {76		(Str(s), Num(c)) => Val::string(s.to_string().repeat(c.get() as usize)),77		(Num(c), Str(s)) => Val::string(s.to_string().repeat(c.get() as usize)),7879		(Num(v1), Num(v2)) => Val::try_num(v1.get() * v2.get())?,8081		#[cfg(feature = "exp-bigint")]82		(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a * &**b)),8384		_ => bail!(BinaryOperatorDoesNotOperateOnValues(85			BinaryOpType::Mul,86			a.value_type(),87			b.value_type(),88		)),89	})90}9192fn is_attempt_to_divide_by_zero(a: &Val, b: &Val) -> bool {93	use Val::*;94	match (a, b) {95		// string format96		(Str(_), _) => false,9798		(_, Num(b)) => return **b == 0.,99		#[cfg(feature = "exp-bigint")]100		(_, BigInt(b)) => return **b == num_bigint::BigInt::ZERO,101102		// something else103		_ => false,104	}105}106107pub fn evaluate_div_op(a: &Val, b: &Val) -> Result<Val> {108	use Val::*;109110	if is_attempt_to_divide_by_zero(a, b) {111		bail!(DivisionByZero);112	}113114	Ok(match (a, b) {115		(Num(a), Num(b)) => Val::try_num(a.get() / b.get())?,116		#[cfg(feature = "exp-bigint")]117		(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a / &**b)),118		(a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(119			BinaryOpType::Div,120			a.value_type(),121			b.value_type()122		)),123	})124}125126pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {127	use Val::*;128129	if is_attempt_to_divide_by_zero(a, b) {130		bail!(DivisionByZero);131	}132133	Ok(match (a, b) {134		(Num(a), Num(b)) => Val::try_num(a.get() % b.get())?,135		#[cfg(feature = "exp-bigint")]136		(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a % &**b)),137		(Str(str), vals) => {138			String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)?139		}140		(a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(141			BinaryOpType::Mod,142			a.value_type(),143			b.value_type()144		)),145	})146}147148pub fn evaluate_binary_op_special(149	ctx: Context,150	a: &Spanned<Expr>,151	op: BinaryOpType,152	b: &Spanned<Expr>,153) -> Result<Val> {154	use BinaryOpType::*;155	use Val::*;156	Ok(match (evaluate(ctx.clone(), a)?, op, b) {157		(Bool(true), Or, _o) => Val::Bool(true),158		(Bool(false), And, _o) => Val::Bool(false),159		#[cfg(feature = "exp-null-coaelse")]160		(Null, NullCoaelse, eb) => evaluate(ctx, eb)?,161		#[cfg(feature = "exp-null-coaelse")]162		(a, NullCoaelse, _o) => a,163		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,164	})165}166167pub fn evaluate_compare_op(a: &Val, b: &Val, op: BinaryOpType) -> Result<Ordering> {168	use Val::*;169	Ok(match (a, b) {170		(Str(a), Str(b)) => a.cmp(b),171172		(Num(a), Num(b)) => a.cmp(b),173174		#[cfg(feature = "exp-bigint")]175		(BigInt(a), BigInt(b)) => a.cmp(b),176177		(Arr(a), Arr(b)) => {178			let ai = a.iter();179			let bi = b.iter();180181			for (a, b) in ai.zip(bi) {182				let ord = evaluate_compare_op(&a?, &b?, op)?;183				if !ord.is_eq() {184					return Ok(ord);185				}186			}187			a.len().cmp(&b.len())188		}189		(_, _) => bail!(BinaryOperatorDoesNotOperateOnValues(190			op,191			a.value_type(),192			b.value_type()193		)),194	})195}196197pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {198	use BinaryOpType::*;199	use Val::*;200	Ok(match (a, op, b) {201		(a, Eq, b) => Bool(equals(a, b)?),202		(a, Neq, b) => Bool(!equals(a, b)?),203204		(a, Lt, b) => Bool(evaluate_compare_op(a, b, Lt)?.is_lt()),205		(a, Gt, b) => Bool(evaluate_compare_op(a, b, Gt)?.is_gt()),206		(a, Lte, b) => Bool(evaluate_compare_op(a, b, Lte)?.is_le()),207		(a, Gte, b) => Bool(evaluate_compare_op(a, b, Gte)?.is_ge()),208209		(Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),210211		// Bool X Bool212		(Bool(a), And, Bool(b)) => Bool(*a && *b),213		(Bool(a), Or, Bool(b)) => Bool(*a || *b),214215		(a, Add, b) => evaluate_add_op(a, b)?,216		(a, Sub, b) => evaluate_sub_op(a, b)?,217		(a, Mul, b) => evaluate_mul_op(a, b)?,218		(a, Div, b) => evaluate_div_op(a, b)?,219		(a, Mod, b) => evaluate_mod_op(a, b)?,220221		(Num(v1), BitAnd, Num(v2)) => {222			Val::try_num((v1.truncate_for_bitwise()? & v2.truncate_for_bitwise()?) as f64)?223		}224		(Num(v1), BitOr, Num(v2)) => {225			Val::try_num((v1.truncate_for_bitwise()? | v2.truncate_for_bitwise()?) as f64)?226		}227		(Num(v1), BitXor, Num(v2)) => {228			Val::try_num((v1.truncate_for_bitwise()? ^ v2.truncate_for_bitwise()?) as f64)?229		}230		(Num(v1), Lhs, Num(v2)) => {231			if v2.get() < 0.0 {232				bail!("shift by negative exponent")233			}234			let base = v1.truncate_for_bitwise()?;235			let exp = v2.truncate_for_bitwise()? % 64;236237			if exp >= 1 && base >= (1i64 << (63 - exp as u32)) {238				bail!("left shift would overflow")239			}240			Val::try_num(base.wrapping_shl(exp as u32) as f64)?241		}242		(Num(v1), Rhs, Num(v2)) => {243			if v2.get() < 0.0 {244				bail!("shift by negative exponent")245			}246			let exp = ((v2.get() as i64) & 63) as u32;247			Val::try_num(v1.truncate_for_bitwise()?.wrapping_shr(exp) as f64)?248		}249250		// Bigint X Bigint251		_ => bail!(BinaryOperatorDoesNotOperateOnValues(252			op,253			a.value_type(),254			b.value_type(),255		)),256	})257}