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.rsdiffbeforeafterboth1use hashbrown::HashMap;2use jrsonnet_gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};56use crate::{7 error::Result,8 evaluate,9 gc::GcHashMap,10 typed::Typed,11 val::{StrValue, ThunkValue},12 Context, Thunk, Val,13};1415/// Marker for arguments, which can be evaluated with context set to None16pub trait OptionalContext {}1718#[derive(Trace)]19struct EvaluateThunk {20 ctx: Context,21 expr: LocExpr,22}23impl ThunkValue for EvaluateThunk {24 type Output = Val;25 fn get(self: Box<Self>) -> Result<Val> {26 evaluate(self.ctx, &self.expr)27 }28}2930pub trait ArgLike {31 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;32}3334impl ArgLike for &LocExpr {35 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {36 Ok(if tailstrict {37 Thunk::evaluated(evaluate(ctx, self)?)38 } else {39 Thunk::new(EvaluateThunk {40 ctx,41 expr: (*self).clone(),42 })43 })44 }45}4647impl<T> ArgLike for T48where49 T: Typed + Clone,50{51 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {52 let val = T::into_untyped(self.clone())?;53 Ok(Thunk::evaluated(val))54 }55}56impl<T> OptionalContext for T where T: Typed + Clone {}5758impl ArgLike for Thunk<Val> {59 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {60 if tailstrict {61 self.force()?;62 }63 Ok(self.clone())64 }65}66impl OptionalContext for Thunk<Val> {}6768#[derive(Clone, Trace)]69pub enum TlaArg {70 String(IStr),71 Code(LocExpr),72 Val(Val),73}74impl ArgLike for TlaArg {75 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {76 match self {77 TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(StrValue::Flat(s.clone())))),78 TlaArg::Code(code) => Ok(if tailstrict {79 Thunk::evaluated(evaluate(ctx, code)?)80 } else {81 Thunk::new(EvaluateThunk {82 ctx,83 expr: code.clone(),84 })85 }),86 TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),87 }88 }89}9091mod sealed {92 /// Implemented for `ArgsLike`, where only unnamed arguments present93 pub trait Unnamed {}94 /// Implemented for `ArgsLike`, where only named arguments present95 pub trait Named {}96}9798pub trait ArgsLike {99 fn unnamed_len(&self) -> usize;100 fn unnamed_iter(101 &self,102 ctx: Context,103 tailstrict: bool,104 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,105 ) -> Result<()>;106 fn named_iter(107 &self,108 ctx: Context,109 tailstrict: bool,110 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,111 ) -> Result<()>;112 fn named_names(&self, handler: &mut dyn FnMut(&IStr));113}114115impl ArgsLike for Vec<Val> {116 fn unnamed_len(&self) -> usize {117 self.len()118 }119 fn unnamed_iter(120 &self,121 _ctx: Context,122 _tailstrict: bool,123 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,124 ) -> Result<()> {125 for (idx, el) in self.iter().enumerate() {126 handler(idx, Thunk::evaluated(el.clone()))?;127 }128 Ok(())129 }130 fn named_iter(131 &self,132 _ctx: Context,133 _tailstrict: bool,134 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,135 ) -> Result<()> {136 Ok(())137 }138 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}139}140141impl ArgsLike for ArgsDesc {142 fn unnamed_len(&self) -> usize {143 self.unnamed.len()144 }145146 fn unnamed_iter(147 &self,148 ctx: Context,149 tailstrict: bool,150 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,151 ) -> Result<()> {152 for (id, arg) in self.unnamed.iter().enumerate() {153 handler(154 id,155 if tailstrict {156 Thunk::evaluated(evaluate(ctx.clone(), arg)?)157 } else {158 Thunk::new(EvaluateThunk {159 ctx: ctx.clone(),160 expr: arg.clone(),161 })162 },163 )?;164 }165 Ok(())166 }167168 fn named_iter(169 &self,170 ctx: Context,171 tailstrict: bool,172 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,173 ) -> Result<()> {174 for (name, arg) in &self.named {175 handler(176 name,177 if tailstrict {178 Thunk::evaluated(evaluate(ctx.clone(), arg)?)179 } else {180 Thunk::new(EvaluateThunk {181 ctx: ctx.clone(),182 expr: arg.clone(),183 })184 },185 )?;186 }187 Ok(())188 }189190 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {191 for (name, _) in &self.named {192 handler(name);193 }194 }195}196197impl<V: ArgLike, S> sealed::Named for HashMap<IStr, V, S> {}198impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {199 fn unnamed_len(&self) -> usize {200 0201 }202203 fn unnamed_iter(204 &self,205 _ctx: Context,206 _tailstrict: bool,207 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,208 ) -> Result<()> {209 Ok(())210 }211212 fn named_iter(213 &self,214 ctx: Context,215 tailstrict: bool,216 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,217 ) -> Result<()> {218 for (name, value) in self.iter() {219 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;220 }221 Ok(())222 }223224 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {225 for (name, _) in self.iter() {226 handler(name);227 }228 }229}230impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}231232impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {233 fn unnamed_len(&self) -> usize {234 self.0.unnamed_len()235 }236237 fn unnamed_iter(238 &self,239 ctx: Context,240 tailstrict: bool,241 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,242 ) -> Result<()> {243 self.0.unnamed_iter(ctx, tailstrict, handler)244 }245246 fn named_iter(247 &self,248 ctx: Context,249 tailstrict: bool,250 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,251 ) -> Result<()> {252 self.0.named_iter(ctx, tailstrict, handler)253 }254255 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {256 self.0.named_names(handler);257 }258}259260macro_rules! impl_args_like {261 ($count:expr; $($gen:ident)*) => {262 impl<$($gen: ArgLike,)*> sealed::Unnamed for ($($gen,)*) {}263 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {264 fn unnamed_len(&self) -> usize {265 $count266 }267 #[allow(non_snake_case, unused_assignments)]268 fn unnamed_iter(269 &self,270 ctx: Context,271 tailstrict: bool,272 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,273 ) -> Result<()> {274 let mut i = 0usize;275 let ($($gen,)*) = self;276 $(277 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;278 i+=1;279 )*280 Ok(())281 }282 fn named_iter(283 &self,284 _ctx: Context,285 _tailstrict: bool,286 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,287 ) -> Result<()> {288 Ok(())289 }290 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}291 }292 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}293294 impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {}295 impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {296 fn unnamed_len(&self) -> usize {297 0298 }299 fn unnamed_iter(300 &self,301 _ctx: Context,302 _tailstrict: bool,303 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,304 ) -> Result<()> {305 Ok(())306 }307 #[allow(non_snake_case)]308 fn named_iter(309 &self,310 ctx: Context,311 tailstrict: bool,312 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,313 ) -> Result<()> {314 let ($($gen,)*) = self;315 $(316 handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?;317 )*318 Ok(())319 }320 #[allow(non_snake_case)]321 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {322 let ($($gen,)*) = self;323 $(324 handler(&$gen.0);325 )*326 }327 }328 impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {}329 };330 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {331 impl_args_like!($count; $($cur)*);332 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);333 };334 ($count:expr; $($cur:ident)* @) => {335 impl_args_like!($count; $($cur)*);336 }337}338impl_args_like! {339 // First argument is already in position, so count starts from 1340 1usize; A @ B C D E F G H I J K L341}342343impl ArgsLike for () {344 fn unnamed_len(&self) -> usize {345 0346 }347348 fn unnamed_iter(349 &self,350 _ctx: Context,351 _tailstrict: bool,352 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,353 ) -> Result<()> {354 Ok(())355 }356357 fn named_iter(358 &self,359 _ctx: Context,360 _tailstrict: bool,361 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,362 ) -> Result<()> {363 Ok(())364 }365366 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}367}368impl OptionalContext for () {}1use hashbrown::HashMap;2use jrsonnet_gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};56use crate::{7 error::Result,8 evaluate,9 gc::GcHashMap,10 typed::Typed,11 val::{StrValue, ThunkValue},12 Context, Thunk, Val,13};1415/// Marker for arguments, which can be evaluated with context set to None16pub trait OptionalContext {}1718#[derive(Trace)]19struct EvaluateThunk {20 ctx: Context,21 expr: LocExpr,22}23impl ThunkValue for EvaluateThunk {24 type Output = Val;25 fn get(self: Box<Self>) -> Result<Val> {26 evaluate(self.ctx, &self.expr)27 }28}2930pub trait ArgLike {31 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;32}3334impl ArgLike for &LocExpr {35 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {36 Ok(if tailstrict {37 Thunk::evaluated(evaluate(ctx, self)?)38 } else {39 Thunk::new(EvaluateThunk {40 ctx,41 expr: (*self).clone(),42 })43 })44 }45}4647impl<T> ArgLike for T48where49 T: Typed + Clone,50{51 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {52 if T::provides_lazy() && !tailstrict {53 return Ok(T::into_lazy_untyped(self.clone()));54 }55 let val = T::into_untyped(self.clone())?;56 Ok(Thunk::evaluated(val))57 }58}59impl<T> OptionalContext for T where T: Typed + Clone {}6061#[derive(Clone, Trace)]62pub enum TlaArg {63 String(IStr),64 Code(LocExpr),65 Val(Val),66 Lazy(Thunk<Val>),67}68impl ArgLike for TlaArg {69 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {70 match self {71 TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(StrValue::Flat(s.clone())))),72 TlaArg::Code(code) => Ok(if tailstrict {73 Thunk::evaluated(evaluate(ctx, code)?)74 } else {75 Thunk::new(EvaluateThunk {76 ctx,77 expr: code.clone(),78 })79 }),80 TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),81 TlaArg::Lazy(lazy) => Ok(lazy.clone()),82 }83 }84}8586mod sealed {87 /// Implemented for `ArgsLike`, where only unnamed arguments present88 pub trait Unnamed {}89 /// Implemented for `ArgsLike`, where only named arguments present90 pub trait Named {}91}9293pub trait ArgsLike {94 fn unnamed_len(&self) -> usize;95 fn unnamed_iter(96 &self,97 ctx: Context,98 tailstrict: bool,99 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,100 ) -> Result<()>;101 fn named_iter(102 &self,103 ctx: Context,104 tailstrict: bool,105 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,106 ) -> Result<()>;107 fn named_names(&self, handler: &mut dyn FnMut(&IStr));108}109110impl ArgsLike for Vec<Val> {111 fn unnamed_len(&self) -> usize {112 self.len()113 }114 fn unnamed_iter(115 &self,116 _ctx: Context,117 _tailstrict: bool,118 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,119 ) -> Result<()> {120 for (idx, el) in self.iter().enumerate() {121 handler(idx, Thunk::evaluated(el.clone()))?;122 }123 Ok(())124 }125 fn named_iter(126 &self,127 _ctx: Context,128 _tailstrict: bool,129 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,130 ) -> Result<()> {131 Ok(())132 }133 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}134}135136impl ArgsLike for ArgsDesc {137 fn unnamed_len(&self) -> usize {138 self.unnamed.len()139 }140141 fn unnamed_iter(142 &self,143 ctx: Context,144 tailstrict: bool,145 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,146 ) -> Result<()> {147 for (id, arg) in self.unnamed.iter().enumerate() {148 handler(149 id,150 if tailstrict {151 Thunk::evaluated(evaluate(ctx.clone(), arg)?)152 } else {153 Thunk::new(EvaluateThunk {154 ctx: ctx.clone(),155 expr: arg.clone(),156 })157 },158 )?;159 }160 Ok(())161 }162163 fn named_iter(164 &self,165 ctx: Context,166 tailstrict: bool,167 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,168 ) -> Result<()> {169 for (name, arg) in &self.named {170 handler(171 name,172 if tailstrict {173 Thunk::evaluated(evaluate(ctx.clone(), arg)?)174 } else {175 Thunk::new(EvaluateThunk {176 ctx: ctx.clone(),177 expr: arg.clone(),178 })179 },180 )?;181 }182 Ok(())183 }184185 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {186 for (name, _) in &self.named {187 handler(name);188 }189 }190}191192impl<V: ArgLike, S> sealed::Named for HashMap<IStr, V, S> {}193impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {194 fn unnamed_len(&self) -> usize {195 0196 }197198 fn unnamed_iter(199 &self,200 _ctx: Context,201 _tailstrict: bool,202 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,203 ) -> Result<()> {204 Ok(())205 }206207 fn named_iter(208 &self,209 ctx: Context,210 tailstrict: bool,211 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,212 ) -> Result<()> {213 for (name, value) in self.iter() {214 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;215 }216 Ok(())217 }218219 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {220 for (name, _) in self.iter() {221 handler(name);222 }223 }224}225impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}226227impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {228 fn unnamed_len(&self) -> usize {229 self.0.unnamed_len()230 }231232 fn unnamed_iter(233 &self,234 ctx: Context,235 tailstrict: bool,236 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,237 ) -> Result<()> {238 self.0.unnamed_iter(ctx, tailstrict, handler)239 }240241 fn named_iter(242 &self,243 ctx: Context,244 tailstrict: bool,245 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,246 ) -> Result<()> {247 self.0.named_iter(ctx, tailstrict, handler)248 }249250 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {251 self.0.named_names(handler);252 }253}254255macro_rules! impl_args_like {256 ($count:expr; $($gen:ident)*) => {257 impl<$($gen: ArgLike,)*> sealed::Unnamed for ($($gen,)*) {}258 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {259 fn unnamed_len(&self) -> usize {260 $count261 }262 #[allow(non_snake_case, unused_assignments)]263 fn unnamed_iter(264 &self,265 ctx: Context,266 tailstrict: bool,267 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,268 ) -> Result<()> {269 let mut i = 0usize;270 let ($($gen,)*) = self;271 $(272 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;273 i+=1;274 )*275 Ok(())276 }277 fn named_iter(278 &self,279 _ctx: Context,280 _tailstrict: bool,281 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,282 ) -> Result<()> {283 Ok(())284 }285 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}286 }287 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}288289 impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {}290 impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {291 fn unnamed_len(&self) -> usize {292 0293 }294 fn unnamed_iter(295 &self,296 _ctx: Context,297 _tailstrict: bool,298 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,299 ) -> Result<()> {300 Ok(())301 }302 #[allow(non_snake_case)]303 fn named_iter(304 &self,305 ctx: Context,306 tailstrict: bool,307 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,308 ) -> Result<()> {309 let ($($gen,)*) = self;310 $(311 handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?;312 )*313 Ok(())314 }315 #[allow(non_snake_case)]316 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {317 let ($($gen,)*) = self;318 $(319 handler(&$gen.0);320 )*321 }322 }323 impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {}324 };325 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {326 impl_args_like!($count; $($cur)*);327 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);328 };329 ($count:expr; $($cur:ident)* @) => {330 impl_args_like!($count; $($cur)*);331 }332}333impl_args_like! {334 // First argument is already in position, so count starts from 1335 1usize; A @ B C D E F G H I J K L336}337338impl ArgsLike for () {339 fn unnamed_len(&self) -> usize {340 0341 }342343 fn unnamed_iter(344 &self,345 _ctx: Context,346 _tailstrict: bool,347 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,348 ) -> Result<()> {349 Ok(())350 }351352 fn named_iter(353 &self,354 _ctx: Context,355 _tailstrict: bool,356 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,357 ) -> Result<()> {358 Ok(())359 }360361 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}362}363impl OptionalContext for () {}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.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,6 +1,6 @@
-use std::ops::Deref;
+use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};
-use jrsonnet_gcmodule::Cc;
+use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::{IBytes, IStr};
pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
@@ -11,10 +11,28 @@
function::{native::NativeDesc, FuncDesc, FuncVal},
throw,
typed::CheckType,
- val::{IndexableVal, StrValue},
- ObjValue, ObjValueBuilder, Val,
+ val::{IndexableVal, StrValue, ThunkMapper},
+ ObjValue, ObjValueBuilder, Thunk, Val,
};
+#[derive(Trace)]
+struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);
+impl<K> ThunkMapper<Val> for FromUntyped<K>
+where
+ K: Typed + Trace,
+{
+ type Output = K;
+
+ fn map(self, from: Val) -> Result<Self::Output> {
+ K::from_untyped(from)
+ }
+}
+impl<K: Trace> Default for FromUntyped<K> {
+ fn default() -> Self {
+ Self(PhantomData)
+ }
+}
+
pub trait TypedObj: Typed {
fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
fn parse(obj: &ObjValue) -> Result<Self>;
@@ -28,8 +46,24 @@
pub trait Typed: Sized {
const TYPE: &'static ComplexValType;
fn into_untyped(typed: Self) -> Result<Val>;
+ fn into_lazy_untyped(typed: Self) -> Thunk<Val> {
+ Thunk::from(Self::into_untyped(typed))
+ }
fn from_untyped(untyped: Val) -> Result<Self>;
+ fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {
+ Self::from_untyped(lazy.evaluate()?)
+ }
+
+ // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`
+ fn provides_lazy() -> bool {
+ false
+ }
+ // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible
+ fn wants_lazy() -> bool {
+ false
+ }
+
/// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result
/// This method returns identity in impl Typed for Result, and should not be overriden
#[doc(hidden)]
@@ -39,6 +73,54 @@
}
}
+impl<T> Typed for Thunk<T>
+where
+ T: Typed + Trace + Clone,
+{
+ const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);
+
+ fn into_untyped(typed: Self) -> Result<Val> {
+ T::into_untyped(typed.evaluate()?)
+ }
+
+ fn from_untyped(untyped: Val) -> Result<Self> {
+ Self::from_lazy_untyped(Thunk::evaluated(untyped))
+ }
+
+ fn provides_lazy() -> bool {
+ true
+ }
+
+ fn into_lazy_untyped(inner: Self) -> Thunk<Val> {
+ #[derive(Trace)]
+ struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);
+ impl<K> ThunkMapper<K> for IntoUntyped<K>
+ where
+ K: Typed + Trace,
+ {
+ type Output = Val;
+
+ fn map(self, from: K) -> Result<Self::Output> {
+ K::into_untyped(from)
+ }
+ }
+ impl<K: Trace> Default for IntoUntyped<K> {
+ fn default() -> Self {
+ Self(PhantomData)
+ }
+ }
+ inner.map(<IntoUntyped<T>>::default())
+ }
+
+ fn wants_lazy() -> bool {
+ true
+ }
+
+ fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {
+ Ok(inner.map(<FromUntyped<T>>::default()))
+ }
+}
+
const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;
macro_rules! impl_int {
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(())
}