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

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs5.9 KiBsourcehistory
1use std::rc::Rc;23use jrsonnet_gcmodule::Trace;45use crate::{6	Context, LocalsFrame, PackedContext, Result, SupThis, Thunk, Unbound, Val,7	analyze::{8		ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LExpr, LLocalExpr, LocalSlot,9	},10	bail,11	evaluate::evaluate,12};1314#[allow(dead_code, reason = "not dead in exp-destruct")]15fn destruct_array(16	start: &[LDestruct],17	rest: Option<&LDestructRest>,18	end: &[LDestruct],1920	fill: &LocalsFrame,21	value: Thunk<Val>,22	a_ctx: &Context,23) {24	let min_len = start.len() + end.len();25	let has_rest = rest.is_some();26	let full = Thunk!(move || {27		let v = value.evaluate()?;28		let Val::Arr(arr) = v else {29			bail!("expected array");30		};31		if !has_rest {32			if arr.len() as usize != min_len {33				bail!("expected {} elements, got {}", min_len, arr.len())34			}35		} else if (arr.len() as usize) < min_len {36			bail!(37				"expected at least {} elements, but array was only {}",38				min_len,39				arr.len()40			)41		}42		Ok(arr)43	});4445	for (i, d) in start.iter().enumerate() {46		let full = full.clone();47		destruct(48			d,49			fill,50			Thunk!(move || Ok(full.evaluate()?.get(i as u32)?.expect("length is checked"))),51			a_ctx,52		);53	}5455	let start_len = start.len() as u32;56	let end_len = end.len() as u32;5758	if let Some(LDestructRest::Keep(slot)) = rest {59		let full = full.clone();60		fill.set(61			*slot,62			Thunk!(move || {63				let full = full.evaluate()?;64				let to = full.len() - end_len;65				Ok(Val::Arr(full.slice(66					Some(start_len as i32),67					Some(to as i32),68					None,69				)))70			}),71		);72	}7374	for (i, d) in end.iter().enumerate() {75		let full = full.clone();76		destruct(77			d,78			fill,79			Thunk!(move || {80				let full = full.evaluate()?;81				Ok(full82					.get(full.len() - end_len + i as u32)?83					.expect("length is checked"))84			}),85			a_ctx,86		);87	}88}8990#[allow(dead_code, reason = "not dead in exp-destruct")]91fn destruct_object(92	fields: &[LDestructField],93	rest: Option<&LDestructRest>,9495	fill: &LocalsFrame,96	value: Thunk<Val>,97	a_ctx: &Context,98) {99	use jrsonnet_interner::IStr;100	use rustc_hash::FxHashSet;101102	use crate::{ObjValueBuilder, bail};103104	let captured_fields: FxHashSet<IStr> = fields.iter().map(|f| f.name.clone()).collect();105	let field_names: Vec<(IStr, bool)> = fields106		.iter()107		.map(|f| (f.name.clone(), f.default.is_some()))108		.collect();109	let has_rest = rest.is_some();110	let full = Thunk!(move || {111		let v = value.evaluate()?;112		let Val::Obj(obj) = v else {113			bail!("expected object");114		};115		for (field, has_default) in &field_names {116			if !has_default && !obj.has_field_ex(field.clone(), true) {117				bail!("missing field: {field}");118			}119		}120		if !has_rest {121			let len = obj.len();122			if len as usize > field_names.len() {123				bail!("too many fields, and rest not found");124			}125		}126		Ok(obj)127	});128129	if let Some(LDestructRest::Keep(slot)) = rest {130		let full = full.clone();131		fill.set(132			*slot,133			Thunk!(move || {134				let full = full.evaluate()?;135				let mut out = ObjValueBuilder::new();136				out.extend_with_core(full.as_standalone());137				out.with_fields_omitted(captured_fields);138				Ok(Val::Obj(out.build()))139			}),140		);141	}142143	for field in fields {144		let field_name = field.name.clone();145		let default_thunk: Option<Thunk<Val>> = field146			.default147			.as_ref()148			.map(|(shape, expr)| build_b_thunk(a_ctx, shape, expr.clone()));149150		let field_full = full.clone();151		let value_thunk = Thunk!(move || {152			let obj = field_full.evaluate()?;153			obj.get(field_name)?.map_or_else(154				|| default_thunk.as_ref().expect("shape is checked").evaluate(),155				Ok,156			)157		});158159		if let Some(into) = &field.into {160			destruct(into, fill, value_thunk, a_ctx);161		} else {162			unreachable!("analyzer lowers object-destruct shorthands into `into`");163		}164	}165}166167#[allow(unused_variables)]168pub fn destruct(d: &LDestruct, fill: &LocalsFrame, value: Thunk<Val>, a_ctx: &Context) {169	match d {170		LDestruct::Full(slot) => fill.set(*slot, value),171		#[cfg(feature = "exp-destruct")]172		LDestruct::Skip => {}173		#[cfg(feature = "exp-destruct")]174		LDestruct::Array { start, rest, end } => {175			destruct_array(start, rest.as_ref(), end, fill, value, a_ctx)176		}177		#[cfg(feature = "exp-destruct")]178		LDestruct::Object { fields, rest } => destruct_object(fields, rest.as_ref(), fill, value, a_ctx),179	}180}181182pub fn build_b_thunk(a_ctx: &Context, shape: &ClosureShape, expr: Rc<LExpr>) -> Thunk<Val> {183	let env = Context::enter_using(a_ctx, shape);184	Thunk!(move || evaluate(env, &expr))185}186pub fn build_b_thunk_uno(a_ctx: &Context, shape: Rc<(ClosureShape, LExpr)>) -> Thunk<Val> {187	let env = Context::enter_using(a_ctx, &shape.0);188	Thunk!(move || evaluate(env, &shape.1))189}190191pub fn fill_letrec_binds(fill: &LocalsFrame, ctx: &Context, binds: &[LBind]) {192	for bind in binds {193		let value_thunk = build_b_thunk(ctx, &bind.value_shape, bind.value.clone());194		destruct(&bind.destruct, fill, value_thunk, ctx);195	}196}197198pub fn evaluate_local_expr(parent: Context, l: &LLocalExpr) -> Result<Val> {199	let ctx = parent200		.pack_captures_sup_this(&l.frame_shape)201		.enter(|fill, ctx| {202			fill_letrec_binds(fill, ctx, &l.binds);203		});204	evaluate(ctx, &l.body)205}206207pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}208impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}209210pub fn evaluate_locals_unbound(211	outer: &Context,212	frame_shape: &ClosureShape,213	this_slot: Option<LocalSlot>,214	locals: Rc<Vec<LBind>>,215) -> impl CloneableUnbound<Context> {216	#[derive(Trace, Clone)]217	struct UnboundLocals {218		captures: PackedContext,219		this_slot: Option<LocalSlot>,220		locals: Rc<Vec<LBind>>,221	}222	impl Unbound for UnboundLocals {223		type Bound = Context;224225		fn bind(&self, sup_this: SupThis) -> Result<Context> {226			Ok(self.captures.clone().enter(sup_this, |fill, ctx| {227				if let Some(slot) = self.this_slot {228					let this_obj = ctx.sup_this().expect("sup_this set above").this().clone();229					fill.set(slot, Thunk::evaluated(Val::Obj(this_obj)));230				}231				fill_letrec_binds(fill, ctx, &self.locals);232			}))233		}234	}235236	UnboundLocals {237		captures: outer.pack_captures(frame_shape),238		this_slot,239		locals,240	}241}