difftreelog
fix align std.format output with standard jsonnet changes
in: master
4 files 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())?,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: &LocExpr,151 op: BinaryOpType,152 b: &LocExpr,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)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?,222 (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?,223 (Num(v1), BitXor, Num(v2)) => Val::try_num((v1.get() as i64 ^ v2.get() as i64) as f64)?,224 (Num(v1), Lhs, Num(v2)) => {225 if v2.get() < 0.0 {226 bail!("shift by negative exponent")227 }228 let exp = ((v2.get() as i64) & 63) as u32;229 Val::try_num((v1.get() as i64).wrapping_shl(exp) as f64)?230 }231 (Num(v1), Rhs, Num(v2)) => {232 if v2.get() < 0.0 {233 bail!("shift by negative exponent")234 }235 let exp = ((v2.get() as i64) & 63) as u32;236 Val::try_num((v1.get() as i64).wrapping_shr(exp) as f64)?237 }238239 // Bigint X Bigint240 _ => bail!(BinaryOperatorDoesNotOperateOnValues(241 op,242 a.value_type(),243 b.value_type(),244 )),245 })246}crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/format.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs
@@ -137,7 +137,7 @@
#[derive(Debug, PartialEq, Eq)]
pub enum Width {
Star,
- Fixed(usize),
+ Fixed(u16),
}
pub fn try_parse_field_width(str: &str) -> ParseResult<'_, Width> {
if str.is_empty() {
@@ -147,11 +147,11 @@
if bytes[0] == b'*' {
return Ok((Width::Star, &str[1..]));
}
- let mut out: usize = 0;
+ let mut out: u16 = 0;
let mut digits = 0;
while let Some(digit) = (bytes[digits] as char).to_digit(10) {
out *= 10;
- out += digit as usize;
+ out += digit as u16;
digits += 1;
if digits == bytes.len() {
return Err(TruncatedFormatCode);
@@ -299,15 +299,18 @@
#[inline]
pub fn render_integer(
out: &mut String,
+ neg: bool,
iv: f64,
- padding: usize,
- precision: usize,
+ padding: u16,
+ precision: u16,
blank: bool,
sign: bool,
radix: i64,
- prefix: &str,
+ zero_prefix: &str,
+ prefix_in_padding: bool,
caps: bool,
) {
+ debug_assert!(iv >= 0.0, "render_integer receives sign using arg");
let iv = iv.floor() as i64;
// Digit char indexes in reverse order, i.e
// for radix = 16 and n = 12f: [15, 2, 1]
@@ -322,12 +325,14 @@
}
nums
};
- let neg = iv < 0;
#[allow(clippy::bool_to_int_with_if)]
let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });
+
+ let pref_len = zero_prefix.len() as u16;
let zp2 = zp
+ .saturating_sub(if !prefix_in_padding { pref_len } else { 0 })
.max(precision)
- .saturating_sub(prefix.len() + digits.len());
+ .saturating_sub(if prefix_in_padding { pref_len } else { 0 } + digits.len() as u16);
if neg {
out.push('-');
@@ -337,11 +342,13 @@
out.push(' ');
}
- out.reserve(zp2);
+ out.reserve(zp2 as usize);
+ if iv != 0 {
+ out.push_str(zero_prefix);
+ }
for _ in 0..zp2 {
out.push('0');
}
- out.push_str(prefix);
for digit in digits.into_iter().rev() {
let ch = NUMBERS[digit as usize] as char;
@@ -351,25 +358,30 @@
pub fn render_decimal(
out: &mut String,
+ neg: bool,
iv: f64,
- padding: usize,
- precision: usize,
+ padding: u16,
+ precision: u16,
blank: bool,
sign: bool,
) {
- render_integer(out, iv, padding, precision, blank, sign, 10, "", false);
+ render_integer(
+ out, neg, iv, padding, precision, blank, sign, 10, "", false, false,
+ );
}
pub fn render_octal(
out: &mut String,
+ neg: bool,
iv: f64,
- padding: usize,
- precision: usize,
+ padding: u16,
+ precision: u16,
alt: bool,
blank: bool,
sign: bool,
) {
render_integer(
out,
+ neg,
iv,
padding,
precision,
@@ -377,6 +389,7 @@
sign,
8,
if alt && iv != 0.0 { "0" } else { "" },
+ true,
false,
);
}
@@ -385,8 +398,8 @@
pub fn render_hexadecimal(
out: &mut String,
iv: f64,
- padding: usize,
- precision: usize,
+ padding: u16,
+ precision: u16,
alt: bool,
blank: bool,
sign: bool,
@@ -394,7 +407,8 @@
) {
render_integer(
out,
- iv,
+ iv < 0.0,
+ iv.abs(),
padding,
precision,
blank,
@@ -405,6 +419,7 @@
(true, false) => "0x",
(false, _) => "",
},
+ false,
caps,
);
}
@@ -413,31 +428,36 @@
pub fn render_float(
out: &mut String,
n: f64,
- mut padding: usize,
- precision: usize,
+ mut padding: u16,
+ precision: u16,
blank: bool,
sign: bool,
ensure_pt: bool,
trailing: bool,
) {
+ // Represent the rounded number as an integer * 1/10**prec.
+ // Note that it can also be equal to 10**prec and we'll need to carry
+ // over to the wholes. We operate on the absolute numbers, so that we
+ // don't have trouble with the rounding direction.
+ let denominator = 10.0f64.powi(precision as i32);
+ let numerator = n.abs() * denominator + 0.5;
+ let whole = (numerator / denominator).floor();
+ let frac = numerator.floor() % denominator;
+
#[allow(clippy::bool_to_int_with_if)]
let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };
padding = padding.saturating_sub(dot_size + precision);
- render_decimal(out, n.floor(), padding, 0, blank, sign);
+ render_decimal(out, n < 0.0, whole, padding, 0, blank, sign);
if precision == 0 {
if ensure_pt {
out.push('.');
}
return;
}
- let frac = n
- .fract()
- .mul_add(10.0_f64.powf(precision as f64), 0.5)
- .floor();
if trailing || frac > 0.0 {
out.push('.');
let mut frac_str = String::new();
- render_decimal(&mut frac_str, frac, precision, 0, false, false);
+ render_decimal(&mut frac_str, false, frac, precision, 0, false, false);
let mut trim = frac_str.len();
if !trailing {
for b in frac_str.as_bytes().iter().rev() {
@@ -458,25 +478,38 @@
pub fn render_float_sci(
out: &mut String,
n: f64,
- mut padding: usize,
- precision: usize,
+ mut padding: u16,
+ precision: u16,
blank: bool,
sign: bool,
ensure_pt: bool,
trailing: bool,
caps: bool,
) {
- let exponent = n.log10().floor();
+ let exponent = if n == 0.0 {
+ 0.0
+ } else {
+ n.abs().log10().floor()
+ };
+
let mantissa = if exponent as i16 == -324 {
n * 10.0 / 10.0_f64.powf(exponent + 1.0)
} else {
n / 10.0_f64.powf(exponent)
};
let mut exponent_str = String::new();
- render_decimal(&mut exponent_str, exponent, 3, 0, false, true);
+ render_decimal(
+ &mut exponent_str,
+ exponent < 0.0,
+ exponent.abs(),
+ 3,
+ 0,
+ false,
+ true,
+ );
// +1 for e
- padding = padding.saturating_sub(exponent_str.len() + 1);
+ padding = padding.saturating_sub(exponent_str.len() as u16 + 1);
render_float(
out, mantissa, padding, precision, blank, sign, ensure_pt, trailing,
@@ -490,8 +523,8 @@
out: &mut String,
value: &Val,
code: &Code<'_>,
- width: usize,
- precision: Option<usize>,
+ width: u16,
+ precision: Option<u16>,
) -> Result<()> {
let clfags = &code.cflags;
let (fpprec, iprec) = precision.map_or((6, 0), |v| (v, v));
@@ -510,7 +543,8 @@
let value = f64::from_untyped(value.clone())?;
render_decimal(
&mut tmp_out,
- value,
+ value <= -1.0,
+ value.abs(),
padding,
iprec,
clfags.blank,
@@ -521,7 +555,8 @@
let value = f64::from_untyped(value.clone())?;
render_octal(
&mut tmp_out,
- value,
+ value <= -1.0,
+ value.abs(),
padding,
iprec,
clfags.alt,
@@ -589,7 +624,7 @@
code.caps,
);
} else {
- let digits_before_pt = 1.max(exponent as usize + 1);
+ let digits_before_pt = 1.max(exponent as u16 + 1);
render_float(
&mut tmp_out,
value,
@@ -628,7 +663,7 @@
ConvTypeV::Percent => tmp_out.push('%'),
};
- let padding = width.saturating_sub(tmp_out.len());
+ let padding = width.saturating_sub(tmp_out.len() as u16);
if !clfags.left {
for _ in 0..padding {
@@ -663,7 +698,7 @@
}
let value = &values[0];
values = &values[1..];
- usize::from_untyped(value.clone())?
+ u16::from_untyped(value.clone())?
}
Width::Fixed(n) => n,
};
@@ -674,7 +709,7 @@
}
let value = &values[0];
values = &values[1..];
- Some(usize::from_untyped(value.clone())?)
+ Some(u16::from_untyped(value.clone())?)
}
Some(Width::Fixed(n)) => Some(n),
None => None,
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -120,8 +120,8 @@
}
}
-pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;
-pub const MIN_SAFE_INTEGER: f64 = -MAX_SAFE_INTEGER;
+pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;
+pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;
macro_rules! impl_int {
($($ty:ty)*) => {$(
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -22,7 +22,7 @@
function::FuncVal,
gc::WithCapacityExt as _,
manifest::{ManifestFormat, ToStringFormat},
- typed::BoundedUsize,
+ typed::{BoundedUsize, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER},
ObjValue, Result, Unbound, WeakObjValue,
};
@@ -418,6 +418,12 @@
pub const fn get(&self) -> f64 {
self.0
}
+ pub(crate) fn truncate_for_bitwise(&self) -> Result<i64> {
+ if self.0 < MIN_SAFE_INTEGER || self.0 > dbg!(MAX_SAFE_INTEGER) {
+ bail!("numberic value outside of safe integer range for bitwise operation");
+ }
+ Ok(self.0 as i64)
+ }
}
impl PartialEq for NumValue {
fn eq(&self, other: &Self) -> bool {
@@ -490,7 +496,6 @@
type Error = ConvertNumValueError;
#[inline]
fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {
- use crate::typed::conversions::{MIN_SAFE_INTEGER, MAX_SAFE_INTEGER};
let value = value as f64;
if value < MIN_SAFE_INTEGER {
return Err(ConvertNumValueError::Underflow)