git.delta.rocks / jrsonnet / refs/commits / a2182e096977

difftreelog

feat forObj evaluation

pvwvukvwYaroslav Bolyukin2026-05-06parent: #30703de.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth
410 /// Is `over` does not depend on any variable introduced by an earlier for-spec in this comprehension chain410 /// Is `over` does not depend on any variable introduced by an earlier for-spec in this comprehension chain
411 loop_invariant: bool,411 loop_invariant: bool,
412 },412 },
413 #[cfg(feature = "exp-object-iteration")]
414 ForObj {
415 frame_shape: ClosureShape,
416 key: LocalSlot,
417 visibility: jrsonnet_ir::Visibility,
418 value: LDestruct,
419 over: LExpr,
420 loop_invariant: bool,
421 },
413}422}
414423
415struct FrameAlloc<'s> {424struct FrameAlloc<'s> {
1863 (r, rest)1872 (r, rest)
1864 }1873 }
1865 #[cfg(feature = "exp-object-iteration")]1874 #[cfg(feature = "exp-object-iteration")]
1866 CompSpec::ForObjSpec(_) => todo!(),1875 CompSpec::ForObjSpec(data) => {
1876 let mut over_taint = AnalysisResult::default();
1877 let over_l = analyze(&data.over, stack, &mut over_taint);
1878 let loop_invariant = over_taint.local_dependent_depth > outer_depth;
1879 taint.taint_by(over_taint);
1880
1881 let mut alloc = FrameAlloc::new(stack);
1882 let closure = alloc.push_locals_closure();
1883 let Some((_, key_slot)) = alloc.define_local(data.key.clone(), None) else {
1884 stack.pop_closure(closure);
1885 return go(idx + 1, specs, outer_depth, stack, taint, inside);
1886 };
1887 let Some(l_value) = alloc.alloc_destruct(&data.value) else {
1888 stack.pop_closure(closure);
1889 return go(idx + 1, specs, outer_depth, stack, taint, inside);
1890 };
1891 let mut pending = alloc.finish();
1892
1893 let var_analysis = AnalysisResult::default();
1894 pending.record_spec_init(&LDestruct::Full(key_slot), var_analysis);
1895 pending.record_spec_init(&l_value, var_analysis);
1896
1897 let body_frame = pending.finish();
1898 let (r, mut rest) =
1899 go(idx + 1, specs, outer_depth, body_frame.stack, taint, inside);
1900 body_frame.finish();
1901 let frame_shape = stack.pop_closure(closure);
1902
1903 rest.insert(
1904 0,
1905 LCompSpec::ForObj {
1906 frame_shape,
1907 key: key_slot,
1908 visibility: data.visibility,
1909 value: l_value,
1910 over: over_l,
1911 loop_invariant,
1912 },
1913 );
1914 (r, rest)
1915 }
1867 }1916 }
1868 }1917 }
1869 let outer_depth = stack.depth;1918 let outer_depth = stack.depth;
1970 use super::*;2019 use super::*;
19712020
1972 #[test]2021 #[test]
2022 #[cfg(not(feature = "exp-null-coaelse"))]
1973 fn snapshots() {2023 fn snapshots() {
1974 glob!("analysis_tests/*.jsonnet", |path| {2024 glob!("analysis_tests/*.jsonnet", |path| {
1975 let code = fs::read_to_string(path).expect("read test file");2025 let code = fs::read_to_string(path).expect("read test file");
modifiedcrates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth
6 destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},6 destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},
7 evaluate_field_member_static, evaluate_field_member_unbound,7 evaluate_field_member_static, evaluate_field_member_unbound,
8};8};
9#[cfg(feature = "exp-object-iteration")]
10use jrsonnet_interner::IStr;
11
9use crate::{12use crate::{
10 Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,13 Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,
18 evaluate::{evaluate, evaluate_trivial},21 evaluate::{evaluate, evaluate_trivial},
19};22};
23
24enum CachedOver {
25 Arr(ArrValue),
26 #[cfg(feature = "exp-object-iteration")]
27 Obj(ObjValue),
28}
2029
21trait CompCollector {30trait CompCollector {
22 fn reserve(&mut self, _guaranteed: usize) {}31 fn reserve(&mut self, _guaranteed: usize) {}
215 Ok(Val::arr(items))224 Ok(Val::arr(items))
216}225}
217226
218fn cache_overs(ctx: &Context, specs: &[LCompSpec]) -> Result<Vec<Option<ArrValue>>> {227fn cache_overs(ctx: &Context, specs: &[LCompSpec]) -> Result<Vec<Option<CachedOver>>> {
219 specs228 specs
220 .iter()229 .iter()
221 .map(|spec| {230 .map(|spec| {
229 let Val::Arr(arr) = val else {238 let Val::Arr(arr) = val else {
230 bail!(InComprehensionCanOnlyIterateOverArray)239 bail!(InComprehensionCanOnlyIterateOverArray)
231 };240 };
232 Some(arr)241 Some(CachedOver::Arr(arr))
233 }242 }
243 #[cfg(feature = "exp-object-iteration")]
244 LCompSpec::ForObj {
245 over,
246 loop_invariant: true,
247 ..
248 } => {
249 let val = evaluate(ctx.clone(), over)?;
250 let Val::Obj(obj) = val else {
251 bail!(TypeMismatch(
252 "object iteration over",
253 vec![jrsonnet_types::ValType::Obj],
254 val.value_type(),
255 ))
256 };
257 Some(CachedOver::Obj(obj))
258 }
234 _ => None,259 _ => None,
235 })260 })
236 })261 })
240fn evaluate_compspecs_eager(265fn evaluate_compspecs_eager(
241 ctx: Context,266 ctx: Context,
242 specs: &[LCompSpec],267 specs: &[LCompSpec],
243 cached_overs: &[Option<ArrValue>],268 cached_overs: &[Option<CachedOver>],
244 idx: usize,269 idx: usize,
245 guaranteed_reserve: usize,270 guaranteed_reserve: usize,
246 collector: &mut dyn CompCollector,271 collector: &mut dyn CompCollector,
269 over,294 over,
270 ..295 ..
271 } => {296 } => {
272 let arr = if let Some(cached) = &cached_overs[idx] {297 let arr = if let Some(CachedOver::Arr(cached)) = &cached_overs[idx] {
273 cached.clone()298 cached.clone()
274 } else {299 } else {
275 let arr_val = evaluate(ctx.clone(), over)?;300 let arr_val = evaluate(ctx.clone(), over)?;
304 _ => unreachable!("eager compspecs are not possible with non-full patterns"),329 _ => unreachable!("eager compspecs are not possible with non-full patterns"),
305 }330 }
306 }331 }
332 #[cfg(feature = "exp-object-iteration")]
333 LCompSpec::ForObj { .. } => {
334 unreachable!("eager compspecs filter rejects ForObj");
335 }
307 }336 }
308 Ok(())337 Ok(())
309}338}
310339
311fn evaluate_compspecs(340fn evaluate_compspecs(
312 ctx: Context,341 ctx: Context,
313 specs: &[LCompSpec],342 specs: &[LCompSpec],
314 cached_overs: &[Option<ArrValue>],343 cached_overs: &[Option<CachedOver>],
315 idx: usize,344 idx: usize,
316 guaranteed_reserve: usize,345 guaranteed_reserve: usize,
317 collector: &mut dyn CompCollector,346 collector: &mut dyn CompCollector,
340 over,369 over,
341 ..370 ..
342 } => {371 } => {
343 let arr = if let Some(cached) = &cached_overs[idx] {372 let arr = if let Some(CachedOver::Arr(cached)) = &cached_overs[idx] {
344 cached.clone()373 cached.clone()
345 } else {374 } else {
346 let arr_val = evaluate(ctx.clone(), over)?;375 let arr_val = evaluate(ctx.clone(), over)?;
365 )?;394 )?;
366 }395 }
367 }396 }
397 #[cfg(feature = "exp-object-iteration")]
398 LCompSpec::ForObj {
399 frame_shape,
400 key,
401 visibility,
402 value,
403 over,
404 ..
405 } => {
406 use jrsonnet_ir::Visibility;
407 let obj = if let Some(CachedOver::Obj(cached)) = &cached_overs[idx] {
408 cached.clone()
409 } else {
410 let val = evaluate(ctx.clone(), over)?;
411 let Val::Obj(obj) = val else {
412 bail!(TypeMismatch(
413 "object iteration over",
414 vec![ValType::Obj],
415 val.value_type(),
416 ))
417 };
418 obj
419 };
420 let fields = obj.fields_with_visibility(
421 #[cfg(feature = "exp-preserve-order")]
422 false,
423 );
424 let pairs: Vec<(IStr, Visibility)> = fields
425 .into_iter()
426 .filter(|(_, v)| match visibility {
427 Visibility::Normal => v.is_visible(),
428 Visibility::Hidden => !v.is_visible(),
429 Visibility::Unhide => true,
430 })
431 .collect();
432 let inner_reserve = guaranteed_reserve.max(1) * pairs.len();
433 for (i, (field_name, _)) in pairs.into_iter().enumerate() {
434 let key_val = Val::string(field_name.clone());
435 let value_thunk = obj
436 .get_lazy(field_name.clone())
437 .expect("field exists, just enumerated");
438 let inner_ctx = ctx.pack_captures_sup_this(frame_shape).enter(|fill, ctx| {
439 fill.set(*key, Thunk::evaluated(key_val));
440 destruct(value, fill, value_thunk, &ctx);
441 });
442 evaluate_compspecs(
443 inner_ctx,
444 specs,
445 cached_overs,
446 idx + 1,
447 if i == 0 { inner_reserve } else { 0 },
448 collector,
449 )?;
450 }
451 }
368 }452 }
369 Ok(())453 Ok(())
370}454}
modifiedcrates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth
909909
910 out910 out
911 }911 }
912 pub fn fields_with_visibility(
913 &self,
914 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
915 ) -> Vec<(IStr, Visibility)> {
916 #[cfg(feature = "exp-preserve-order")]
917 if preserve_order {
918 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self
919 .fields_visibility()
920 .into_iter()
921 .enumerate()
922 .map(|(idx, (k, d))| {
923 (
924 (
925 k,
926 d.exists_visible.expect("non-existing fields filtered out"),
927 ),
928 (d.sort_key(), idx),
929 )
930 })
931 .unzip();
932 keys.sort_unstable_by_key(|v| v.0);
933 for i in 0..fields.len() {
934 let x = fields[i].clone();
935 let mut j = i;
936 loop {
937 let k = keys[j].1;
938 keys[j].1 = j;
939 if k == i {
940 break;
941 }
942 fields[j] = fields[k].clone();
943 j = k;
944 }
945 fields[j] = x;
946 }
947 return fields;
948 }
949 let mut fields: Vec<_> = self
950 .fields_visibility()
951 .into_iter()
952 .map(|(k, d)| {
953 (
954 k,
955 d.exists_visible.expect("non-existing fields filtered out"),
956 )
957 })
958 .collect();
959 fields.sort_unstable_by(|a, b| a.0.cmp(&b.0));
960 fields
961 }
912 pub fn fields_ex(962 pub fn fields_ex(
913 &self,963 &self,
914 include_hidden: bool,964 include_hidden: bool,
modifiedcrates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth
441 use crate::{ParserSettings, parse};441 use crate::{ParserSettings, parse};
442442
443 #[test]443 #[test]
444 #[cfg(not(feature = "exp-null-coaelse"))]
444 fn snapshots() {445 fn snapshots() {
445 glob!("tests/*.jsonnet", |path| {446 glob!("tests/*.jsonnet", |path| {
446 let input = fs::read_to_string(path).expect("read test file");447 let input = fs::read_to_string(path).expect("read test file");