difftreelog
fix enforce Val::Num finityness at type level
in: master
13 files changed
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -120,7 +120,7 @@
}
fn get_cheap(&self, index: usize) -> Option<Val> {
- self.0.get(index).map(|v| Val::Num(f64::from(*v)))
+ self.0.get(index).map(|v| Val::Num((*v).into()))
}
fn is_cheap(&self) -> bool {
true
@@ -399,7 +399,7 @@
}
fn get_cheap(&self, index: usize) -> Option<Val> {
- self.range().nth(index).map(|i| Val::Num(f64::from(i)))
+ self.range().nth(index).map(|i| Val::Num(i.into()))
}
fn is_cheap(&self) -> bool {
true
@@ -430,12 +430,12 @@
}
#[derive(Trace, Debug, Clone)]
-pub struct MappedArray<const WithIndex: bool> {
+pub struct MappedArray<const WITH_INDEX: bool> {
inner: ArrValue,
cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
mapper: FuncVal,
}
-impl<const WithIndex: bool> MappedArray<WithIndex> {
+impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {
pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
let len = inner.len();
Self {
@@ -445,14 +445,14 @@
}
}
fn evaluate(&self, index: usize, value: Val) -> Result<Val> {
- if WithIndex {
+ if WITH_INDEX {
self.mapper.evaluate_simple(&(index, value), false)
} else {
self.mapper.evaluate_simple(&(value,), false)
}
}
}
-impl<const WithIndex: bool> ArrayLike for MappedArray<WithIndex> {
+impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {
fn len(&self) -> usize {
self.cached.borrow().len()
}
@@ -493,12 +493,12 @@
}
fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
#[derive(Trace)]
- struct ArrayElement<const WithIndex: bool> {
- arr_thunk: MappedArray<WithIndex>,
+ struct ArrayElement<const WITH_INDEX: bool> {
+ arr_thunk: MappedArray<WITH_INDEX>,
index: usize,
}
- impl<const WithIndex: bool> ThunkValue for ArrayElement<WithIndex> {
+ impl<const WITH_INDEX: bool> ThunkValue for ArrayElement<WITH_INDEX> {
type Output = Val;
fn get(self: Box<Self>) -> Result<Self::Output> {
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,7 +1,5 @@
use std::{
- cmp::Ordering,
- fmt::{Debug, Display},
- path::PathBuf,
+ cmp::Ordering, convert::Infallible, fmt::{Debug, Display}, path::PathBuf
};
use jrsonnet_gcmodule::Trace;
@@ -14,6 +12,7 @@
function::{builtin::ParamDefault, CallLocation},
stdlib::format::FormatError,
typed::TypeLocError,
+ val::ConvertNumValueError,
ObjValue,
};
@@ -236,6 +235,9 @@
#[error("invalid unicode codepoint: {0}")]
InvalidUnicodeCodepointGot(u32),
+ #[error("convert num value: {0}")]
+ ConvertNumValue(#[from] ConvertNumValueError),
+
#[error("format error: {0}")]
Format(#[from] FormatError),
#[error("type error: {0}")]
@@ -259,6 +261,12 @@
}
}
+impl From<Infallible> for Error {
+ fn from(_value: Infallible) -> Self {
+ unreachable!()
+ }
+}
+
/// Single stack trace frame
#[derive(Clone, Debug, Trace)]
pub struct StackTraceElement {
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -17,7 +17,7 @@
evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
function::{CallLocation, FuncDesc, FuncVal},
typed::Typed,
- val::{CachedUnbound, IndexableVal, StrValue, Thunk, ThunkValue},
+ val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk, ThunkValue},
Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
ResultExt, State, Unbound, Val,
};
@@ -37,7 +37,7 @@
}
Some(match &*expr.0 {
Expr::Str(s) => Val::string(s.clone()),
- Expr::Num(n) => Val::Num(*n),
+ Expr::Num(n) => Val::Num(NumValue::new(*n).expect("parser will not allow non-finite values")),
Expr::Literal(LiteralType::False) => Val::Bool(false),
Expr::Literal(LiteralType::True) => Val::Bool(true),
Expr::Literal(LiteralType::Null) => Val::Null,
@@ -438,7 +438,7 @@
Literal(LiteralType::Null) => Val::Null,
Parened(e) => evaluate(ctx, e)?,
Str(v) => Val::string(v.clone()),
- Num(v) => Val::new_checked_num(*v)?,
+ Num(v) => Val::try_num(*v)?,
// I have tried to remove special behavior from super by implementing standalone-super
// expresion, but looks like this case still needs special treatment.
//
@@ -530,6 +530,7 @@
n.value_type(),
)),
(Val::Arr(v), Val::Num(n)) => {
+ let n = n.get();
if n.fract() > f64::EPSILON {
bail!(FractionalIndex)
}
@@ -553,13 +554,13 @@
.clone()
.into_flat()
.chars()
- .skip(n as usize)
+ .skip(n.get() as usize)
.take(1)
.collect::<String>()
.into();
if v.is_empty() {
let size = s.into_flat().chars().count();
- bail!(StringBoundsError(n as usize, size))
+ bail!(StringBoundsError(n.get() as usize, size))
}
StrValue::Flat(v)
}),
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -17,10 +17,10 @@
use UnaryOpType::*;
use Val::*;
Ok(match (op, b) {
- (Plus, Num(n)) => Num(*n),
- (Minus, Num(n)) => Num(-*n),
+ (Plus, Num(n)) => Val::Num(*n),
+ (Minus, Num(n)) => Val::try_num(-n.get())?,
(Not, Bool(v)) => Bool(!v),
- (BitNot, Num(n)) => Num(!(*n as i64) as f64),
+ (BitNot, Num(n)) => Val::try_num(!(n.get() as i64) as f64)?,
(op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),
})
}
@@ -40,7 +40,7 @@
(Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),
(Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),
- (Num(v1), Num(v2)) => Val::new_checked_num(v1 + v2)?,
+ (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?,
#[cfg(feature = "exp-bigint")]
(BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),
_ => bail!(BinaryOperatorDoesNotOperateOnValues(
@@ -55,10 +55,10 @@
use Val::*;
match (a, b) {
(Num(a), Num(b)) => {
- if *b == 0.0 {
+ if b.get() == 0.0 {
bail!(DivisionByZero)
}
- Ok(Num(a % b))
+ Ok(Val::try_num(a.get() % b.get())?)
}
(Str(str), vals) => {
String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)
@@ -143,39 +143,39 @@
(Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),
(a, Mod, b) => evaluate_mod_op(a, b)?,
- (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(*v2 as usize)),
+ (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(v2.get() as usize)),
// Bool X Bool
(Bool(a), And, Bool(b)) => Bool(*a && *b),
(Bool(a), Or, Bool(b)) => Bool(*a || *b),
// Num X Num
- (Num(v1), Mul, Num(v2)) => Val::new_checked_num(v1 * v2)?,
+ (Num(v1), Mul, Num(v2)) => Val::try_num(v1.get() * v2.get())?,
(Num(v1), Div, Num(v2)) => {
- if *v2 == 0.0 {
+ if v2.get() == 0.0 {
bail!(DivisionByZero)
}
- Val::new_checked_num(v1 / v2)?
+ Val::try_num(v1.get() / v2.get())?
}
- (Num(v1), Sub, Num(v2)) => Val::new_checked_num(v1 - v2)?,
+ (Num(v1), Sub, Num(v2)) => Val::try_num(v1.get() - v2.get())?,
- (Num(v1), BitAnd, Num(v2)) => Num((*v1 as i64 & *v2 as i64) as f64),
- (Num(v1), BitOr, Num(v2)) => Num((*v1 as i64 | *v2 as i64) as f64),
- (Num(v1), BitXor, Num(v2)) => Num((*v1 as i64 ^ *v2 as i64) as f64),
+ (Num(v1), BitAnd, Num(v2)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?,
+ (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?,
+ (Num(v1), BitXor, Num(v2)) => Val::try_num((v1.get() as i64 ^ v2.get() as i64) as f64)?,
(Num(v1), Lhs, Num(v2)) => {
- if *v2 < 0.0 {
+ if v2.get() < 0.0 {
bail!("shift by negative exponent")
}
- let exp = ((*v2 as i64) & 63) as u32;
- Num((*v1 as i64).wrapping_shl(exp) as f64)
+ let exp = ((v2.get() as i64) & 63) as u32;
+ Val::try_num((v1.get() as i64).wrapping_shl(exp) as f64)?
}
(Num(v1), Rhs, Num(v2)) => {
- if *v2 < 0.0 {
+ if v2.get() < 0.0 {
bail!("shift by negative exponent")
}
- let exp = ((*v2 as i64) & 63) as u32;
- Num((*v1 as i64).wrapping_shr(exp) as f64)
+ let exp = ((v2.get() as i64) & 63) as u32;
+ Val::try_num((v1.get() as i64).wrapping_shr(exp) as f64)?
}
// Bigint X Bigint
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -2,7 +2,7 @@
use jrsonnet_interner::IStr;
use serde::{
- de::Visitor,
+ de::{self, Visitor},
ser::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
SerializeTupleStruct, SerializeTupleVariant,
@@ -11,7 +11,8 @@
};
use crate::{
- arr::ArrValue, runtime_error, Error as JrError, ObjValue, ObjValueBuilder, Result, State, Val,
+ arr::ArrValue, runtime_error, val::NumValue, Error as JrError, ObjValue, ObjValueBuilder,
+ Result, State, Val,
};
impl<'de> Deserialize<'de> for Val {
@@ -37,22 +38,21 @@
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
Ok(Val::Bool(v))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
- if !v.is_finite() {
- return Err(E::custom("only finite numbers are supported"));
- }
- Ok(Val::Num(v))
+ Ok(Val::Num(NumValue::new(v).ok_or_else(|| {
+ E::custom("only finite numbers are supported")
+ })?))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
Ok(Val::string(v))
}
@@ -67,27 +67,27 @@
// }
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
- Ok(Val::Num(v as f64))
+ Ok(Val::Num(NumValue::new(v as f64).expect("no overflow")))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
- Ok(Val::Num(v as f64))
+ Ok(Val::Num(NumValue::new(v as f64).expect("no overflow")))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
Ok(Val::Arr(ArrValue::bytes(v.into())))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
Ok(Val::Null)
}
@@ -100,7 +100,7 @@
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
- E: serde::de::Error,
+ E: de::Error,
{
Ok(Val::Null)
}
@@ -114,7 +114,7 @@
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
- A: serde::de::SeqAccess<'de>,
+ A: de::SeqAccess<'de>,
{
let mut out = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
@@ -127,7 +127,7 @@
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
- A: serde::de::MapAccess<'de>,
+ A: de::MapAccess<'de>,
{
let mut out = map
.size_hint()
@@ -159,11 +159,12 @@
Self::Null => serializer.serialize_none(),
Self::Str(s) => serializer.serialize_str(&s.clone().into_flat()),
Self::Num(n) => {
+ let n = n.get();
if n.fract() == 0.0 {
- let n = *n as i64;
+ let n = n as i64;
serializer.serialize_i64(n)
} else {
- serializer.serialize_f64(*n)
+ serializer.serialize_f64(n)
}
}
#[cfg(feature = "exp-bigint")]
@@ -449,15 +450,15 @@
}
fn serialize_i8(self, v: i8) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_i16(self, v: i16) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_i32(self, v: i32) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_i64(self, v: i64) -> Result<Val> {
@@ -465,15 +466,15 @@
}
fn serialize_u8(self, v: u8) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_u16(self, v: u16) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_u32(self, v: u32) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::Num(v.into()))
}
fn serialize_u64(self, v: u64) -> Result<Val> {
@@ -481,11 +482,11 @@
}
fn serialize_f32(self, v: f32) -> Result<Val> {
- Ok(Val::Num(f64::from(v)))
+ Ok(Val::try_num(f64::from(v))?)
}
fn serialize_f64(self, v: f64) -> Result<Val> {
- Ok(Val::Num(v))
+ Ok(Val::try_num(v)?)
}
fn serialize_char(self, v: char) -> Result<Val> {
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/format.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs
@@ -604,10 +604,13 @@
}
}
ConvTypeV::Char => match value.clone() {
- Val::Num(n) => tmp_out.push(
- std::char::from_u32(n as u32)
- .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
- ),
+ Val::Num(n) => {
+ let n = n.get();
+ tmp_out.push(
+ std::char::from_u32(n as u32)
+ .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
+ )
+ }
Val::Str(s) => {
let s = s.into_flat();
if s.chars().count() != 1 {
@@ -786,6 +789,7 @@
#[cfg(test)]
pub mod test_format {
use super::*;
+ use crate::val::NumValue;
#[test]
fn parse() {
@@ -799,17 +803,21 @@
);
}
+ fn num(v: f64) -> Val {
+ Val::Num(NumValue::new(v).expect("finite"))
+ }
+
#[test]
fn octals() {
- assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010");
- assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010");
- assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10");
- assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010");
- assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10");
- assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010");
- assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 ");
- assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 ");
- assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");
+ assert_eq!(format_arr("%#o", &[num(8.0)]).unwrap(), "010");
+ assert_eq!(format_arr("%#4o", &[num(8.0)]).unwrap(), " 010");
+ assert_eq!(format_arr("%4o", &[num(8.0)]).unwrap(), " 10");
+ assert_eq!(format_arr("%04o", &[num(8.0)]).unwrap(), "0010");
+ assert_eq!(format_arr("%+4o", &[num(8.0)]).unwrap(), " +10");
+ assert_eq!(format_arr("%+04o", &[num(8.0)]).unwrap(), "+010");
+ assert_eq!(format_arr("%-4o", &[num(8.0)]).unwrap(), "10 ");
+ assert_eq!(format_arr("%+-4o", &[num(8.0)]).unwrap(), "+10 ");
+ assert_eq!(format_arr("%+-04o", &[num(8.0)]).unwrap(), "+10 ");
}
#[test]
@@ -817,7 +825,7 @@
assert_eq!(
format_arr(
"How much error budget is left looking at our %.3f%% availability gurantees?",
- &[Val::Num(4.0)]
+ &[num(4.0)]
)
.unwrap(),
"How much error budget is left looking at our 4.000% availability gurantees?"
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9 arr::{ArrValue, BytesArray},10 bail,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 typed::CheckType,13 val::{IndexableVal, StrValue, ThunkMapper},14 ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,15};1617#[derive(Trace)]18struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);19impl<K> ThunkMapper<Val> for FromUntyped<K>20where21 K: Typed + Trace,22{23 type Output = K;2425 fn map(self, from: Val) -> Result<Self::Output> {26 K::from_untyped(from)27 }28}29impl<K: Trace> Default for FromUntyped<K> {30 fn default() -> Self {31 Self(PhantomData)32 }33}3435pub trait TypedObj: Typed {36 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;37 fn parse(obj: &ObjValue) -> Result<Self>;38 fn into_object(self) -> Result<ObjValue> {39 let mut builder = ObjValueBuilder::new();40 self.serialize(&mut builder)?;41 Ok(builder.build())42 }43}4445pub trait Typed: Sized {46 const TYPE: &'static ComplexValType;47 fn into_untyped(typed: Self) -> Result<Val>;48 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {49 Thunk::from(Self::into_untyped(typed))50 }51 fn from_untyped(untyped: Val) -> Result<Self>;52 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {53 Self::from_untyped(lazy.evaluate()?)54 }5556 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`57 fn provides_lazy() -> bool {58 false59 }6061 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible62 fn wants_lazy() -> bool {63 false64 }6566 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result67 /// This method returns identity in impl Typed for Result, and should not be overriden68 #[doc(hidden)]69 fn into_result(typed: Self) -> Result<Val> {70 let value = Self::into_untyped(typed)?;71 Ok(value)72 }73}7475impl<T> Typed for Thunk<T>76where77 T: Typed + Trace + Clone,78{79 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8081 fn into_untyped(typed: Self) -> Result<Val> {82 T::into_untyped(typed.evaluate()?)83 }8485 fn from_untyped(untyped: Val) -> Result<Self> {86 Self::from_lazy_untyped(Thunk::evaluated(untyped))87 }8889 fn provides_lazy() -> bool {90 true91 }9293 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {94 #[derive(Trace)]95 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);96 impl<K> ThunkMapper<K> for IntoUntyped<K>97 where98 K: Typed + Trace,99 {100 type Output = Val;101102 fn map(self, from: K) -> Result<Self::Output> {103 K::into_untyped(from)104 }105 }106 impl<K: Trace> Default for IntoUntyped<K> {107 fn default() -> Self {108 Self(PhantomData)109 }110 }111 inner.map(<IntoUntyped<T>>::default())112 }113114 fn wants_lazy() -> bool {115 true116 }117118 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {119 Ok(inner.map(<FromUntyped<T>>::default()))120 }121}122123const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;124125macro_rules! impl_int {126 ($($ty:ty)*) => {$(127 impl Typed for $ty {128 const TYPE: &'static ComplexValType =129 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));130 fn from_untyped(value: Val) -> Result<Self> {131 <Self as Typed>::TYPE.check(&value)?;132 match value {133 Val::Num(n) => {134 #[allow(clippy::float_cmp)]135 if n.trunc() != n {136 bail!(137 "cannot convert number with fractional part to {}",138 stringify!($ty)139 )140 }141 Ok(n as Self)142 }143 _ => unreachable!(),144 }145 }146 #[allow(clippy::cast_lossless)]147 fn into_untyped(value: Self) -> Result<Val> {148 Ok(Val::Num(value as f64))149 }150 }151 )*};152}153154impl_int!(i8 u8 i16 u16 i32 u32);155156macro_rules! impl_bounded_int {157 ($($name:ident = $ty:ty)*) => {$(158 #[derive(Clone, Copy)]159 pub struct $name<const MIN: $ty, const MAX: $ty>($ty);160 impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {161 pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {162 if value >= MIN && value <= MAX {163 Some(Self(value))164 } else {165 None166 }167 }168 pub const fn value(self) -> $ty {169 self.0170 }171 }172 impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {173 type Target = $ty;174 fn deref(&self) -> &Self::Target {175 &self.0176 }177 }178179 impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {180 const TYPE: &'static ComplexValType =181 &ComplexValType::BoundedNumber(182 Some(MIN as f64),183 Some(MAX as f64),184 );185186 fn from_untyped(value: Val) -> Result<Self> {187 <Self as Typed>::TYPE.check(&value)?;188 match value {189 Val::Num(n) => {190 #[allow(clippy::float_cmp)]191 if n.trunc() != n {192 bail!(193 "cannot convert number with fractional part to {}",194 stringify!($ty)195 )196 }197 Ok(Self(n as $ty))198 }199 _ => unreachable!(),200 }201 }202203 #[allow(clippy::cast_lossless)]204 fn into_untyped(value: Self) -> Result<Val> {205 Ok(Val::Num(value.0 as f64))206 }207 }208 )*};209}210211impl_bounded_int!(212 BoundedI8 = i8213 BoundedI16 = i16214 BoundedI32 = i32215 BoundedI64 = i64216 BoundedUsize = usize217);218219impl Typed for f64 {220 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);221222 fn into_untyped(value: Self) -> Result<Val> {223 Ok(Val::Num(value))224 }225226 fn from_untyped(value: Val) -> Result<Self> {227 <Self as Typed>::TYPE.check(&value)?;228 match value {229 Val::Num(n) => Ok(n),230 _ => unreachable!(),231 }232 }233}234235pub struct PositiveF64(pub f64);236impl Typed for PositiveF64 {237 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);238239 fn into_untyped(value: Self) -> Result<Val> {240 Ok(Val::Num(value.0))241 }242243 fn from_untyped(value: Val) -> Result<Self> {244 <Self as Typed>::TYPE.check(&value)?;245 match value {246 Val::Num(n) => Ok(Self(n)),247 _ => unreachable!(),248 }249 }250}251impl Typed for usize {252 const TYPE: &'static ComplexValType =253 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));254255 fn into_untyped(value: Self) -> Result<Val> {256 if value > MAX_SAFE_INTEGER as Self {257 bail!("number is too large")258 }259 Ok(Val::Num(value as f64))260 }261262 fn from_untyped(value: Val) -> Result<Self> {263 <Self as Typed>::TYPE.check(&value)?;264 match value {265 Val::Num(n) => {266 #[allow(clippy::float_cmp)]267 if n.trunc() != n {268 bail!("cannot convert number with fractional part to usize")269 }270 Ok(n as Self)271 }272 _ => unreachable!(),273 }274 }275}276277impl Typed for IStr {278 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);279280 fn into_untyped(value: Self) -> Result<Val> {281 Ok(Val::string(value))282 }283284 fn from_untyped(value: Val) -> Result<Self> {285 <Self as Typed>::TYPE.check(&value)?;286 match value {287 Val::Str(s) => Ok(s.into_flat()),288 _ => unreachable!(),289 }290 }291}292293impl Typed for String {294 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);295296 fn into_untyped(value: Self) -> Result<Val> {297 Ok(Val::string(value))298 }299300 fn from_untyped(value: Val) -> Result<Self> {301 <Self as Typed>::TYPE.check(&value)?;302 match value {303 Val::Str(s) => Ok(s.to_string()),304 _ => unreachable!(),305 }306 }307}308309impl Typed for StrValue {310 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);311312 fn into_untyped(value: Self) -> Result<Val> {313 Ok(Val::Str(value))314 }315316 fn from_untyped(value: Val) -> Result<Self> {317 <Self as Typed>::TYPE.check(&value)?;318 match value {319 Val::Str(s) => Ok(s),320 _ => unreachable!(),321 }322 }323}324325impl Typed for char {326 const TYPE: &'static ComplexValType = &ComplexValType::Char;327328 fn into_untyped(value: Self) -> Result<Val> {329 Ok(Val::string(value))330 }331332 fn from_untyped(value: Val) -> Result<Self> {333 <Self as Typed>::TYPE.check(&value)?;334 match value {335 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),336 _ => unreachable!(),337 }338 }339}340341impl<T> Typed for Vec<T>342where343 T: Typed,344{345 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);346347 fn into_untyped(value: Self) -> Result<Val> {348 Ok(Val::Arr(349 value350 .into_iter()351 .map(T::into_untyped)352 .collect::<Result<ArrValue>>()?,353 ))354 }355356 fn from_untyped(value: Val) -> Result<Self> {357 let Val::Arr(a) = value else {358 <Self as Typed>::TYPE.check(&value)?;359 unreachable!("typecheck should fail")360 };361 a.iter()362 .enumerate()363 .map(|(i, r)| {364 r.and_then(|t| {365 T::from_untyped(t).with_description(|| format!("parsing elem <{i}>"))366 })367 })368 .collect::<Result<Self>>()369 }370}371372impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {373 const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);374375 fn into_untyped(typed: Self) -> Result<Val> {376 let mut out = ObjValueBuilder::with_capacity(typed.len());377 for (k, v) in typed {378 let Some(key) = K::into_untyped(k)?.as_str() else {379 bail!("map key should serialize to string");380 };381 let value = V::into_untyped(v)?;382 out.field(key).value(value);383 }384 Ok(Val::Obj(out.build()))385 }386387 fn from_untyped(value: Val) -> Result<Self> {388 Self::TYPE.check(&value)?;389 let obj = value.as_obj().expect("typecheck should fail");390391 let mut out = Self::new();392 if V::wants_lazy() {393 for key in obj.fields_ex(394 false,395 #[cfg(feature = "exp-preserve-order")]396 false,397 ) {398 let value = obj.get_lazy(key.clone()).expect("field exists");399 let value = V::from_lazy_untyped(value)?;400 let key = K::from_untyped(Val::Str(key.into()))?;401 let _ = out.insert(key, value);402 }403 } else {404 for (key, value) in obj.iter(405 #[cfg(feature = "exp-preserve-order")]406 false,407 ) {408 let key = K::from_untyped(Val::Str(key.into()))?;409 let value = V::from_untyped(value?)?;410 let _ = out.insert(key, value);411 }412 }413 Ok(out)414 }415}416417impl Typed for Val {418 const TYPE: &'static ComplexValType = &ComplexValType::Any;419420 fn into_untyped(typed: Self) -> Result<Val> {421 Ok(typed)422 }423 fn from_untyped(untyped: Val) -> Result<Self> {424 Ok(untyped)425 }426}427428// Hack429#[doc(hidden)]430impl<T> Typed for Result<T>431where432 T: Typed,433{434 const TYPE: &'static ComplexValType = &ComplexValType::Any;435436 fn into_untyped(_typed: Self) -> Result<Val> {437 panic!("do not use this conversion")438 }439440 fn from_untyped(_untyped: Val) -> Result<Self> {441 panic!("do not use this conversion")442 }443444 fn into_result(typed: Self) -> Result<Val> {445 typed.map(T::into_untyped)?446 }447}448449/// Specialization450impl Typed for IBytes {451 const TYPE: &'static ComplexValType =452 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));453454 fn into_untyped(value: Self) -> Result<Val> {455 Ok(Val::Arr(ArrValue::bytes(value)))456 }457458 fn from_untyped(value: Val) -> Result<Self> {459 let Val::Arr(a) = &value else {460 <Self as Typed>::TYPE.check(&value)?;461 unreachable!()462 };463 if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {464 return Ok(bytes.0.as_slice().into());465 };466 <Self as Typed>::TYPE.check(&value)?;467 // Any::downcast_ref::<ByteArray>(&a);468 let mut out = Vec::with_capacity(a.len());469 for e in a.iter() {470 let r = e?;471 out.push(u8::from_untyped(r)?);472 }473 Ok(out.as_slice().into())474 }475}476477pub struct M1;478impl Typed for M1 {479 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));480481 fn into_untyped(_: Self) -> Result<Val> {482 Ok(Val::Num(-1.0))483 }484485 fn from_untyped(value: Val) -> Result<Self> {486 <Self as Typed>::TYPE.check(&value)?;487 Ok(Self)488 }489}490491macro_rules! decl_either {492 ($($name: ident, $($id: ident)*);*) => {$(493 #[derive(Clone)]494 pub enum $name<$($id),*> {495 $($id($id)),*496 }497 impl<$($id),*> Typed for $name<$($id),*>498 where499 $($id: Typed,)*500 {501 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);502503 fn into_untyped(value: Self) -> Result<Val> {504 match value {$(505 $name::$id(v) => $id::into_untyped(v)506 ),*}507 }508509 fn from_untyped(value: Val) -> Result<Self> {510 $(511 if $id::TYPE.check(&value).is_ok() {512 $id::from_untyped(value).map(Self::$id)513 } else514 )* {515 <Self as Typed>::TYPE.check(&value)?;516 unreachable!()517 }518 }519 }520 )*}521}522decl_either!(523 Either1, A;524 Either2, A B;525 Either3, A B C;526 Either4, A B C D;527 Either5, A B C D E;528 Either6, A B C D E F;529 Either7, A B C D E F G530);531#[macro_export]532macro_rules! Either {533 ($a:ty) => {$crate::typed::Either1<$a>};534 ($a:ty, $b:ty) => {$crate::typed::Either2<$a, $b>};535 ($a:ty, $b:ty, $c:ty) => {$crate::typed::Either3<$a, $b, $c>};536 ($a:ty, $b:ty, $c:ty, $d:ty) => {$crate::typed::Either4<$a, $b, $c, $d>};537 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {$crate::typed::Either5<$a, $b, $c, $d, $e>};538 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {$crate::typed::Either6<$a, $b, $c, $d, $e, $f>};539 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {$crate::typed::Either7<$a, $b, $c, $d, $e, $f, $g>};540}541pub use Either;542543pub type MyType = Either![u32, f64, String];544545impl Typed for ArrValue {546 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);547548 fn into_untyped(value: Self) -> Result<Val> {549 Ok(Val::Arr(value))550 }551552 fn from_untyped(value: Val) -> Result<Self> {553 <Self as Typed>::TYPE.check(&value)?;554 match value {555 Val::Arr(a) => Ok(a),556 _ => unreachable!(),557 }558 }559}560561impl Typed for FuncVal {562 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);563564 fn into_untyped(value: Self) -> Result<Val> {565 Ok(Val::Func(value))566 }567568 fn from_untyped(value: Val) -> Result<Self> {569 <Self as Typed>::TYPE.check(&value)?;570 match value {571 Val::Func(a) => Ok(a),572 _ => unreachable!(),573 }574 }575}576577impl Typed for Cc<FuncDesc> {578 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);579580 fn into_untyped(value: Self) -> Result<Val> {581 Ok(Val::Func(FuncVal::Normal(value)))582 }583584 fn from_untyped(value: Val) -> Result<Self> {585 <Self as Typed>::TYPE.check(&value)?;586 match value {587 Val::Func(FuncVal::Normal(desc)) => Ok(desc),588 Val::Func(_) => bail!("expected normal function, not builtin"),589 _ => unreachable!(),590 }591 }592}593594impl Typed for ObjValue {595 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);596597 fn into_untyped(value: Self) -> Result<Val> {598 Ok(Val::Obj(value))599 }600601 fn from_untyped(value: Val) -> Result<Self> {602 <Self as Typed>::TYPE.check(&value)?;603 match value {604 Val::Obj(a) => Ok(a),605 _ => unreachable!(),606 }607 }608}609610impl Typed for bool {611 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);612613 fn into_untyped(value: Self) -> Result<Val> {614 Ok(Val::Bool(value))615 }616617 fn from_untyped(value: Val) -> Result<Self> {618 <Self as Typed>::TYPE.check(&value)?;619 match value {620 Val::Bool(a) => Ok(a),621 _ => unreachable!(),622 }623 }624}625impl Typed for IndexableVal {626 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[627 &ComplexValType::Simple(ValType::Arr),628 &ComplexValType::Simple(ValType::Str),629 ]);630631 fn into_untyped(value: Self) -> Result<Val> {632 match value {633 Self::Str(s) => Ok(Val::string(s)),634 Self::Arr(a) => Ok(Val::Arr(a)),635 }636 }637638 fn from_untyped(value: Val) -> Result<Self> {639 <Self as Typed>::TYPE.check(&value)?;640 value.into_indexable()641 }642}643644pub struct Null;645impl Typed for Null {646 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);647648 fn into_untyped(_: Self) -> Result<Val> {649 Ok(Val::Null)650 }651652 fn from_untyped(value: Val) -> Result<Self> {653 <Self as Typed>::TYPE.check(&value)?;654 Ok(Self)655 }656}657658pub struct NativeFn<D: NativeDesc>(D::Value);659impl<D: NativeDesc> Deref for NativeFn<D> {660 type Target = D::Value;661662 fn deref(&self) -> &Self::Target {663 &self.0664 }665}666impl<D: NativeDesc> Typed for NativeFn<D> {667 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);668669 fn into_untyped(_typed: Self) -> Result<Val> {670 bail!("can only convert functions from jsonnet to native")671 }672673 fn from_untyped(untyped: Val) -> Result<Self> {674 Ok(Self(675 untyped676 .as_func()677 .expect("shape is checked")678 .into_native::<D>(),679 ))680 }681}crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -1,6 +1,6 @@
use std::{fmt::Display, rc::Rc};
-mod conversions;
+pub(crate) mod conversions;
pub use conversions::*;
use jrsonnet_gcmodule::Trace;
pub use jrsonnet_types::{ComplexValType, ValType};
@@ -155,10 +155,11 @@
},
Self::BoundedNumber(from, to) => {
if let Val::Num(n) = value {
- if from.map(|from| from > *n).unwrap_or(false)
- || to.map(|to| to < *n).unwrap_or(false)
+ let n = n.get();
+ if from.map(|from| from > n).unwrap_or(false)
+ || to.map(|to| to < n).unwrap_or(false)
{
- return Err(TypeError::BoundsFailed(*n, *from, *to).into());
+ return Err(TypeError::BoundsFailed(n, *from, *to).into());
}
Ok(())
} else {
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -1,14 +1,18 @@
use std::{
cell::RefCell,
+ cmp::Ordering,
fmt::{self, Debug, Display},
mem::replace,
num::NonZeroU32,
+ ops::Deref,
rc::Rc,
};
+use derivative::Derivative;
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
+use thiserror::Error;
pub use crate::arr::{ArrValue, ArrayLike};
use crate::{
@@ -379,18 +383,127 @@
}
impl Eq for StrValue {}
impl PartialOrd for StrValue {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for StrValue {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ fn cmp(&self, other: &Self) -> Ordering {
let a = self.clone().into_flat();
let b = other.clone().into_flat();
a.cmp(&b)
}
}
+/// Represents jsonnet number
+/// Jsonnet numbers are finite f64, with NaNs disallowed
+#[derive(Trace, Clone, Copy, Derivative)]
+#[derivative(Debug = "transparent")]
+#[repr(transparent)]
+pub struct NumValue(f64);
+impl NumValue {
+ /// Creates a [`NumValue`], if value is finite and not NaN
+ pub fn new(v: f64) -> Option<Self> {
+ if !v.is_finite() {
+ return None;
+ }
+ Some(Self(v))
+ }
+ pub const fn get(&self) -> f64 {
+ self.0
+ }
+}
+impl PartialEq for NumValue {
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+}
+impl Eq for NumValue {}
+impl Ord for NumValue {
+ fn cmp(&self, other: &Self) -> Ordering {
+ // Can't use `total_cmp`: its behavior for `-0` and `0`
+ // is not following wanted.
+ self.0.partial_cmp(&other.0).expect("NaNs are disallowed")
+ }
+}
+impl PartialOrd for NumValue {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl Display for NumValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+impl Deref for NumValue {
+ type Target = f64;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+macro_rules! impl_num {
+ ($($ty:ty),+) => {$(
+ impl From<$ty> for NumValue {
+ fn from(value: $ty) -> Self {
+ Self(value.into())
+ }
+ }
+ )+};
+}
+impl_num!(i8, u8, i16, u16, i32, u32);
+
+#[derive(Clone, Copy, Debug, Error, Trace)]
+pub enum ConvertNumValueError {
+ #[error("overflow")]
+ Overflow,
+ #[error("underflow")]
+ Underflow,
+ #[error("non-finite")]
+ NonFinite,
+}
+impl From<ConvertNumValueError> for Error {
+ fn from(e: ConvertNumValueError) -> Self {
+ Self::new(e.into())
+ }
+}
+
+macro_rules! impl_try_num {
+ ($($ty:ty),+) => {$(
+ impl TryFrom<$ty> for NumValue {
+ type Error = ConvertNumValueError;
+ 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)
+ } else if value > MAX_SAFE_INTEGER {
+ return Err(ConvertNumValueError::Overflow)
+ }
+ // Number is finite.
+ Ok(Self(value))
+ }
+ }
+ )+};
+}
+impl_try_num!(usize, isize, i64, u64);
+
+impl TryFrom<f64> for NumValue {
+ type Error = ConvertNumValueError;
+
+ fn try_from(value: f64) -> Result<Self, Self::Error> {
+ Self::new(value).ok_or(ConvertNumValueError::NonFinite)
+ }
+}
+impl TryFrom<f32> for NumValue {
+ type Error = ConvertNumValueError;
+
+ fn try_from(value: f32) -> Result<Self, Self::Error> {
+ Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)
+ }
+}
+
/// Represents any valid Jsonnet value.
#[derive(Debug, Clone, Trace, Default)]
pub enum Val {
@@ -404,7 +517,7 @@
/// Represents a Jsonnet number.
/// Should be finite, and not NaN
/// This restriction isn't enforced by enum, as enum field can't be marked as private
- Num(f64),
+ Num(NumValue),
/// Experimental bigint
#[cfg(feature = "exp-bigint")]
BigInt(#[trace(skip)] Box<num_bigint::BigInt>),
@@ -449,7 +562,7 @@
}
pub const fn as_num(&self) -> Option<f64> {
match self {
- Self::Num(n) => Some(*n),
+ Self::Num(n) => Some(n.get()),
_ => None,
}
}
@@ -472,16 +585,6 @@
}
}
- /// Creates `Val::Num` after checking for numeric overflow.
- /// As numbers are `f64`, we can just check for their finity.
- pub fn new_checked_num(num: f64) -> Result<Self> {
- if num.is_finite() {
- Ok(Self::Num(num))
- } else {
- bail!("overflow")
- }
- }
-
pub const fn value_type(&self) -> ValType {
match self {
Self::Str(..) => ValType::Str,
@@ -527,6 +630,15 @@
pub fn string(string: impl Into<StrValue>) -> Self {
Self::Str(string.into())
}
+ pub fn num(num: impl Into<NumValue>) -> Self {
+ Self::Num(num.into())
+ }
+ pub fn try_num<V, E>(num: V) -> Result<Self, E>
+ where
+ NumValue: TryFrom<V, Error = E>,
+ {
+ Ok(Self::Num(num.try_into()?))
+ }
}
impl From<IStr> for Val {
@@ -560,7 +672,7 @@
(Val::Bool(a), Val::Bool(b)) => a == b,
(Val::Null, Val::Null) => true,
(Val::Str(a), Val::Str(b)) => a == b,
- (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,
+ (Val::Num(a), Val::Num(b)) => (a.get() - b.get()).abs() <= f64::EPSILON,
#[cfg(feature = "exp-bigint")]
(Val::BigInt(a), Val::BigInt(b)) => a == b,
(Val::Arr(_), Val::Arr(_)) => {
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -275,7 +275,7 @@
if arr.is_empty() {
return eval_on_empty(onEmpty);
}
- Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))
+ Ok(Val::try_num(arr.iter().sum::<f64>() / (arr.len() as f64))?)
}
#[builtin]
crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -6,12 +6,12 @@
operator::evaluate_mod_op,
stdlib::std_format,
typed::{Either, Either2},
- val::{equals, primitive_equals},
+ val::{equals, primitive_equals, NumValue},
IStr, Result, Val,
};
#[builtin]
-pub fn builtin_mod(a: Either![f64, IStr], b: Val) -> Result<Val> {
+pub fn builtin_mod(a: Either![NumValue, IStr], b: Val) -> Result<Val> {
use Either2::*;
evaluate_mod_op(
&match a {
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -20,20 +20,6 @@
Unknown,
}
-#[derive(PartialEq)]
-struct NonNaNf64(f64);
-impl PartialOrd for NonNaNf64 {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(other))
- }
-}
-impl Eq for NonNaNf64 {}
-impl Ord for NonNaNf64 {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.0.partial_cmp(&other.0).expect("non nan")
- }
-}
-
fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {
let mut sort_type = SortKeyType::Unknown;
for i in values {
@@ -56,7 +42,7 @@
let sort_type = get_sort_type(&values, |k| k)?;
match sort_type {
SortKeyType::Number => values.sort_unstable_by_key(|v| match v {
- Val::Num(n) => NonNaNf64(*n),
+ Val::Num(n) => *n,
_ => unreachable!(),
}),
SortKeyType::String => values.sort_unstable_by_key(|v| match v {
@@ -95,7 +81,7 @@
let sort_type = get_sort_type(&vk, |v| &v.1)?;
match sort_type {
SortKeyType::Number => vk.sort_by_key(|v| match v.1 {
- Val::Num(n) => NonNaNf64(n),
+ Val::Num(n) => n,
_ => unreachable!(),
}),
SortKeyType::String => vk.sort_by_key(|v| match &v.1 {
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -116,7 +116,9 @@
.enumerate()
{
if &strb[i..i + pat.len()] == pat {
- out.push(Val::Num(ch_idx as f64));
+ out.push(Val::Num(
+ ch_idx.try_into().expect("unrealisticly long string"),
+ ));
}
}
out.into()