difftreelog
feat unify Arg and Typed handling for Thunk
in: master
8 files changed
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -81,7 +81,7 @@
#[must_use]
pub fn into_future(self, ctx: Pending<Self>) -> Self {
{
- ctx.0.borrow_mut().replace(self);
+ ctx.clone().fill(self);
}
ctx.unwrap()
}
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,29 +1,49 @@
-use std::cell::RefCell;
+use std::cell::OnceCell;
use jrsonnet_gcmodule::{Cc, Trace};
+use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};
+
// TODO: Replace with OnceCell once in std
#[derive(Clone, Trace)]
-pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
+pub struct Pending<V: Trace + 'static>(pub Cc<OnceCell<V>>);
impl<T: Trace + 'static> Pending<T> {
pub fn new() -> Self {
- Self(Cc::new(RefCell::new(None)))
+ Self(Cc::new(OnceCell::new()))
}
pub fn new_filled(v: T) -> Self {
- Self(Cc::new(RefCell::new(Some(v))))
+ let cell = OnceCell::new();
+ let _ = cell.set(v);
+ Self(Cc::new(cell))
}
/// # Panics
/// If wrapper is filled already
pub fn fill(self, value: T) {
- assert!(self.0.borrow().is_none(), "wrapper is filled already");
- self.0.borrow_mut().replace(value);
+ self.0
+ .set(value)
+ .map_err(|_| ())
+ .expect("wrapper is filled already")
}
}
impl<T: Clone + Trace + 'static> Pending<T> {
/// # Panics
/// If wrapper is not yet filled
pub fn unwrap(&self) -> T {
- self.0.borrow().as_ref().cloned().unwrap()
+ self.0.get().cloned().expect("pending was not filled")
+ }
+ pub fn try_get(&self) -> Option<T> {
+ self.0.get().cloned()
+ }
+}
+
+impl<T: Trace + Clone> ThunkValue for Pending<T> {
+ type Output = T;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let Some(value) = self.0.get() else {
+ throw!(InfiniteRecursionDetected);
+ };
+ Ok(value.clone())
}
}
@@ -32,3 +52,9 @@
Self::new()
}
}
+
+impl<T: Trace + Clone> Into<Thunk<T>> for Pending<T> {
+ fn into(self) -> Thunk<T> {
+ Thunk::new(self)
+ }
+}
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -48,28 +48,22 @@
where
T: Typed + Clone,
{
- fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+ fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+ if T::provides_lazy() && !tailstrict {
+ return Ok(T::into_lazy_untyped(self.clone()));
+ }
let val = T::into_untyped(self.clone())?;
Ok(Thunk::evaluated(val))
}
}
impl<T> OptionalContext for T where T: Typed + Clone {}
-impl ArgLike for Thunk<Val> {
- fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
- if tailstrict {
- self.force()?;
- }
- Ok(self.clone())
- }
-}
-impl OptionalContext for Thunk<Val> {}
-
#[derive(Clone, Trace)]
pub enum TlaArg {
String(IStr),
Code(LocExpr),
Val(Val),
+ Lazy(Thunk<Val>),
}
impl ArgLike for TlaArg {
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
@@ -84,6 +78,7 @@
})
}),
TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
+ TlaArg::Lazy(lazy) => Ok(lazy.clone()),
}
}
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -15,7 +15,9 @@
function::CallLocation,
gc::{GcHashMap, GcHashSet, TraceBox},
operator::evaluate_add_op,
- tb, throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
+ tb, throw,
+ val::ThunkValue,
+ MaybeUnbound, Result, State, Thunk, Unbound, Val,
};
#[cfg(not(feature = "exp-preserve-order"))]
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::ops::Deref;23use jrsonnet_gcmodule::Cc;4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9 arr::ArrValue,10 error::Result,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 throw,13 typed::CheckType,14 val::{IndexableVal, StrValue},15 ObjValue, ObjValueBuilder, Val,16};1718pub trait TypedObj: Typed {19 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;20 fn parse(obj: &ObjValue) -> Result<Self>;21 fn into_object(self) -> Result<ObjValue> {22 let mut builder = ObjValueBuilder::new();23 self.serialize(&mut builder)?;24 Ok(builder.build())25 }26}2728pub trait Typed: Sized {29 const TYPE: &'static ComplexValType;30 fn into_untyped(typed: Self) -> Result<Val>;31 fn from_untyped(untyped: Val) -> Result<Self>;3233 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result34 /// This method returns identity in impl Typed for Result, and should not be overriden35 #[doc(hidden)]36 fn into_result(typed: Self) -> Result<Val> {37 let value = Self::into_untyped(typed)?;38 Ok(value)39 }40}4142const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;4344macro_rules! impl_int {45 ($($ty:ty)*) => {$(46 impl Typed for $ty {47 const TYPE: &'static ComplexValType =48 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));49 fn from_untyped(value: Val) -> Result<Self> {50 <Self as Typed>::TYPE.check(&value)?;51 match value {52 Val::Num(n) => {53 #[allow(clippy::float_cmp)]54 if n.trunc() != n {55 throw!(56 "cannot convert number with fractional part to {}",57 stringify!($ty)58 )59 }60 Ok(n as Self)61 }62 _ => unreachable!(),63 }64 }65 fn into_untyped(value: Self) -> Result<Val> {66 Ok(Val::Num(value as f64))67 }68 }69 )*};70}7172impl_int!(i8 u8 i16 u16 i32 u32);7374macro_rules! impl_bounded_int {75 ($($name:ident = $ty:ty)*) => {$(76 #[derive(Clone, Copy)]77 pub struct $name<const MIN: $ty, const MAX: $ty>($ty);78 impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {79 pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {80 if value >= MIN && value <= MAX {81 Some(Self(value))82 } else {83 None84 }85 }86 pub const fn value(self) -> $ty {87 self.088 }89 }90 impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {91 type Target = $ty;92 fn deref(&self) -> &Self::Target {93 &self.094 }95 }9697 impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {98 const TYPE: &'static ComplexValType =99 &ComplexValType::BoundedNumber(100 Some(MIN as f64),101 Some(MAX as f64),102 );103104 fn from_untyped(value: Val) -> Result<Self> {105 <Self as Typed>::TYPE.check(&value)?;106 match value {107 Val::Num(n) => {108 #[allow(clippy::float_cmp)]109 if n.trunc() != n {110 throw!(111 "cannot convert number with fractional part to {}",112 stringify!($ty)113 )114 }115 Ok(Self(n as $ty))116 }117 _ => unreachable!(),118 }119 }120121 fn into_untyped(value: Self) -> Result<Val> {122 Ok(Val::Num(value.0 as f64))123 }124 }125 )*};126}127128impl_bounded_int!(129 BoundedI8 = i8130 BoundedI16 = i16131 BoundedI32 = i32132 BoundedI64 = i64133 BoundedUsize = usize134);135136impl Typed for f64 {137 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);138139 fn into_untyped(value: Self) -> Result<Val> {140 Ok(Val::Num(value))141 }142143 fn from_untyped(value: Val) -> Result<Self> {144 <Self as Typed>::TYPE.check(&value)?;145 match value {146 Val::Num(n) => Ok(n),147 _ => unreachable!(),148 }149 }150}151152pub struct PositiveF64(pub f64);153impl Typed for PositiveF64 {154 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);155156 fn into_untyped(value: Self) -> Result<Val> {157 Ok(Val::Num(value.0))158 }159160 fn from_untyped(value: Val) -> Result<Self> {161 <Self as Typed>::TYPE.check(&value)?;162 match value {163 Val::Num(n) => Ok(Self(n)),164 _ => unreachable!(),165 }166 }167}168impl Typed for usize {169 const TYPE: &'static ComplexValType =170 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));171172 fn into_untyped(value: Self) -> Result<Val> {173 if value > MAX_SAFE_INTEGER as Self {174 throw!("number is too large")175 }176 Ok(Val::Num(value as f64))177 }178179 fn from_untyped(value: Val) -> Result<Self> {180 <Self as Typed>::TYPE.check(&value)?;181 match value {182 Val::Num(n) => {183 #[allow(clippy::float_cmp)]184 if n.trunc() != n {185 throw!("cannot convert number with fractional part to usize")186 }187 Ok(n as Self)188 }189 _ => unreachable!(),190 }191 }192}193194impl Typed for IStr {195 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);196197 fn into_untyped(value: Self) -> Result<Val> {198 Ok(Val::Str(StrValue::Flat(value)))199 }200201 fn from_untyped(value: Val) -> Result<Self> {202 <Self as Typed>::TYPE.check(&value)?;203 match value {204 Val::Str(s) => Ok(s.into_flat()),205 _ => unreachable!(),206 }207 }208}209210impl Typed for String {211 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);212213 fn into_untyped(value: Self) -> Result<Val> {214 Ok(Val::Str(StrValue::Flat(value.into())))215 }216217 fn from_untyped(value: Val) -> Result<Self> {218 <Self as Typed>::TYPE.check(&value)?;219 match value {220 Val::Str(s) => Ok(s.to_string()),221 _ => unreachable!(),222 }223 }224}225226impl Typed for char {227 const TYPE: &'static ComplexValType = &ComplexValType::Char;228229 fn into_untyped(value: Self) -> Result<Val> {230 Ok(Val::Str(StrValue::Flat(value.to_string().into())))231 }232233 fn from_untyped(value: Val) -> Result<Self> {234 <Self as Typed>::TYPE.check(&value)?;235 match value {236 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),237 _ => unreachable!(),238 }239 }240}241242impl<T> Typed for Vec<T>243where244 T: Typed,245{246 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);247248 fn into_untyped(value: Self) -> Result<Val> {249 Ok(Val::Arr(250 value251 .into_iter()252 .map(T::into_untyped)253 .collect::<Result<ArrValue>>()?,254 ))255 }256257 fn from_untyped(value: Val) -> Result<Self> {258 let Val::Arr(a) = value else {259 <Self as Typed>::TYPE.check(&value)?;260 unreachable!("typecheck should fail")261 };262 a.iter()263 .map(|r| r.and_then(T::from_untyped))264 .collect::<Result<Vec<T>>>()265 }266}267268impl Typed for Val {269 const TYPE: &'static ComplexValType = &ComplexValType::Any;270271 fn into_untyped(typed: Self) -> Result<Val> {272 Ok(typed)273 }274 fn from_untyped(untyped: Val) -> Result<Self> {275 Ok(untyped)276 }277}278279// Hack280#[doc(hidden)]281impl<T> Typed for Result<T>282where283 T: Typed,284{285 const TYPE: &'static ComplexValType = &ComplexValType::Any;286287 fn into_untyped(_typed: Self) -> Result<Val> {288 panic!("do not use this conversion")289 }290291 fn from_untyped(_untyped: Val) -> Result<Self> {292 panic!("do not use this conversion")293 }294295 fn into_result(typed: Self) -> Result<Val> {296 typed.map(T::into_untyped)?297 }298}299300/// Specialization301impl Typed for IBytes {302 const TYPE: &'static ComplexValType =303 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));304305 fn into_untyped(value: Self) -> Result<Val> {306 Ok(Val::Arr(ArrValue::bytes(value)))307 }308309 fn from_untyped(value: Val) -> Result<Self> {310 if let Val::Arr(ArrValue::Bytes(bytes)) = value {311 return Ok(bytes.0);312 }313 <Self as Typed>::TYPE.check(&value)?;314 match value {315 Val::Arr(a) => {316 let mut out = Vec::with_capacity(a.len());317 for e in a.iter() {318 let r = e?;319 out.push(u8::from_untyped(r)?);320 }321 Ok(out.as_slice().into())322 }323 _ => unreachable!(),324 }325 }326}327328pub struct M1;329impl Typed for M1 {330 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));331332 fn into_untyped(_: Self) -> Result<Val> {333 Ok(Val::Num(-1.0))334 }335336 fn from_untyped(value: Val) -> Result<Self> {337 <Self as Typed>::TYPE.check(&value)?;338 Ok(Self)339 }340}341342macro_rules! decl_either {343 ($($name: ident, $($id: ident)*);*) => {$(344 #[derive(Clone)]345 pub enum $name<$($id),*> {346 $($id($id)),*347 }348 impl<$($id),*> Typed for $name<$($id),*>349 where350 $($id: Typed,)*351 {352 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);353354 fn into_untyped(value: Self) -> Result<Val> {355 match value {$(356 $name::$id(v) => $id::into_untyped(v)357 ),*}358 }359360 fn from_untyped(value: Val) -> Result<Self> {361 $(362 if $id::TYPE.check(&value).is_ok() {363 $id::from_untyped(value).map(Self::$id)364 } else365 )* {366 <Self as Typed>::TYPE.check(&value)?;367 unreachable!()368 }369 }370 }371 )*}372}373decl_either!(374 Either1, A;375 Either2, A B;376 Either3, A B C;377 Either4, A B C D;378 Either5, A B C D E;379 Either6, A B C D E F;380 Either7, A B C D E F G381);382#[macro_export]383macro_rules! Either {384 ($a:ty) => {Either1<$a>};385 ($a:ty, $b:ty) => {Either2<$a, $b>};386 ($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};387 ($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};388 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};389 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};390 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};391}392pub use Either;393394pub type MyType = Either![u32, f64, String];395396impl Typed for ArrValue {397 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);398399 fn into_untyped(value: Self) -> Result<Val> {400 Ok(Val::Arr(value))401 }402403 fn from_untyped(value: Val) -> Result<Self> {404 <Self as Typed>::TYPE.check(&value)?;405 match value {406 Val::Arr(a) => Ok(a),407 _ => unreachable!(),408 }409 }410}411412impl Typed for FuncVal {413 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);414415 fn into_untyped(value: Self) -> Result<Val> {416 Ok(Val::Func(value))417 }418419 fn from_untyped(value: Val) -> Result<Self> {420 <Self as Typed>::TYPE.check(&value)?;421 match value {422 Val::Func(a) => Ok(a),423 _ => unreachable!(),424 }425 }426}427428impl Typed for Cc<FuncDesc> {429 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);430431 fn into_untyped(value: Self) -> Result<Val> {432 Ok(Val::Func(FuncVal::Normal(value)))433 }434435 fn from_untyped(value: Val) -> Result<Self> {436 <Self as Typed>::TYPE.check(&value)?;437 match value {438 Val::Func(FuncVal::Normal(desc)) => Ok(desc),439 Val::Func(_) => throw!("expected normal function, not builtin"),440 _ => unreachable!(),441 }442 }443}444445impl Typed for ObjValue {446 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);447448 fn into_untyped(value: Self) -> Result<Val> {449 Ok(Val::Obj(value))450 }451452 fn from_untyped(value: Val) -> Result<Self> {453 <Self as Typed>::TYPE.check(&value)?;454 match value {455 Val::Obj(a) => Ok(a),456 _ => unreachable!(),457 }458 }459}460461impl Typed for bool {462 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);463464 fn into_untyped(value: Self) -> Result<Val> {465 Ok(Val::Bool(value))466 }467468 fn from_untyped(value: Val) -> Result<Self> {469 <Self as Typed>::TYPE.check(&value)?;470 match value {471 Val::Bool(a) => Ok(a),472 _ => unreachable!(),473 }474 }475}476impl Typed for IndexableVal {477 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[478 &ComplexValType::Simple(ValType::Arr),479 &ComplexValType::Simple(ValType::Str),480 ]);481482 fn into_untyped(value: Self) -> Result<Val> {483 match value {484 IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),485 IndexableVal::Arr(a) => Ok(Val::Arr(a)),486 }487 }488489 fn from_untyped(value: Val) -> Result<Self> {490 <Self as Typed>::TYPE.check(&value)?;491 value.into_indexable()492 }493}494495pub struct Null;496impl Typed for Null {497 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);498499 fn into_untyped(_: Self) -> Result<Val> {500 Ok(Val::Null)501 }502503 fn from_untyped(value: Val) -> Result<Self> {504 <Self as Typed>::TYPE.check(&value)?;505 Ok(Self)506 }507}508509pub struct NativeFn<D: NativeDesc>(D::Value);510impl<D: NativeDesc> Deref for NativeFn<D> {511 type Target = D::Value;512513 fn deref(&self) -> &Self::Target {514 &self.0515 }516}517impl<D: NativeDesc> Typed for NativeFn<D> {518 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);519520 fn into_untyped(_typed: Self) -> Result<Val> {521 throw!("can only convert functions from jsonnet to native")522 }523524 fn from_untyped(untyped: Val) -> Result<Self> {525 Ok(Self(526 untyped527 .as_func()528 .expect("shape is checked")529 .into_native::<D>(),530 ))531 }532}1use 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,10 error::Result,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 throw,13 typed::CheckType,14 val::{IndexableVal, StrValue, ThunkMapper},15 ObjValue, ObjValueBuilder, Thunk, Val,16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22 K: Typed + Trace,23{24 type Output = K;2526 fn map(self, from: Val) -> Result<Self::Output> {27 K::from_untyped(from)28 }29}30impl<K: Trace> Default for FromUntyped<K> {31 fn default() -> Self {32 Self(PhantomData)33 }34}3536pub trait TypedObj: Typed {37 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;38 fn parse(obj: &ObjValue) -> Result<Self>;39 fn into_object(self) -> Result<ObjValue> {40 let mut builder = ObjValueBuilder::new();41 self.serialize(&mut builder)?;42 Ok(builder.build())43 }44}4546pub trait Typed: Sized {47 const TYPE: &'static ComplexValType;48 fn into_untyped(typed: Self) -> Result<Val>;49 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50 Thunk::from(Self::into_untyped(typed))51 }52 fn from_untyped(untyped: Val) -> Result<Self>;53 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54 Self::from_untyped(lazy.evaluate()?)55 }5657 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58 fn provides_lazy() -> bool {59 false60 }6162 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63 fn wants_lazy() -> bool {64 false65 }6667 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result68 /// This method returns identity in impl Typed for Result, and should not be overriden69 #[doc(hidden)]70 fn into_result(typed: Self) -> Result<Val> {71 let value = Self::into_untyped(typed)?;72 Ok(value)73 }74}7576impl<T> Typed for Thunk<T>77where78 T: Typed + Trace + Clone,79{80 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182 fn into_untyped(typed: Self) -> Result<Val> {83 T::into_untyped(typed.evaluate()?)84 }8586 fn from_untyped(untyped: Val) -> Result<Self> {87 Self::from_lazy_untyped(Thunk::evaluated(untyped))88 }8990 fn provides_lazy() -> bool {91 true92 }9394 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95 #[derive(Trace)]96 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97 impl<K> ThunkMapper<K> for IntoUntyped<K>98 where99 K: Typed + Trace,100 {101 type Output = Val;102103 fn map(self, from: K) -> Result<Self::Output> {104 K::into_untyped(from)105 }106 }107 impl<K: Trace> Default for IntoUntyped<K> {108 fn default() -> Self {109 Self(PhantomData)110 }111 }112 inner.map(<IntoUntyped<T>>::default())113 }114115 fn wants_lazy() -> bool {116 true117 }118119 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120 Ok(inner.map(<FromUntyped<T>>::default()))121 }122}123124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;125126macro_rules! impl_int {127 ($($ty:ty)*) => {$(128 impl Typed for $ty {129 const TYPE: &'static ComplexValType =130 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));131 fn from_untyped(value: Val) -> Result<Self> {132 <Self as Typed>::TYPE.check(&value)?;133 match value {134 Val::Num(n) => {135 #[allow(clippy::float_cmp)]136 if n.trunc() != n {137 throw!(138 "cannot convert number with fractional part to {}",139 stringify!($ty)140 )141 }142 Ok(n as Self)143 }144 _ => unreachable!(),145 }146 }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 throw!(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 fn into_untyped(value: Self) -> Result<Val> {204 Ok(Val::Num(value.0 as f64))205 }206 }207 )*};208}209210impl_bounded_int!(211 BoundedI8 = i8212 BoundedI16 = i16213 BoundedI32 = i32214 BoundedI64 = i64215 BoundedUsize = usize216);217218impl Typed for f64 {219 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);220221 fn into_untyped(value: Self) -> Result<Val> {222 Ok(Val::Num(value))223 }224225 fn from_untyped(value: Val) -> Result<Self> {226 <Self as Typed>::TYPE.check(&value)?;227 match value {228 Val::Num(n) => Ok(n),229 _ => unreachable!(),230 }231 }232}233234pub struct PositiveF64(pub f64);235impl Typed for PositiveF64 {236 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);237238 fn into_untyped(value: Self) -> Result<Val> {239 Ok(Val::Num(value.0))240 }241242 fn from_untyped(value: Val) -> Result<Self> {243 <Self as Typed>::TYPE.check(&value)?;244 match value {245 Val::Num(n) => Ok(Self(n)),246 _ => unreachable!(),247 }248 }249}250impl Typed for usize {251 const TYPE: &'static ComplexValType =252 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));253254 fn into_untyped(value: Self) -> Result<Val> {255 if value > MAX_SAFE_INTEGER as Self {256 throw!("number is too large")257 }258 Ok(Val::Num(value as f64))259 }260261 fn from_untyped(value: Val) -> Result<Self> {262 <Self as Typed>::TYPE.check(&value)?;263 match value {264 Val::Num(n) => {265 #[allow(clippy::float_cmp)]266 if n.trunc() != n {267 throw!("cannot convert number with fractional part to usize")268 }269 Ok(n as Self)270 }271 _ => unreachable!(),272 }273 }274}275276impl Typed for IStr {277 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);278279 fn into_untyped(value: Self) -> Result<Val> {280 Ok(Val::Str(StrValue::Flat(value)))281 }282283 fn from_untyped(value: Val) -> Result<Self> {284 <Self as Typed>::TYPE.check(&value)?;285 match value {286 Val::Str(s) => Ok(s.into_flat()),287 _ => unreachable!(),288 }289 }290}291292impl Typed for String {293 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);294295 fn into_untyped(value: Self) -> Result<Val> {296 Ok(Val::Str(StrValue::Flat(value.into())))297 }298299 fn from_untyped(value: Val) -> Result<Self> {300 <Self as Typed>::TYPE.check(&value)?;301 match value {302 Val::Str(s) => Ok(s.to_string()),303 _ => unreachable!(),304 }305 }306}307308impl Typed for char {309 const TYPE: &'static ComplexValType = &ComplexValType::Char;310311 fn into_untyped(value: Self) -> Result<Val> {312 Ok(Val::Str(StrValue::Flat(value.to_string().into())))313 }314315 fn from_untyped(value: Val) -> Result<Self> {316 <Self as Typed>::TYPE.check(&value)?;317 match value {318 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),319 _ => unreachable!(),320 }321 }322}323324impl<T> Typed for Vec<T>325where326 T: Typed,327{328 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);329330 fn into_untyped(value: Self) -> Result<Val> {331 Ok(Val::Arr(332 value333 .into_iter()334 .map(T::into_untyped)335 .collect::<Result<ArrValue>>()?,336 ))337 }338339 fn from_untyped(value: Val) -> Result<Self> {340 let Val::Arr(a) = value else {341 <Self as Typed>::TYPE.check(&value)?;342 unreachable!("typecheck should fail")343 };344 a.iter()345 .map(|r| r.and_then(T::from_untyped))346 .collect::<Result<Vec<T>>>()347 }348}349350impl Typed for Val {351 const TYPE: &'static ComplexValType = &ComplexValType::Any;352353 fn into_untyped(typed: Self) -> Result<Val> {354 Ok(typed)355 }356 fn from_untyped(untyped: Val) -> Result<Self> {357 Ok(untyped)358 }359}360361// Hack362#[doc(hidden)]363impl<T> Typed for Result<T>364where365 T: Typed,366{367 const TYPE: &'static ComplexValType = &ComplexValType::Any;368369 fn into_untyped(_typed: Self) -> Result<Val> {370 panic!("do not use this conversion")371 }372373 fn from_untyped(_untyped: Val) -> Result<Self> {374 panic!("do not use this conversion")375 }376377 fn into_result(typed: Self) -> Result<Val> {378 typed.map(T::into_untyped)?379 }380}381382/// Specialization383impl Typed for IBytes {384 const TYPE: &'static ComplexValType =385 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));386387 fn into_untyped(value: Self) -> Result<Val> {388 Ok(Val::Arr(ArrValue::bytes(value)))389 }390391 fn from_untyped(value: Val) -> Result<Self> {392 if let Val::Arr(ArrValue::Bytes(bytes)) = value {393 return Ok(bytes.0);394 }395 <Self as Typed>::TYPE.check(&value)?;396 match value {397 Val::Arr(a) => {398 let mut out = Vec::with_capacity(a.len());399 for e in a.iter() {400 let r = e?;401 out.push(u8::from_untyped(r)?);402 }403 Ok(out.as_slice().into())404 }405 _ => unreachable!(),406 }407 }408}409410pub struct M1;411impl Typed for M1 {412 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));413414 fn into_untyped(_: Self) -> Result<Val> {415 Ok(Val::Num(-1.0))416 }417418 fn from_untyped(value: Val) -> Result<Self> {419 <Self as Typed>::TYPE.check(&value)?;420 Ok(Self)421 }422}423424macro_rules! decl_either {425 ($($name: ident, $($id: ident)*);*) => {$(426 #[derive(Clone)]427 pub enum $name<$($id),*> {428 $($id($id)),*429 }430 impl<$($id),*> Typed for $name<$($id),*>431 where432 $($id: Typed,)*433 {434 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);435436 fn into_untyped(value: Self) -> Result<Val> {437 match value {$(438 $name::$id(v) => $id::into_untyped(v)439 ),*}440 }441442 fn from_untyped(value: Val) -> Result<Self> {443 $(444 if $id::TYPE.check(&value).is_ok() {445 $id::from_untyped(value).map(Self::$id)446 } else447 )* {448 <Self as Typed>::TYPE.check(&value)?;449 unreachable!()450 }451 }452 }453 )*}454}455decl_either!(456 Either1, A;457 Either2, A B;458 Either3, A B C;459 Either4, A B C D;460 Either5, A B C D E;461 Either6, A B C D E F;462 Either7, A B C D E F G463);464#[macro_export]465macro_rules! Either {466 ($a:ty) => {Either1<$a>};467 ($a:ty, $b:ty) => {Either2<$a, $b>};468 ($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};469 ($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};470 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};471 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};472 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};473}474pub use Either;475476pub type MyType = Either![u32, f64, String];477478impl Typed for ArrValue {479 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);480481 fn into_untyped(value: Self) -> Result<Val> {482 Ok(Val::Arr(value))483 }484485 fn from_untyped(value: Val) -> Result<Self> {486 <Self as Typed>::TYPE.check(&value)?;487 match value {488 Val::Arr(a) => Ok(a),489 _ => unreachable!(),490 }491 }492}493494impl Typed for FuncVal {495 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);496497 fn into_untyped(value: Self) -> Result<Val> {498 Ok(Val::Func(value))499 }500501 fn from_untyped(value: Val) -> Result<Self> {502 <Self as Typed>::TYPE.check(&value)?;503 match value {504 Val::Func(a) => Ok(a),505 _ => unreachable!(),506 }507 }508}509510impl Typed for Cc<FuncDesc> {511 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);512513 fn into_untyped(value: Self) -> Result<Val> {514 Ok(Val::Func(FuncVal::Normal(value)))515 }516517 fn from_untyped(value: Val) -> Result<Self> {518 <Self as Typed>::TYPE.check(&value)?;519 match value {520 Val::Func(FuncVal::Normal(desc)) => Ok(desc),521 Val::Func(_) => throw!("expected normal function, not builtin"),522 _ => unreachable!(),523 }524 }525}526527impl Typed for ObjValue {528 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);529530 fn into_untyped(value: Self) -> Result<Val> {531 Ok(Val::Obj(value))532 }533534 fn from_untyped(value: Val) -> Result<Self> {535 <Self as Typed>::TYPE.check(&value)?;536 match value {537 Val::Obj(a) => Ok(a),538 _ => unreachable!(),539 }540 }541}542543impl Typed for bool {544 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);545546 fn into_untyped(value: Self) -> Result<Val> {547 Ok(Val::Bool(value))548 }549550 fn from_untyped(value: Val) -> Result<Self> {551 <Self as Typed>::TYPE.check(&value)?;552 match value {553 Val::Bool(a) => Ok(a),554 _ => unreachable!(),555 }556 }557}558impl Typed for IndexableVal {559 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[560 &ComplexValType::Simple(ValType::Arr),561 &ComplexValType::Simple(ValType::Str),562 ]);563564 fn into_untyped(value: Self) -> Result<Val> {565 match value {566 IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),567 IndexableVal::Arr(a) => Ok(Val::Arr(a)),568 }569 }570571 fn from_untyped(value: Val) -> Result<Self> {572 <Self as Typed>::TYPE.check(&value)?;573 value.into_indexable()574 }575}576577pub struct Null;578impl Typed for Null {579 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);580581 fn into_untyped(_: Self) -> Result<Val> {582 Ok(Val::Null)583 }584585 fn from_untyped(value: Val) -> Result<Self> {586 <Self as Typed>::TYPE.check(&value)?;587 Ok(Self)588 }589}590591pub struct NativeFn<D: NativeDesc>(D::Value);592impl<D: NativeDesc> Deref for NativeFn<D> {593 type Target = D::Value;594595 fn deref(&self) -> &Self::Target {596 &self.0597 }598}599impl<D: NativeDesc> Typed for NativeFn<D> {600 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);601602 fn into_untyped(_typed: Self) -> Result<Val> {603 throw!("can only convert functions from jsonnet to native")604 }605606 fn from_untyped(untyped: Val) -> Result<Self> {607 Ok(Self(608 untyped609 .as_func()610 .expect("shape is checked")611 .into_native::<D>(),612 ))613 }614}crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -252,6 +252,7 @@
}
Ok(())
}
+ Self::Lazy(_lazy) => Ok(()),
}
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -88,6 +88,54 @@
}
}
+pub trait ThunkMapper<Input>: Trace {
+ type Output;
+ fn map(self, from: Input) -> Result<Self::Output>;
+}
+impl<Input> Thunk<Input>
+where
+ Input: Trace + Clone,
+{
+ pub fn map<M>(self, mapper: M) -> Thunk<M::Output>
+ where
+ M: ThunkMapper<Input>,
+ M::Output: Trace,
+ {
+ #[derive(Trace)]
+ struct Mapped<Input: Trace, Mapper: Trace> {
+ inner: Thunk<Input>,
+ mapper: Mapper,
+ }
+ impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>
+ where
+ Input: Trace + Clone,
+ Mapper: ThunkMapper<Input>,
+ {
+ type Output = Mapper::Output;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let value = self.inner.evaluate()?;
+ let mapped = self.mapper.map(value)?;
+ Ok(mapped)
+ }
+ }
+
+ Thunk::new(Mapped::<Input, M> {
+ inner: self,
+ mapper,
+ })
+ }
+}
+
+impl<T: Trace> From<Result<T>> for Thunk<T> {
+ fn from(value: Result<T>) -> Self {
+ match value {
+ Ok(o) => Self::evaluated(o),
+ Err(e) => Self::errored(e),
+ }
+ }
+}
+
type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);
#[derive(Trace, Clone)]
@@ -272,6 +320,11 @@
Self::Flat(value.into())
}
}
+impl From<IStr> for StrValue {
+ fn from(value: IStr) -> Self {
+ Self::Flat(value)
+ }
+}
impl Display for StrValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -128,10 +128,12 @@
Array(Box<ComplexValType>),
ArrayRef(&'static ComplexValType),
ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
+ AttrsOf(&'static ComplexValType),
Union(Vec<ComplexValType>),
UnionRef(&'static [&'static ComplexValType]),
Sum(Vec<ComplexValType>),
SumRef(&'static [&'static ComplexValType]),
+ Lazy(&'static ComplexValType),
}
impl From<ValType> for ComplexValType {
@@ -195,10 +197,18 @@
}
write!(f, "}}")?;
}
+ ComplexValType::AttrsOf(a) => {
+ if matches!(a, ComplexValType::Any) {
+ write!(f, "object")?;
+ } else {
+ write!(f, "AttrsOf<{a}>")?;
+ }
+ }
ComplexValType::Union(v) => write_union(f, true, v.iter())?,
ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,
ComplexValType::Sum(v) => write_union(f, false, v.iter())?,
ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,
+ ComplexValType::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
};
Ok(())
}