git.delta.rocks / jrsonnet / refs/commits / 7cdcae351387

difftreelog

feat simplify Thunk creation with closure syntax

Yaroslav Bolyukin2024-08-26parent: #7d331b6.patch.diff
in: master

9 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
7use super::ArrValue;7use super::ArrValue;
8use crate::{8use crate::{
9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,
10 val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,10 Context, Error, ObjValue, Result, Thunk, Val,
11};11};
1212
13pub trait ArrayLike: Any + Trace + Debug {13pub trait ArrayLike: Any + Trace + Debug {
182 Ok(Some(new_value))182 Ok(Some(new_value))
183 }183 }
184 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {184 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
185 #[derive(Trace)]
186 struct ArrayElement {
187 arr_thunk: ExprArray,
188 index: usize,
189 }
190
191 impl ThunkValue for ArrayElement {
192 type Output = Val;
193
194 fn get(self: Box<Self>) -> Result<Self::Output> {
195 self.arr_thunk
196 .get(self.index)
197 .transpose()
198 .expect("index checked")
199 }
200 }
201
202 if index >= self.len() {185 if index >= self.len() {
203 return None;186 return None;
208 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}191 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
209 };192 };
210193
194 let arr_thunk = self.clone();
211 Some(Thunk::new(ArrayElement {195 Some(Thunk!(move || {
212 arr_thunk: self.clone(),196 arr_thunk.get(index).transpose().expect("index checked")
213 index,
214 }))197 }))
215 }198 }
216 fn get_cheap(&self, _index: usize) -> Option<Val> {199 fn get_cheap(&self, _index: usize) -> Option<Val> {
492 Ok(Some(new_value))475 Ok(Some(new_value))
493 }476 }
494 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {477 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
495 #[derive(Trace)]
496 struct ArrayElement<const WITH_INDEX: bool> {
497 arr_thunk: MappedArray<WITH_INDEX>,
498 index: usize,
499 }
500
501 impl<const WITH_INDEX: bool> ThunkValue for ArrayElement<WITH_INDEX> {
502 type Output = Val;
503
504 fn get(self: Box<Self>) -> Result<Self::Output> {
505 self.arr_thunk
506 .get(self.index)
507 .transpose()
508 .expect("index checked")
509 }
510 }
511
512 if index >= self.len() {478 if index >= self.len() {
513 return None;479 return None;
518 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}484 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}
519 };485 };
520486
487 let arr_thunk = self.clone();
521 Some(Thunk::new(ArrayElement {488 Some(Thunk!(move || {
522 arr_thunk: self.clone(),489 arr_thunk.get(index).transpose().expect("index checked")
523 index,
524 }))490 }))
525 }491 }
526492
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
1use jrsonnet_gcmodule::Trace;
2use jrsonnet_interner::IStr;1use jrsonnet_interner::IStr;
3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};2use jrsonnet_parser::{BindSpec, Destruct};
43
5use crate::{4use crate::{
6 bail,5 bail,
7 error::{ErrorKind::*, Result},6 error::{ErrorKind::*, Result},
8 evaluate, evaluate_method, evaluate_named,7 evaluate, evaluate_method, evaluate_named,
9 gc::GcHashMap,8 gc::GcHashMap,
10 val::ThunkValue,
11 Context, Pending, Thunk, Val,9 Context, Pending, Thunk, Val,
12};10};
1311
32 Destruct::Array { start, rest, end } => {30 Destruct::Array { start, rest, end } => {
33 use jrsonnet_parser::DestructRest;31 use jrsonnet_parser::DestructRest;
3432
35 use crate::arr::ArrValue;33 let min_len = start.len() + end.len();
3634 let has_rest = rest.is_some();
37 #[derive(Trace)]
38 struct DataThunk {
39 parent: Thunk<Val>,
40 min_len: usize,
41 has_rest: bool,
42 }
43 impl ThunkValue for DataThunk {
44 type Output = ArrValue;35 let full = Thunk!(move || {
45
46 fn get(self: Box<Self>) -> Result<Self::Output> {
47 let v = self.parent.evaluate()?;36 let v = parent.evaluate()?;
48 let Val::Arr(arr) = v else {37 let Val::Arr(arr) = v else {
49 bail!("expected array");38 bail!("expected array");
50 };39 };
51 if !self.has_rest {40 if !has_rest {
52 if arr.len() != self.min_len {41 if arr.len() != min_len {
53 bail!("expected {} elements, got {}", self.min_len, arr.len())42 bail!("expected {} elements, got {}", min_len, arr.len())
54 }43 }
55 } else if arr.len() < self.min_len {44 } else if arr.len() < min_len {
56 bail!(45 bail!(
57 "expected at least {} elements, but array was only {}",46 "expected at least {} elements, but array was only {}",
58 self.min_len,47 min_len,
59 arr.len()48 arr.len()
60 )49 )
61 }50 }
62 Ok(arr)51 Ok(arr)
63 }52 });
64 }
65
66 let full = Thunk::new(DataThunk {
67 min_len: start.len() + end.len(),
68 has_rest: rest.is_some(),
69 parent,
70 });
7153
72 {54 {
73 #[derive(Trace)]
74 struct BaseThunk {
75 full: Thunk<ArrValue>,
76 index: usize,
77 }
78 impl ThunkValue for BaseThunk {
79 type Output = Val;
80
81 fn get(self: Box<Self>) -> Result<Self::Output> {
82 let full = self.full.evaluate()?;
83 Ok(full.get(self.index)?.expect("length is checked"))
84 }
85 }
86 for (i, d) in start.iter().enumerate() {55 for (i, d) in start.iter().enumerate() {
56 let full = full.clone();
87 destruct(57 destruct(
88 d,58 d,
89 Thunk::new(BaseThunk {59 Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),
90 full: full.clone(),
91 index: i,
92 }),
93 fctx.clone(),60 fctx.clone(),
94 new_bindings,61 new_bindings,
95 )?;62 )?;
9865
99 match rest {66 match rest {
100 Some(DestructRest::Keep(v)) => {67 Some(DestructRest::Keep(v)) => {
101 #[derive(Trace)]
102 struct RestThunk {
103 full: Thunk<ArrValue>,
104 start: usize,68 let start = start.len();
105 end: usize,
106 }
107 impl ThunkValue for RestThunk {
108 type Output = Val;
109
110 fn get(self: Box<Self>) -> Result<Self::Output> {
111 let full = self.full.evaluate()?;69 let end = end.len();
112 let to = full.len() - self.end;70 let full = full.clone();
113 Ok(Val::Arr(full.slice(
114 Some(self.start as i32),
115 Some(to as i32),
116 None,
117 )))
118 }
119 }
120
121 destruct(71 destruct(
122 &Destruct::Full(v.clone()),72 &Destruct::Full(v.clone()),
123 Thunk::new(RestThunk {73 Thunk!(move || {
124 full: full.clone(),74 let full = full.evaluate()?;
125 start: start.len(),75 let to = full.len() - end;
126 end: end.len(),76 Ok(Val::Arr(full.slice(
77 Some(start as i32),
78 Some(to as i32),
79 None,
80 )))
127 }),81 }),
128 fctx.clone(),82 fctx.clone(),
129 new_bindings,83 new_bindings,
133 }87 }
13488
135 {89 {
136 #[derive(Trace)]
137 struct EndThunk {
138 full: Thunk<ArrValue>,
139 index: usize,
140 end: usize,
141 }
142 impl ThunkValue for EndThunk {
143 type Output = Val;
144
145 fn get(self: Box<Self>) -> Result<Self::Output> {
146 let full = self.full.evaluate()?;
147 Ok(full
148 .get(full.len() - self.end + self.index)?
149 .expect("length is checked"))
150 }
151 }
152 for (i, d) in end.iter().enumerate() {90 for (i, d) in end.iter().enumerate() {
153 destruct(
154 d,
155 Thunk::new(EndThunk {
156 full: full.clone(),91 let full = full.clone();
157 index: i,
158 end: end.len(),92 let end = end.len();
93 destruct(
94 d,
95 Thunk!(move || {
96 let full = full.evaluate()?;
97 Ok(full.get(full.len() - end + i)?.expect("length is checked"))
159 }),98 }),
160 fctx.clone(),99 fctx.clone(),
161 new_bindings,100 new_bindings,
162 )?;101 )?;
163 }102 }
164 }103 }
165 }104 }
166 #[cfg(feature = "exp-destruct")]105 #[cfg(feature = "exp-destruct")]
167 Destruct::Object { fields, rest } => {106 Destruct::Object { fields, rest } => {
168 use crate::obj::ObjValue;
169
170 #[derive(Trace)]
171 struct DataThunk {
172 parent: Thunk<Val>,
173 field_names: Vec<(IStr, bool)>,107 let field_names: Vec<_> = fields
108 .iter()
174 has_rest: bool,109 .map(|f| (f.0.clone(), f.2.is_some()))
175 }110 .collect();
176 impl ThunkValue for DataThunk {
177 type Output = ObjValue;111 let has_rest = rest.is_some();
178
179 fn get(self: Box<Self>) -> Result<Self::Output> {112 let full = Thunk!(move || {
180 let v = self.parent.evaluate()?;113 let v = parent.evaluate()?;
181 let Val::Obj(obj) = v else {114 let Val::Obj(obj) = v else {
182 bail!("expected object");115 bail!("expected object");
183 };116 };
184 for (field, has_default) in &self.field_names {117 for (field, has_default) in &field_names {
185 if !has_default && !obj.has_field_ex(field.clone(), true) {118 if !has_default && !obj.has_field_ex(field.clone(), true) {
186 bail!("missing field: {field}");119 bail!("missing field: {field}");
187 }120 }
188 }121 }
189 if !self.has_rest {122 if !has_rest {
190 let len = obj.len();123 let len = obj.len();
191 if len > self.field_names.len() {124 if len > field_names.len() {
192 bail!("too many fields, and rest not found");125 bail!("too many fields, and rest not found");
193 }126 }
194 }127 }
195 Ok(obj)128 Ok(obj)
196 }129 });
197 }
198 let field_names: Vec<_> = fields
199 .iter()
200 .map(|f| (f.0.clone(), f.2.is_some()))
201 .collect();
202 let full = Thunk::new(DataThunk {
203 parent,
204 field_names,
205 has_rest: rest.is_some(),
206 });
207130
208 for (field, d, default) in fields {131 for (field, d, default) in fields {
209 #[derive(Trace)]132 let default = default.clone().map(|e| (fctx.clone(), e));
210 struct FieldThunk {
211 full: Thunk<ObjValue>,
212 field: IStr,
213 default: Option<(Pending<Context>, LocExpr)>,
214 }
215 impl ThunkValue for FieldThunk {133 let value = {
216 type Output = Val;134 let field = field.clone();
217135 let full = full.clone();
218 fn get(self: Box<Self>) -> Result<Self::Output> {136 Thunk!(move || {
219 let full = self.full.evaluate()?;137 let full = full.evaluate()?;
220 if let Some(field) = full.get(self.field)? {138 if let Some(field) = full.get(field)? {
221 Ok(field)139 Ok(field)
222 } else {140 } else {
223 let (fctx, expr) = self.default.as_ref().expect("shape is checked");141 let (fctx, expr) = default.as_ref().expect("shape is checked");
224 Ok(evaluate(fctx.clone().unwrap(), expr)?)142 Ok(evaluate(fctx.clone().unwrap(), expr)?)
225 }143 }
226 }144 })
227 }145 };
228 let value = Thunk::new(FieldThunk {146
229 full: full.clone(),
230 field: field.clone(),
231 default: default.clone().map(|e| (fctx.clone(), e)),
232 });
233 if let Some(d) = d {147 if let Some(d) = d {
234 destruct(d, value, fctx.clone(), new_bindings)?;148 destruct(d, value, fctx.clone(), new_bindings)?;
235 } else {149 } else {
253) -> Result<()> {167) -> Result<()> {
254 match d {168 match d {
255 BindSpec::Field { into, value } => {169 BindSpec::Field { into, value } => {
256 #[derive(Trace)]
257 struct EvaluateThunkValue {
258 name: Option<IStr>,170 let name = into.name();
259 fctx: Pending<Context>,171 let value = value.clone();
260 expr: LocExpr,
261 }
262 impl ThunkValue for EvaluateThunkValue {172 let data = {
263 type Output = Val;173 let fctx = fctx.clone();
264 fn get(self: Box<Self>) -> Result<Self::Output> {
265 self.name.map_or_else(174 Thunk!(move || name.map_or_else(
266 || evaluate(self.fctx.unwrap(), &self.expr),175 || evaluate(fctx.unwrap(), &value),
267 |name| evaluate_named(self.fctx.unwrap(), &self.expr, name),176 |name| evaluate_named(fctx.unwrap(), &value, name),
268 )177 ))
269 }
270 }178 };
271 let data = Thunk::new(EvaluateThunkValue {
272 name: into.name(),
273 fctx: fctx.clone(),
274 expr: value.clone(),
275 });
276 destruct(into, data, fctx, new_bindings)?;179 destruct(into, data, fctx, new_bindings)?;
277 }180 }
278 BindSpec::Function {181 BindSpec::Function {
279 name,182 name,
280 params,183 params,
281 value,184 value,
282 } => {185 } => {
283 #[derive(Trace)]
284 struct MethodThunk {
285 fctx: Pending<Context>,
286 name: IStr,
287 params: ParamsDesc,186 let params = params.clone();
288 value: LocExpr,
289 }
290 impl ThunkValue for MethodThunk {
291 type Output = Val;187 let name = name.clone();
292
293 fn get(self: Box<Self>) -> Result<Self::Output> {
294 Ok(evaluate_method(
295 self.fctx.unwrap(),188 let value = value.clone();
296 self.name,
297 self.params,
298 self.value,
299 ))
300 }
301 }
302
303 let old = new_bindings.insert(189 let old = new_bindings.insert(name.clone(), {
304 name.clone(),
305 Thunk::new(MethodThunk {
306 fctx,
307 name: name.clone(),190 let name = name.clone();
308 params: params.clone(),191 Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))
309 value: value.clone(),
310 }),192 });
311 );
312 if old.is_some() {193 if old.is_some() {
313 bail!(DuplicateLocalVar(name.clone()))194 bail!(DuplicateLocalVar(name))
314 }195 }
315 }196 }
316 }197 }
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
18 function::{CallLocation, FuncDesc, FuncVal},18 function::{CallLocation, FuncDesc, FuncVal},
19 in_frame,19 in_frame,
20 typed::Typed,20 typed::Typed,
21 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk, ThunkValue},21 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},
22 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,22 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
23 ResultExt, Unbound, Val,23 ResultExt, Unbound, Val,
24};24};
139 #[cfg(feature = "exp-preserve-order")]139 #[cfg(feature = "exp-preserve-order")]
140 false,140 false,
141 ) {141 ) {
142 #[derive(Trace)]
143 struct ObjectFieldThunk {
144 obj: ObjValue,
145 field: IStr,
146 }
147 impl ThunkValue for ObjectFieldThunk {
148 type Output = Val;
149
150 fn get(self: Box<Self>) -> Result<Self::Output> {
151 self.obj.get(self.field).transpose().expect(
152 "field exists, as field name was obtained from object.fields()",
153 )
154 }
155 }
156
157 let fctx = Pending::new();142 let fctx = Pending::new();
158 let mut new_bindings = GcHashMap::with_capacity(var.capacity_hint());143 let mut new_bindings = GcHashMap::with_capacity(var.capacity_hint());
144 let obj = obj.clone();
159 let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(vec![145 let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(vec![
160 Thunk::evaluated(Val::string(field.clone())),146 Thunk::evaluated(Val::string(field.clone())),
161 Thunk::new(ObjectFieldThunk {147 Thunk!(move || obj.get(field).transpose().expect(
162 field: field.clone(),148 "field exists, as field name was obtained from object.fields()",
163 obj: obj.clone(),149 )),
164 }),
165 ])));150 ])));
166 destruct(var, value, fctx.clone(), &mut new_bindings)?;151 destruct(var, value, fctx.clone(), &mut new_bindings)?;
167 let ctx = ctx152 let ctx = ctx
609 if items.is_empty() {594 if items.is_empty() {
610 Val::Arr(ArrValue::empty())595 Val::Arr(ArrValue::empty())
611 } else if items.len() == 1 {596 } else if items.len() == 1 {
612 #[derive(Trace)]
613 struct ArrayElement {
614 ctx: Context,
615 item: LocExpr,597 let item = items[0].clone();
616 }
617 impl ThunkValue for ArrayElement {
618 type Output = Val;
619 fn get(self: Box<Self>) -> Result<Val> {
620 evaluate(self.ctx, &self.item)
621 }
622 }
623 Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {598 Val::Arr(ArrValue::lazy(vec![Thunk!(move || evaluate(ctx, &item))]))
624 ctx,
625 item: items[0].clone(),
626 })]))
627 } else {599 } else {
628 Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))600 Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
629 }601 }
630 }602 }
631 ArrComp(expr, comp_specs) => {603 ArrComp(expr, comp_specs) => {
632 let mut out = Vec::new();604 let mut out = Vec::new();
633 evaluate_comp(ctx, comp_specs, &mut |ctx| {605 evaluate_comp(ctx, comp_specs, &mut |ctx| {
634 #[derive(Trace)]
635 struct EvaluateThunk {
636 ctx: Context,
637 expr: LocExpr,606 let expr = expr.clone();
638 }
639 impl ThunkValue for EvaluateThunk {
640 type Output = Val;
641 fn get(self: Box<Self>) -> Result<Val> {
642 evaluate(self.ctx, &self.expr)
643 }
644 }
645 out.push(Thunk::new(EvaluateThunk {607 out.push(Thunk!(move || evaluate(ctx, &expr)));
646 ctx,
647 expr: expr.clone(),
648 }));
649 Ok(())608 Ok(())
650 })?;609 })?;
651 Val::Arr(ArrValue::lazy(out))610 Val::Arr(ArrValue::lazy(out))
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
232/// Creates Context, which has all argument default values applied233/// Creates Context, which has all argument default values applied
233/// and with unbound values causing error to be returned234/// and with unbound values causing error to be returned
234pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {235pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
235 #[derive(Trace)]
236 struct DependsOnUnbound(IStr, ParamsDesc);
237 impl ThunkValue for DependsOnUnbound {
238 type Output = Val;
239 fn get(self: Box<Self>) -> Result<Val> {
240 Err(FunctionParameterNotBoundInCall(
241 Some(self.0.clone()),
242 self.1
243 .iter()
244 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))
245 .collect(),
246 )
247 .into())
248 }
249 }
250
251 let fctx = Context::new_future();236 let fctx = Context::new_future();
252237
267 } else {252 } else {
268 destruct(253 destruct(
269 &param.0,254 &param.0,
270 Thunk::new(DependsOnUnbound(255 {
271 param.0.name().unwrap_or_else(|| "<destruct>".into()),256 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());
272 params.clone(),257 let params = params.clone();
258 Thunk!(move || Err(FunctionParameterNotBoundInCall(
259 Some(param_name),
260 params
261 .iter()
262 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))
263 .collect(),
264 )
265 .into()))
273 )),266 },
274 fctx.clone(),267 fctx.clone(),
275 &mut bindings,268 &mut bindings,
276 )?;269 )?;
modifiedcrates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth
159 }159 }
160}160}
161
162pub fn assert_trace<T: Trace>(_v: &T) {}
161163
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
20 in_frame,20 in_frame,
21 operator::evaluate_add_op,21 operator::evaluate_add_op,
22 tb,22 tb,
23 val::{ArrValue, ThunkValue},23 val::ArrValue,
24 MaybeUnbound, Result, Thunk, Unbound, Val,24 MaybeUnbound, Result, Thunk, Unbound, Val,
25};25};
2626
444 })444 })
445 }445 }
446 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {446 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {
447 #[derive(Trace)]
448 struct ThunkGet {
449 obj: ObjValue,
450 key: IStr,
451 }
452 impl ThunkValue for ThunkGet {
453 type Output = Val;
454
455 fn get(self: Box<Self>) -> Result<Self::Output> {
456 Ok(self.obj.get(self.key)?.expect("field exists"))
457 }
458 }
459
460 if !self.has_field_ex(key.clone(), true) {447 if !self.has_field_ex(key.clone(), true) {
461 return None;448 return None;
462 }449 }
463 Some(Thunk::new(ThunkGet {
464 obj: self.clone(),450 let obj = self.clone();
451
465 key,452 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))
466 }))
467 }453 }
468 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {454 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {
469 #[derive(Trace)]
470 struct ThunkGet {
471 obj: ObjValue,455 let obj = self.clone();
472 key: IStr,
473 }
474 impl ThunkValue for ThunkGet {
475 type Output = Val;
476
477 fn get(self: Box<Self>) -> Result<Self::Output> {
478 self.obj.get_or_bail(self.key)
479 }
480 }
481
482 Thunk::new(ThunkGet {456 Thunk!(move || obj.get_or_bail(key))
483 obj: self.clone(),
484 key,
485 })
486 }457 }
487 pub fn ptr_eq(a: &Self, b: &Self) -> bool {458 pub fn ptr_eq(a: &Self, b: &Self) -> bool {
488 Cc::ptr_eq(&a.0, &b.0)459 Cc::ptr_eq(&a.0, &b.0)
733 self.value_cache704 self.value_cache
734 .borrow_mut()705 .borrow_mut()
735 .insert(cache_key.clone(), CacheValue::Pending);706 .insert(cache_key.clone(), CacheValue::Pending);
736 let value = self.get_for_uncached(key, this).map_err(|e| {707 let value = self.get_for_uncached(key, this).inspect_err(|e| {
737 self.value_cache708 self.value_cache
738 .borrow_mut()709 .borrow_mut()
739 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));710 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));
740 e
741 })?;711 })?;
742 self.value_cache.borrow_mut().insert(712 self.value_cache.borrow_mut().insert(
743 cache_key,713 cache_key,
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
11use derivative::Derivative;11use derivative::Derivative;
12use jrsonnet_gcmodule::{Cc, Trace};12use jrsonnet_gcmodule::{Cc, Trace};
13use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;
14pub use jrsonnet_macros::Thunk;
14use jrsonnet_types::ValType;15use jrsonnet_types::ValType;
15use thiserror::Error;16use thiserror::Error;
1617
31 fn get(self: Box<Self>) -> Result<Self::Output>;32 fn get(self: Box<Self>) -> Result<Self::Output>;
32}33}
34
35#[derive(Trace)]
36pub struct ThunkValueClosure<D: Trace, O: 'static> {
37 env: D,
38 // Carries no data, as it is not a real closure, all the
39 // captured environment is stored in `env` field.
40 #[trace(skip)]
41 closure: fn(D) -> Result<O>,
42}
43impl<D: Trace, O: 'static> ThunkValueClosure<D, O> {
44 pub fn new(env: D, closure: fn(D) -> Result<O>) -> Self {
45 Self { env, closure }
46 }
47}
48impl<D: Trace, O: 'static> ThunkValue for ThunkValueClosure<D, O> {
49 type Output = O;
50
51 fn get(self: Box<Self>) -> Result<Self::Output> {
52 (self.closure)(self.env)
53 }
54}
3355
34#[derive(Trace)]56#[derive(Trace)]
35enum ThunkInner<T: Trace> {57enum ThunkInner<T: Trace> {
113 M: ThunkMapper<Input>,135 M: ThunkMapper<Input>,
114 M::Output: Trace,136 M::Output: Trace,
115 {137 {
116 #[derive(Trace)]
117 struct Mapped<Input: Trace, Mapper: Trace> {
118 inner: Thunk<Input>,
119 mapper: Mapper,
120 }
121 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>
122 where
123 Input: Trace + Clone,
124 Mapper: ThunkMapper<Input>,
125 {
126 type Output = Mapper::Output;138 let inner = self;
127
128 fn get(self: Box<Self>) -> Result<Self::Output> {139 Thunk!(move || {
129 let value = self.inner.evaluate()?;140 let value = inner.evaluate()?;
130 let mapped = self.mapper.map(value)?;141 let mapped = mapper.map(value)?;
131 Ok(mapped)142 Ok(mapped)
132 }143 })
133 }
134
135 Thunk::new(Mapped::<Input, M> {
136 inner: self,
137 mapper,
138 })
139 }144 }
140}145}
141146
modifiedcrates/jrsonnet-macros/Cargo.tomldiffbeforeafterboth
17proc-macro2.workspace = true17proc-macro2.workspace = true
18quote.workspace = true18quote.workspace = true
19syn = { workspace = true, features = ["full"] }19syn = { workspace = true, features = ["full"] }
20syn-dissect-closure.workspace = true
2021
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
1use std::string::String;1use std::string::String;
22
3use proc_macro2::TokenStream;3use proc_macro2::TokenStream;
4use quote::quote;4use quote::{quote, quote_spanned};
5use syn::{5use syn::{
6 parenthesized,6 parenthesized,
7 parse::{Parse, ParseStream},7 parse::{Parse, ParseStream},
8 parse_macro_input,8 parse_macro_input,
9 punctuated::Punctuated,9 punctuated::Punctuated,
10 spanned::Spanned,10 spanned::Spanned,
11 token::{self, Comma},11 token::{self, Comma},
12 Attribute, DeriveInput, Error, Expr, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,12 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,
13 PathArguments, Result, ReturnType, Token, Type,13 LitStr, Pat, Path, PathArguments, Result, ReturnType, Token, Type,
14};14};
1515
816 input.expand().into()816 input.expand().into()
817}817}
818
819/// Create Thunk using closure syntax
820#[proc_macro]
821#[allow(non_snake_case)]
822pub fn Thunk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
823 let input = parse_macro_input!(input as ExprClosure);
824
825 let span = input.inputs.span();
826 let move_check = input.capture.is_none().then(|| {
827 quote_spanned! {span => {
828 compile_error!("Thunk! needs to be called with move closure");
829 }}
830 });
831
832 let (env, closure, args) = syn_dissect_closure::split_env(input);
833
834 let trace_check = args.iter().map(|el| {
835 let span = el.span();
836 quote_spanned! {span => ::jrsonnet_evaluator::gc::assert_trace(&#el);}
837 });
838
839 quote! {{
840 #move_check
841 #(#trace_check)*
842 ::jrsonnet_evaluator::Thunk::new(::jrsonnet_evaluator::val::ThunkValueClosure::new(#env, #closure))
843 }}.into()
844}
818845