git.delta.rocks / jrsonnet / refs/commits / 0e2224254bcf

difftreelog

fix enforce Val::Num finityness at type level

Yaroslav Bolyukin2024-05-19parent: #88907d8.patch.diff
in: master

13 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
120 }120 }
121121
122 fn get_cheap(&self, index: usize) -> Option<Val> {122 fn get_cheap(&self, index: usize) -> Option<Val> {
123 self.0.get(index).map(|v| Val::Num(f64::from(*v)))123 self.0.get(index).map(|v| Val::Num((*v).into()))
124 }124 }
125 fn is_cheap(&self) -> bool {125 fn is_cheap(&self) -> bool {
126 true126 true
399 }399 }
400400
401 fn get_cheap(&self, index: usize) -> Option<Val> {401 fn get_cheap(&self, index: usize) -> Option<Val> {
402 self.range().nth(index).map(|i| Val::Num(f64::from(i)))402 self.range().nth(index).map(|i| Val::Num(i.into()))
403 }403 }
404 fn is_cheap(&self) -> bool {404 fn is_cheap(&self) -> bool {
405 true405 true
430}430}
431431
432#[derive(Trace, Debug, Clone)]432#[derive(Trace, Debug, Clone)]
433pub struct MappedArray<const WithIndex: bool> {433pub struct MappedArray<const WITH_INDEX: bool> {
434 inner: ArrValue,434 inner: ArrValue,
435 cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,435 cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
436 mapper: FuncVal,436 mapper: FuncVal,
437}437}
438impl<const WithIndex: bool> MappedArray<WithIndex> {438impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {
439 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {439 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
440 let len = inner.len();440 let len = inner.len();
441 Self {441 Self {
445 }445 }
446 }446 }
447 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {447 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {
448 if WithIndex {448 if WITH_INDEX {
449 self.mapper.evaluate_simple(&(index, value), false)449 self.mapper.evaluate_simple(&(index, value), false)
450 } else {450 } else {
451 self.mapper.evaluate_simple(&(value,), false)451 self.mapper.evaluate_simple(&(value,), false)
452 }452 }
453 }453 }
454}454}
455impl<const WithIndex: bool> ArrayLike for MappedArray<WithIndex> {455impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {
456 fn len(&self) -> usize {456 fn len(&self) -> usize {
457 self.cached.borrow().len()457 self.cached.borrow().len()
458 }458 }
493 }493 }
494 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {494 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
495 #[derive(Trace)]495 #[derive(Trace)]
496 struct ArrayElement<const WithIndex: bool> {496 struct ArrayElement<const WITH_INDEX: bool> {
497 arr_thunk: MappedArray<WithIndex>,497 arr_thunk: MappedArray<WITH_INDEX>,
498 index: usize,498 index: usize,
499 }499 }
500500
501 impl<const WithIndex: bool> ThunkValue for ArrayElement<WithIndex> {501 impl<const WITH_INDEX: bool> ThunkValue for ArrayElement<WITH_INDEX> {
502 type Output = Val;502 type Output = Val;
503503
504 fn get(self: Box<Self>) -> Result<Self::Output> {504 fn get(self: Box<Self>) -> Result<Self::Output> {
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
1use std::{1use std::{
2 cmp::Ordering,2 cmp::Ordering, convert::Infallible, fmt::{Debug, Display}, path::PathBuf
3 fmt::{Debug, Display},
4 path::PathBuf,
5};3};
64
7use jrsonnet_gcmodule::Trace;5use jrsonnet_gcmodule::Trace;
14 function::{builtin::ParamDefault, CallLocation},12 function::{builtin::ParamDefault, CallLocation},
15 stdlib::format::FormatError,13 stdlib::format::FormatError,
16 typed::TypeLocError,14 typed::TypeLocError,
15 val::ConvertNumValueError,
17 ObjValue,16 ObjValue,
18};17};
1918
236 #[error("invalid unicode codepoint: {0}")]235 #[error("invalid unicode codepoint: {0}")]
237 InvalidUnicodeCodepointGot(u32),236 InvalidUnicodeCodepointGot(u32),
237
238 #[error("convert num value: {0}")]
239 ConvertNumValue(#[from] ConvertNumValueError),
238240
239 #[error("format error: {0}")]241 #[error("format error: {0}")]
240 Format(#[from] FormatError),242 Format(#[from] FormatError),
259 }261 }
260}262}
263
264impl From<Infallible> for Error {
265 fn from(_value: Infallible) -> Self {
266 unreachable!()
267 }
268}
261269
262/// Single stack trace frame270/// Single stack trace frame
263#[derive(Clone, Debug, Trace)]271#[derive(Clone, Debug, Trace)]
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
17 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},17 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
18 function::{CallLocation, FuncDesc, FuncVal},18 function::{CallLocation, FuncDesc, FuncVal},
19 typed::Typed,19 typed::Typed,
20 val::{CachedUnbound, IndexableVal, StrValue, Thunk, ThunkValue},20 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk, ThunkValue},
21 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,21 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
22 ResultExt, State, Unbound, Val,22 ResultExt, State, Unbound, Val,
23};23};
37 }37 }
38 Some(match &*expr.0 {38 Some(match &*expr.0 {
39 Expr::Str(s) => Val::string(s.clone()),39 Expr::Str(s) => Val::string(s.clone()),
40 Expr::Num(n) => Val::Num(*n),40 Expr::Num(n) => Val::Num(NumValue::new(*n).expect("parser will not allow non-finite values")),
41 Expr::Literal(LiteralType::False) => Val::Bool(false),41 Expr::Literal(LiteralType::False) => Val::Bool(false),
42 Expr::Literal(LiteralType::True) => Val::Bool(true),42 Expr::Literal(LiteralType::True) => Val::Bool(true),
43 Expr::Literal(LiteralType::Null) => Val::Null,43 Expr::Literal(LiteralType::Null) => Val::Null,
438 Literal(LiteralType::Null) => Val::Null,438 Literal(LiteralType::Null) => Val::Null,
439 Parened(e) => evaluate(ctx, e)?,439 Parened(e) => evaluate(ctx, e)?,
440 Str(v) => Val::string(v.clone()),440 Str(v) => Val::string(v.clone()),
441 Num(v) => Val::new_checked_num(*v)?,441 Num(v) => Val::try_num(*v)?,
442 // I have tried to remove special behavior from super by implementing standalone-super442 // I have tried to remove special behavior from super by implementing standalone-super
443 // expresion, but looks like this case still needs special treatment.443 // expresion, but looks like this case still needs special treatment.
444 //444 //
530 n.value_type(),530 n.value_type(),
531 )),531 )),
532 (Val::Arr(v), Val::Num(n)) => {532 (Val::Arr(v), Val::Num(n)) => {
533 let n = n.get();
533 if n.fract() > f64::EPSILON {534 if n.fract() > f64::EPSILON {
534 bail!(FractionalIndex)535 bail!(FractionalIndex)
535 }536 }
553 .clone()554 .clone()
554 .into_flat()555 .into_flat()
555 .chars()556 .chars()
556 .skip(n as usize)557 .skip(n.get() as usize)
557 .take(1)558 .take(1)
558 .collect::<String>()559 .collect::<String>()
559 .into();560 .into();
560 if v.is_empty() {561 if v.is_empty() {
561 let size = s.into_flat().chars().count();562 let size = s.into_flat().chars().count();
562 bail!(StringBoundsError(n as usize, size))563 bail!(StringBoundsError(n.get() as usize, size))
563 }564 }
564 StrValue::Flat(v)565 StrValue::Flat(v)
565 }),566 }),
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
17 use UnaryOpType::*;17 use UnaryOpType::*;
18 use Val::*;18 use Val::*;
19 Ok(match (op, b) {19 Ok(match (op, b) {
20 (Plus, Num(n)) => Num(*n),20 (Plus, Num(n)) => Val::Num(*n),
21 (Minus, Num(n)) => Num(-*n),21 (Minus, Num(n)) => Val::try_num(-n.get())?,
22 (Not, Bool(v)) => Bool(!v),22 (Not, Bool(v)) => Bool(!v),
23 (BitNot, Num(n)) => Num(!(*n as i64) as f64),23 (BitNot, Num(n)) => Val::try_num(!(n.get() as i64) as f64)?,
24 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),24 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),
25 })25 })
26}26}
40 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),40 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),
41 (Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),41 (Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),
4242
43 (Num(v1), Num(v2)) => Val::new_checked_num(v1 + v2)?,43 (Num(v1), Num(v2)) => Val::try_num(v1.get() + v2.get())?,
44 #[cfg(feature = "exp-bigint")]44 #[cfg(feature = "exp-bigint")]
45 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),45 (BigInt(a), BigInt(b)) => BigInt(Box::new(&**a + &**b)),
46 _ => bail!(BinaryOperatorDoesNotOperateOnValues(46 _ => bail!(BinaryOperatorDoesNotOperateOnValues(
55 use Val::*;55 use Val::*;
56 match (a, b) {56 match (a, b) {
57 (Num(a), Num(b)) => {57 (Num(a), Num(b)) => {
58 if *b == 0.0 {58 if b.get() == 0.0 {
59 bail!(DivisionByZero)59 bail!(DivisionByZero)
60 }60 }
61 Ok(Num(a % b))61 Ok(Val::try_num(a.get() % b.get())?)
62 }62 }
63 (Str(str), vals) => {63 (Str(str), vals) => {
64 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)64 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)
143 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),143 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone().into_flat(), true)),
144 (a, Mod, b) => evaluate_mod_op(a, b)?,144 (a, Mod, b) => evaluate_mod_op(a, b)?,
145145
146 (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(*v2 as usize)),146 (Str(v1), Mul, Num(v2)) => Val::string(v1.to_string().repeat(v2.get() as usize)),
147147
148 // Bool X Bool148 // Bool X Bool
149 (Bool(a), And, Bool(b)) => Bool(*a && *b),149 (Bool(a), And, Bool(b)) => Bool(*a && *b),
150 (Bool(a), Or, Bool(b)) => Bool(*a || *b),150 (Bool(a), Or, Bool(b)) => Bool(*a || *b),
151151
152 // Num X Num152 // Num X Num
153 (Num(v1), Mul, Num(v2)) => Val::new_checked_num(v1 * v2)?,153 (Num(v1), Mul, Num(v2)) => Val::try_num(v1.get() * v2.get())?,
154 (Num(v1), Div, Num(v2)) => {154 (Num(v1), Div, Num(v2)) => {
155 if *v2 == 0.0 {155 if v2.get() == 0.0 {
156 bail!(DivisionByZero)156 bail!(DivisionByZero)
157 }157 }
158 Val::new_checked_num(v1 / v2)?158 Val::try_num(v1.get() / v2.get())?
159 }159 }
160160
161 (Num(v1), Sub, Num(v2)) => Val::new_checked_num(v1 - v2)?,161 (Num(v1), Sub, Num(v2)) => Val::try_num(v1.get() - v2.get())?,
162162
163 (Num(v1), BitAnd, Num(v2)) => Num((*v1 as i64 & *v2 as i64) as f64),163 (Num(v1), BitAnd, Num(v2)) => Val::try_num((v1.get() as i64 & v2.get() as i64) as f64)?,
164 (Num(v1), BitOr, Num(v2)) => Num((*v1 as i64 | *v2 as i64) as f64),164 (Num(v1), BitOr, Num(v2)) => Val::try_num((v1.get() as i64 | v2.get() as i64) as f64)?,
165 (Num(v1), BitXor, Num(v2)) => Num((*v1 as i64 ^ *v2 as i64) as f64),165 (Num(v1), BitXor, Num(v2)) => Val::try_num((v1.get() as i64 ^ v2.get() as i64) as f64)?,
166 (Num(v1), Lhs, Num(v2)) => {166 (Num(v1), Lhs, Num(v2)) => {
167 if *v2 < 0.0 {167 if v2.get() < 0.0 {
168 bail!("shift by negative exponent")168 bail!("shift by negative exponent")
169 }169 }
170 let exp = ((*v2 as i64) & 63) as u32;170 let exp = ((v2.get() as i64) & 63) as u32;
171 Num((*v1 as i64).wrapping_shl(exp) as f64)171 Val::try_num((v1.get() as i64).wrapping_shl(exp) as f64)?
172 }172 }
173 (Num(v1), Rhs, Num(v2)) => {173 (Num(v1), Rhs, Num(v2)) => {
174 if *v2 < 0.0 {174 if v2.get() < 0.0 {
175 bail!("shift by negative exponent")175 bail!("shift by negative exponent")
176 }176 }
177 let exp = ((*v2 as i64) & 63) as u32;177 let exp = ((v2.get() as i64) & 63) as u32;
178 Num((*v1 as i64).wrapping_shr(exp) as f64)178 Val::try_num((v1.get() as i64).wrapping_shr(exp) as f64)?
179 }179 }
180180
181 // Bigint X Bigint181 // Bigint X Bigint
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
22
3use jrsonnet_interner::IStr;3use jrsonnet_interner::IStr;
4use serde::{4use serde::{
5 de::Visitor,5 de::{self, Visitor},
6 ser::{6 ser::{
7 Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,7 Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
8 SerializeTupleStruct, SerializeTupleVariant,8 SerializeTupleStruct, SerializeTupleVariant,
11};11};
1212
13use crate::{13use crate::{
14 arr::ArrValue, runtime_error, Error as JrError, ObjValue, ObjValueBuilder, Result, State, Val,14 arr::ArrValue, runtime_error, val::NumValue, Error as JrError, ObjValue, ObjValueBuilder,
15 Result, State, Val,
15};16};
1617
3738
38 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>39 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
39 where40 where
40 E: serde::de::Error,41 E: de::Error,
41 {42 {
42 Ok(Val::Bool(v))43 Ok(Val::Bool(v))
43 }44 }
44 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>45 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
45 where46 where
46 E: serde::de::Error,47 E: de::Error,
47 {48 {
48 if !v.is_finite() {
49 return Err(E::custom("only finite numbers are supported"));49 Ok(Val::Num(NumValue::new(v).ok_or_else(|| {
50 }50 E::custom("only finite numbers are supported")
51 Ok(Val::Num(v))51 })?))
52 }52 }
53 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>53 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
54 where54 where
55 E: serde::de::Error,55 E: de::Error,
56 {56 {
57 Ok(Val::string(v))57 Ok(Val::string(v))
58 }58 }
67 // }67 // }
68 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>68 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
69 where69 where
70 E: serde::de::Error,70 E: de::Error,
71 {71 {
72 Ok(Val::Num(v as f64))72 Ok(Val::Num(NumValue::new(v as f64).expect("no overflow")))
73 }73 }
74 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>74 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
75 where75 where
76 E: serde::de::Error,76 E: de::Error,
77 {77 {
78 Ok(Val::Num(v as f64))78 Ok(Val::Num(NumValue::new(v as f64).expect("no overflow")))
79 }79 }
8080
81 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>81 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
82 where82 where
83 E: serde::de::Error,83 E: de::Error,
84 {84 {
85 Ok(Val::Arr(ArrValue::bytes(v.into())))85 Ok(Val::Arr(ArrValue::bytes(v.into())))
86 }86 }
8787
88 fn visit_none<E>(self) -> Result<Self::Value, E>88 fn visit_none<E>(self) -> Result<Self::Value, E>
89 where89 where
90 E: serde::de::Error,90 E: de::Error,
91 {91 {
92 Ok(Val::Null)92 Ok(Val::Null)
93 }93 }
100100
101 fn visit_unit<E>(self) -> Result<Self::Value, E>101 fn visit_unit<E>(self) -> Result<Self::Value, E>
102 where102 where
103 E: serde::de::Error,103 E: de::Error,
104 {104 {
105 Ok(Val::Null)105 Ok(Val::Null)
106 }106 }
114114
115 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>115 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
116 where116 where
117 A: serde::de::SeqAccess<'de>,117 A: de::SeqAccess<'de>,
118 {118 {
119 let mut out = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);119 let mut out = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
120120
127127
128 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>128 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
129 where129 where
130 A: serde::de::MapAccess<'de>,130 A: de::MapAccess<'de>,
131 {131 {
132 let mut out = map132 let mut out = map
133 .size_hint()133 .size_hint()
159 Self::Null => serializer.serialize_none(),159 Self::Null => serializer.serialize_none(),
160 Self::Str(s) => serializer.serialize_str(&s.clone().into_flat()),160 Self::Str(s) => serializer.serialize_str(&s.clone().into_flat()),
161 Self::Num(n) => {161 Self::Num(n) => {
162 let n = n.get();
162 if n.fract() == 0.0 {163 if n.fract() == 0.0 {
163 let n = *n as i64;164 let n = n as i64;
164 serializer.serialize_i64(n)165 serializer.serialize_i64(n)
165 } else {166 } else {
166 serializer.serialize_f64(*n)167 serializer.serialize_f64(n)
167 }168 }
168 }169 }
169 #[cfg(feature = "exp-bigint")]170 #[cfg(feature = "exp-bigint")]
449 }450 }
450451
451 fn serialize_i8(self, v: i8) -> Result<Val> {452 fn serialize_i8(self, v: i8) -> Result<Val> {
452 Ok(Val::Num(f64::from(v)))453 Ok(Val::Num(v.into()))
453 }454 }
454455
455 fn serialize_i16(self, v: i16) -> Result<Val> {456 fn serialize_i16(self, v: i16) -> Result<Val> {
456 Ok(Val::Num(f64::from(v)))457 Ok(Val::Num(v.into()))
457 }458 }
458459
459 fn serialize_i32(self, v: i32) -> Result<Val> {460 fn serialize_i32(self, v: i32) -> Result<Val> {
460 Ok(Val::Num(f64::from(v)))461 Ok(Val::Num(v.into()))
461 }462 }
462463
463 fn serialize_i64(self, v: i64) -> Result<Val> {464 fn serialize_i64(self, v: i64) -> Result<Val> {
464 Ok(Val::Str(v.to_string().into()))465 Ok(Val::Str(v.to_string().into()))
465 }466 }
466467
467 fn serialize_u8(self, v: u8) -> Result<Val> {468 fn serialize_u8(self, v: u8) -> Result<Val> {
468 Ok(Val::Num(f64::from(v)))469 Ok(Val::Num(v.into()))
469 }470 }
470471
471 fn serialize_u16(self, v: u16) -> Result<Val> {472 fn serialize_u16(self, v: u16) -> Result<Val> {
472 Ok(Val::Num(f64::from(v)))473 Ok(Val::Num(v.into()))
473 }474 }
474475
475 fn serialize_u32(self, v: u32) -> Result<Val> {476 fn serialize_u32(self, v: u32) -> Result<Val> {
476 Ok(Val::Num(f64::from(v)))477 Ok(Val::Num(v.into()))
477 }478 }
478479
479 fn serialize_u64(self, v: u64) -> Result<Val> {480 fn serialize_u64(self, v: u64) -> Result<Val> {
480 Ok(Val::Str(v.to_string().into()))481 Ok(Val::Str(v.to_string().into()))
481 }482 }
482483
483 fn serialize_f32(self, v: f32) -> Result<Val> {484 fn serialize_f32(self, v: f32) -> Result<Val> {
484 Ok(Val::Num(f64::from(v)))485 Ok(Val::try_num(f64::from(v))?)
485 }486 }
486487
487 fn serialize_f64(self, v: f64) -> Result<Val> {488 fn serialize_f64(self, v: f64) -> Result<Val> {
488 Ok(Val::Num(v))489 Ok(Val::try_num(v)?)
489 }490 }
490491
491 fn serialize_char(self, v: char) -> Result<Val> {492 fn serialize_char(self, v: char) -> Result<Val> {
modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
604 }604 }
605 }605 }
606 ConvTypeV::Char => match value.clone() {606 ConvTypeV::Char => match value.clone() {
607 Val::Num(n) => tmp_out.push(607 Val::Num(n) => {
608 let n = n.get();
609 tmp_out.push(
608 std::char::from_u32(n as u32)610 std::char::from_u32(n as u32)
609 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,611 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
610 ),612 )
613 }
611 Val::Str(s) => {614 Val::Str(s) => {
612 let s = s.into_flat();615 let s = s.into_flat();
613 if s.chars().count() != 1 {616 if s.chars().count() != 1 {
786#[cfg(test)]789#[cfg(test)]
787pub mod test_format {790pub mod test_format {
788 use super::*;791 use super::*;
792 use crate::val::NumValue;
789793
790 #[test]794 #[test]
791 fn parse() {795 fn parse() {
799 );803 );
800 }804 }
805
806 fn num(v: f64) -> Val {
807 Val::Num(NumValue::new(v).expect("finite"))
808 }
801809
802 #[test]810 #[test]
803 fn octals() {811 fn octals() {
804 assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010");812 assert_eq!(format_arr("%#o", &[num(8.0)]).unwrap(), "010");
805 assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010");813 assert_eq!(format_arr("%#4o", &[num(8.0)]).unwrap(), " 010");
806 assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10");814 assert_eq!(format_arr("%4o", &[num(8.0)]).unwrap(), " 10");
807 assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010");815 assert_eq!(format_arr("%04o", &[num(8.0)]).unwrap(), "0010");
808 assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10");816 assert_eq!(format_arr("%+4o", &[num(8.0)]).unwrap(), " +10");
809 assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010");817 assert_eq!(format_arr("%+04o", &[num(8.0)]).unwrap(), "+010");
810 assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 ");818 assert_eq!(format_arr("%-4o", &[num(8.0)]).unwrap(), "10 ");
811 assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 ");819 assert_eq!(format_arr("%+-4o", &[num(8.0)]).unwrap(), "+10 ");
812 assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");820 assert_eq!(format_arr("%+-04o", &[num(8.0)]).unwrap(), "+10 ");
813 }821 }
814822
815 #[test]823 #[test]
816 fn percent_doesnt_consumes_values() {824 fn percent_doesnt_consumes_values() {
817 assert_eq!(825 assert_eq!(
818 format_arr(826 format_arr(
819 "How much error budget is left looking at our %.3f%% availability gurantees?",827 "How much error budget is left looking at our %.3f%% availability gurantees?",
820 &[Val::Num(4.0)]828 &[num(4.0)]
821 )829 )
822 .unwrap(),830 .unwrap(),
823 "How much error budget is left looking at our 4.000% availability gurantees?"831 "How much error budget is left looking at our 4.000% availability gurantees?"
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
10 bail,10 bail,
11 function::{native::NativeDesc, FuncDesc, FuncVal},11 function::{native::NativeDesc, FuncDesc, FuncVal},
12 typed::CheckType,12 typed::CheckType,
13 val::{IndexableVal, StrValue, ThunkMapper},13 val::{IndexableVal, NumValue, StrValue, ThunkMapper},
14 ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,14 ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
15};15};
1616
120 }120 }
121}121}
122122
123const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;123pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;
124pub const MIN_SAFE_INTEGER: f64 = -MAX_SAFE_INTEGER;
124125
125macro_rules! impl_int {126macro_rules! impl_int {
126 ($($ty:ty)*) => {$(127 ($($ty:ty)*) => {$(
131 <Self as Typed>::TYPE.check(&value)?;132 <Self as Typed>::TYPE.check(&value)?;
132 match value {133 match value {
133 Val::Num(n) => {134 Val::Num(n) => {
135 let n = n.get();
134 #[allow(clippy::float_cmp)]136 #[allow(clippy::float_cmp)]
135 if n.trunc() != n {137 if n.trunc() != n {
136 bail!(138 bail!(
143 _ => unreachable!(),145 _ => unreachable!(),
144 }146 }
145 }147 }
146 #[allow(clippy::cast_lossless)]
147 fn into_untyped(value: Self) -> Result<Val> {148 fn into_untyped(value: Self) -> Result<Val> {
148 Ok(Val::Num(value as f64))149 Ok(Val::Num(value.into()))
149 }150 }
150 }151 }
151 )*};152 )*};
187 <Self as Typed>::TYPE.check(&value)?;188 <Self as Typed>::TYPE.check(&value)?;
188 match value {189 match value {
189 Val::Num(n) => {190 Val::Num(n) => {
191 let n = n.get();
190 #[allow(clippy::float_cmp)]192 #[allow(clippy::float_cmp)]
191 if n.trunc() != n {193 if n.trunc() != n {
192 bail!(194 bail!(
202204
203 #[allow(clippy::cast_lossless)]205 #[allow(clippy::cast_lossless)]
204 fn into_untyped(value: Self) -> Result<Val> {206 fn into_untyped(value: Self) -> Result<Val> {
205 Ok(Val::Num(value.0 as f64))207 Ok(Val::try_num(value.0)?)
206 }208 }
207 }209 }
208 )*};210 )*};
220 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);222 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
221223
222 fn into_untyped(value: Self) -> Result<Val> {224 fn into_untyped(value: Self) -> Result<Val> {
223 Ok(Val::Num(value))225 Ok(Val::try_num(value)?)
224 }226 }
225227
226 fn from_untyped(value: Val) -> Result<Self> {228 fn from_untyped(value: Val) -> Result<Self> {
227 <Self as Typed>::TYPE.check(&value)?;229 <Self as Typed>::TYPE.check(&value)?;
228 match value {230 match value {
229 Val::Num(n) => Ok(n),231 Val::Num(n) => Ok(n.get()),
230 _ => unreachable!(),232 _ => unreachable!(),
231 }233 }
232 }234 }
237 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);239 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);
238240
239 fn into_untyped(value: Self) -> Result<Val> {241 fn into_untyped(value: Self) -> Result<Val> {
240 Ok(Val::Num(value.0))242 Ok(Val::try_num(value.0)?)
241 }243 }
242244
243 fn from_untyped(value: Val) -> Result<Self> {245 fn from_untyped(value: Val) -> Result<Self> {
244 <Self as Typed>::TYPE.check(&value)?;246 <Self as Typed>::TYPE.check(&value)?;
245 match value {247 match value {
246 Val::Num(n) => Ok(Self(n)),248 Val::Num(n) => Ok(Self(n.get())),
247 _ => unreachable!(),249 _ => unreachable!(),
248 }250 }
249 }251 }
253 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));255 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));
254256
255 fn into_untyped(value: Self) -> Result<Val> {257 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))258 Ok(Val::try_num(value)?)
260 }259 }
261260
262 fn from_untyped(value: Val) -> Result<Self> {261 fn from_untyped(value: Val) -> Result<Self> {
263 <Self as Typed>::TYPE.check(&value)?;262 <Self as Typed>::TYPE.check(&value)?;
264 match value {263 match value {
265 Val::Num(n) => {264 Val::Num(n) => {
265 let n = n.get();
266 #[allow(clippy::float_cmp)]266 #[allow(clippy::float_cmp)]
267 if n.trunc() != n {267 if n.trunc() != n {
268 bail!("cannot convert number with fractional part to usize")268 bail!("cannot convert number with fractional part to usize")
479 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));479 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));
480480
481 fn into_untyped(_: Self) -> Result<Val> {481 fn into_untyped(_: Self) -> Result<Val> {
482 Ok(Val::Num(-1.0))482 Ok(Val::Num(NumValue::new(-1.0).expect("finite")))
483 }483 }
484484
485 fn from_untyped(value: Val) -> Result<Self> {485 fn from_untyped(value: Val) -> Result<Self> {
680 }680 }
681}681}
682
683impl Typed for NumValue {
684 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
685
686 fn into_untyped(typed: Self) -> Result<Val> {
687 Ok(Val::Num(typed))
688 }
689
690 fn from_untyped(untyped: Val) -> Result<Self> {
691 Self::TYPE.check(&untyped)?;
692 match untyped {
693 Val::Num(v) => Ok(v),
694 _ => unreachable!(),
695 }
696 }
697}
682698
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
1use std::{fmt::Display, rc::Rc};1use std::{fmt::Display, rc::Rc};
22
3mod conversions;3pub(crate) mod conversions;
4pub use conversions::*;4pub use conversions::*;
5use jrsonnet_gcmodule::Trace;5use jrsonnet_gcmodule::Trace;
6pub use jrsonnet_types::{ComplexValType, ValType};6pub use jrsonnet_types::{ComplexValType, ValType};
155 },155 },
156 Self::BoundedNumber(from, to) => {156 Self::BoundedNumber(from, to) => {
157 if let Val::Num(n) = value {157 if let Val::Num(n) = value {
158 let n = n.get();
158 if from.map(|from| from > *n).unwrap_or(false)159 if from.map(|from| from > n).unwrap_or(false)
159 || to.map(|to| to < *n).unwrap_or(false)160 || to.map(|to| to < n).unwrap_or(false)
160 {161 {
161 return Err(TypeError::BoundsFailed(*n, *from, *to).into());162 return Err(TypeError::BoundsFailed(n, *from, *to).into());
162 }163 }
163 Ok(())164 Ok(())
164 } else {165 } else {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
1use std::{1use std::{
2 cell::RefCell,2 cell::RefCell,
3 cmp::Ordering,
3 fmt::{self, Debug, Display},4 fmt::{self, Debug, Display},
4 mem::replace,5 mem::replace,
5 num::NonZeroU32,6 num::NonZeroU32,
7 ops::Deref,
6 rc::Rc,8 rc::Rc,
7};9};
810
11use derivative::Derivative;
9use jrsonnet_gcmodule::{Cc, Trace};12use jrsonnet_gcmodule::{Cc, Trace};
10use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;
11use jrsonnet_types::ValType;14use jrsonnet_types::ValType;
15use thiserror::Error;
1216
13pub use crate::arr::{ArrValue, ArrayLike};17pub use crate::arr::{ArrValue, ArrayLike};
14use crate::{18use crate::{
379}383}
380impl Eq for StrValue {}384impl Eq for StrValue {}
381impl PartialOrd for StrValue {385impl PartialOrd for StrValue {
382 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {386 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
383 Some(self.cmp(other))387 Some(self.cmp(other))
384 }388 }
385}389}
386impl Ord for StrValue {390impl Ord for StrValue {
387 fn cmp(&self, other: &Self) -> std::cmp::Ordering {391 fn cmp(&self, other: &Self) -> Ordering {
388 let a = self.clone().into_flat();392 let a = self.clone().into_flat();
389 let b = other.clone().into_flat();393 let b = other.clone().into_flat();
390 a.cmp(&b)394 a.cmp(&b)
391 }395 }
392}396}
397
398/// Represents jsonnet number
399/// Jsonnet numbers are finite f64, with NaNs disallowed
400#[derive(Trace, Clone, Copy, Derivative)]
401#[derivative(Debug = "transparent")]
402#[repr(transparent)]
403pub struct NumValue(f64);
404impl NumValue {
405 /// Creates a [`NumValue`], if value is finite and not NaN
406 pub fn new(v: f64) -> Option<Self> {
407 if !v.is_finite() {
408 return None;
409 }
410 Some(Self(v))
411 }
412 pub const fn get(&self) -> f64 {
413 self.0
414 }
415}
416impl PartialEq for NumValue {
417 fn eq(&self, other: &Self) -> bool {
418 self.0 == other.0
419 }
420}
421impl Eq for NumValue {}
422impl Ord for NumValue {
423 fn cmp(&self, other: &Self) -> Ordering {
424 // Can't use `total_cmp`: its behavior for `-0` and `0`
425 // is not following wanted.
426 self.0.partial_cmp(&other.0).expect("NaNs are disallowed")
427 }
428}
429impl PartialOrd for NumValue {
430 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
431 Some(self.cmp(other))
432 }
433}
434impl Display for NumValue {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 Display::fmt(&self.0, f)
437 }
438}
439impl Deref for NumValue {
440 type Target = f64;
441
442 fn deref(&self) -> &Self::Target {
443 &self.0
444 }
445}
446macro_rules! impl_num {
447 ($($ty:ty),+) => {$(
448 impl From<$ty> for NumValue {
449 fn from(value: $ty) -> Self {
450 Self(value.into())
451 }
452 }
453 )+};
454}
455impl_num!(i8, u8, i16, u16, i32, u32);
456
457#[derive(Clone, Copy, Debug, Error, Trace)]
458pub enum ConvertNumValueError {
459 #[error("overflow")]
460 Overflow,
461 #[error("underflow")]
462 Underflow,
463 #[error("non-finite")]
464 NonFinite,
465}
466impl From<ConvertNumValueError> for Error {
467 fn from(e: ConvertNumValueError) -> Self {
468 Self::new(e.into())
469 }
470}
471
472macro_rules! impl_try_num {
473 ($($ty:ty),+) => {$(
474 impl TryFrom<$ty> for NumValue {
475 type Error = ConvertNumValueError;
476 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {
477 use crate::typed::conversions::{MIN_SAFE_INTEGER, MAX_SAFE_INTEGER};
478 let value = value as f64;
479 if value < MIN_SAFE_INTEGER {
480 return Err(ConvertNumValueError::Underflow)
481 } else if value > MAX_SAFE_INTEGER {
482 return Err(ConvertNumValueError::Overflow)
483 }
484 // Number is finite.
485 Ok(Self(value))
486 }
487 }
488 )+};
489}
490impl_try_num!(usize, isize, i64, u64);
491
492impl TryFrom<f64> for NumValue {
493 type Error = ConvertNumValueError;
494
495 fn try_from(value: f64) -> Result<Self, Self::Error> {
496 Self::new(value).ok_or(ConvertNumValueError::NonFinite)
497 }
498}
499impl TryFrom<f32> for NumValue {
500 type Error = ConvertNumValueError;
501
502 fn try_from(value: f32) -> Result<Self, Self::Error> {
503 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)
504 }
505}
393506
394/// Represents any valid Jsonnet value.507/// Represents any valid Jsonnet value.
395#[derive(Debug, Clone, Trace, Default)]508#[derive(Debug, Clone, Trace, Default)]
404 /// Represents a Jsonnet number.517 /// Represents a Jsonnet number.
405 /// Should be finite, and not NaN518 /// Should be finite, and not NaN
406 /// This restriction isn't enforced by enum, as enum field can't be marked as private519 /// This restriction isn't enforced by enum, as enum field can't be marked as private
407 Num(f64),520 Num(NumValue),
408 /// Experimental bigint521 /// Experimental bigint
409 #[cfg(feature = "exp-bigint")]522 #[cfg(feature = "exp-bigint")]
410 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),523 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),
449 }562 }
450 pub const fn as_num(&self) -> Option<f64> {563 pub const fn as_num(&self) -> Option<f64> {
451 match self {564 match self {
452 Self::Num(n) => Some(*n),565 Self::Num(n) => Some(n.get()),
453 _ => None,566 _ => None,
454 }567 }
455 }568 }
472 }585 }
473 }586 }
474
475 /// Creates `Val::Num` after checking for numeric overflow.
476 /// As numbers are `f64`, we can just check for their finity.
477 pub fn new_checked_num(num: f64) -> Result<Self> {
478 if num.is_finite() {
479 Ok(Self::Num(num))
480 } else {
481 bail!("overflow")
482 }
483 }
484587
485 pub const fn value_type(&self) -> ValType {588 pub const fn value_type(&self) -> ValType {
486 match self {589 match self {
527 pub fn string(string: impl Into<StrValue>) -> Self {630 pub fn string(string: impl Into<StrValue>) -> Self {
528 Self::Str(string.into())631 Self::Str(string.into())
529 }632 }
633 pub fn num(num: impl Into<NumValue>) -> Self {
634 Self::Num(num.into())
635 }
636 pub fn try_num<V, E>(num: V) -> Result<Self, E>
637 where
638 NumValue: TryFrom<V, Error = E>,
639 {
640 Ok(Self::Num(num.try_into()?))
641 }
530}642}
531643
532impl From<IStr> for Val {644impl From<IStr> for Val {
560 (Val::Bool(a), Val::Bool(b)) => a == b,672 (Val::Bool(a), Val::Bool(b)) => a == b,
561 (Val::Null, Val::Null) => true,673 (Val::Null, Val::Null) => true,
562 (Val::Str(a), Val::Str(b)) => a == b,674 (Val::Str(a), Val::Str(b)) => a == b,
563 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,675 (Val::Num(a), Val::Num(b)) => (a.get() - b.get()).abs() <= f64::EPSILON,
564 #[cfg(feature = "exp-bigint")]676 #[cfg(feature = "exp-bigint")]
565 (Val::BigInt(a), Val::BigInt(b)) => a == b,677 (Val::BigInt(a), Val::BigInt(b)) => a == b,
566 (Val::Arr(_), Val::Arr(_)) => {678 (Val::Arr(_), Val::Arr(_)) => {
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
275 if arr.is_empty() {275 if arr.is_empty() {
276 return eval_on_empty(onEmpty);276 return eval_on_empty(onEmpty);
277 }277 }
278 Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))278 Ok(Val::try_num(arr.iter().sum::<f64>() / (arr.len() as f64))?)
279}279}
280280
281#[builtin]281#[builtin]
modifiedcrates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth
6 operator::evaluate_mod_op,6 operator::evaluate_mod_op,
7 stdlib::std_format,7 stdlib::std_format,
8 typed::{Either, Either2},8 typed::{Either, Either2},
9 val::{equals, primitive_equals},9 val::{equals, primitive_equals, NumValue},
10 IStr, Result, Val,10 IStr, Result, Val,
11};11};
1212
13#[builtin]13#[builtin]
14pub fn builtin_mod(a: Either![f64, IStr], b: Val) -> Result<Val> {14pub fn builtin_mod(a: Either![NumValue, IStr], b: Val) -> Result<Val> {
15 use Either2::*;15 use Either2::*;
16 evaluate_mod_op(16 evaluate_mod_op(
17 &match a {17 &match a {
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
20 Unknown,20 Unknown,
21}21}
22
23#[derive(PartialEq)]
24struct NonNaNf64(f64);
25impl PartialOrd for NonNaNf64 {
26 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
27 Some(self.cmp(other))
28 }
29}
30impl Eq for NonNaNf64 {}
31impl Ord for NonNaNf64 {
32 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
33 self.0.partial_cmp(&other.0).expect("non nan")
34 }
35}
3622
37fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {23fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {
38 let mut sort_type = SortKeyType::Unknown;24 let mut sort_type = SortKeyType::Unknown;
56 let sort_type = get_sort_type(&values, |k| k)?;42 let sort_type = get_sort_type(&values, |k| k)?;
57 match sort_type {43 match sort_type {
58 SortKeyType::Number => values.sort_unstable_by_key(|v| match v {44 SortKeyType::Number => values.sort_unstable_by_key(|v| match v {
59 Val::Num(n) => NonNaNf64(*n),45 Val::Num(n) => *n,
60 _ => unreachable!(),46 _ => unreachable!(),
61 }),47 }),
62 SortKeyType::String => values.sort_unstable_by_key(|v| match v {48 SortKeyType::String => values.sort_unstable_by_key(|v| match v {
95 let sort_type = get_sort_type(&vk, |v| &v.1)?;81 let sort_type = get_sort_type(&vk, |v| &v.1)?;
96 match sort_type {82 match sort_type {
97 SortKeyType::Number => vk.sort_by_key(|v| match v.1 {83 SortKeyType::Number => vk.sort_by_key(|v| match v.1 {
98 Val::Num(n) => NonNaNf64(n),84 Val::Num(n) => n,
99 _ => unreachable!(),85 _ => unreachable!(),
100 }),86 }),
101 SortKeyType::String => vk.sort_by_key(|v| match &v.1 {87 SortKeyType::String => vk.sort_by_key(|v| match &v.1 {
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
116 .enumerate()116 .enumerate()
117 {117 {
118 if &strb[i..i + pat.len()] == pat {118 if &strb[i..i + pat.len()] == pat {
119 out.push(Val::Num(ch_idx as f64));119 out.push(Val::Num(
120 ch_idx.try_into().expect("unrealisticly long string"),
121 ));
120 }122 }
121 }123 }