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

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs5.6 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, 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	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() != min_len {33				bail!("expected {} elements, got {}", min_len, arr.len32())34			}35		} else if arr.len() < min_len {36			bail!(37				"expected at least {} elements, but array was only {}",38				min_len,39				arr.len32()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)?.expect("length is checked"))),51			ctx,52		);53	}5455	let start_len = start.len();56	let end_len = end.len();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(start_len..to)))66			}),67		);68	}6970	for (i, d) in end.iter().enumerate() {71		let full = full.clone();72		destruct(73			d,74			fill,75			Thunk!(move || {76				let full = full.evaluate()?;77				Ok(full78					.get(full.len() - end_len + i)?79					.expect("length is checked"))80			}),81			ctx,82		);83	}84}8586#[allow(dead_code, reason = "not dead in exp-destruct")]87fn destruct_object(88	fields: &[LDestructField],89	rest: Option<&LDestructRest>,9091	fill: &LocalsFrame,92	value: Thunk<Val>,93	ctx: &Context,94) {95	use jrsonnet_interner::IStr;96	use rustc_hash::FxHashSet;9798	use crate::{ObjValueBuilder, bail};99100	let captured_fields: FxHashSet<IStr> = fields.iter().map(|f| f.name.clone()).collect();101	let field_names: Vec<(IStr, bool)> = fields102		.iter()103		.map(|f| (f.name.clone(), f.default.is_some()))104		.collect();105	let has_rest = rest.is_some();106	let full = Thunk!(move || {107		let v = value.evaluate()?;108		let Val::Obj(obj) = v else {109			bail!("expected object");110		};111		for (field, has_default) in &field_names {112			if !has_default && !obj.has_field_ex(field.clone(), true) {113				bail!("missing field: {field}");114			}115		}116		if !has_rest {117			let len = obj.len32();118			if len as usize > field_names.len() {119				bail!("too many fields, and rest not found");120			}121		}122		Ok(obj)123	});124125	if let Some(LDestructRest::Keep(slot)) = rest {126		let full = full.clone();127		fill.set(128			*slot,129			Thunk!(move || {130				let full = full.evaluate()?;131				let mut out = ObjValueBuilder::new();132				out.extend_with_core(full.as_standalone());133				out.with_fields_omitted(captured_fields);134				Ok(Val::Obj(out.build()))135			}),136		);137	}138139	for field in fields {140		let field_name = field.name.clone();141		let default_thunk: Option<Thunk<Val>> = field.default.as_ref().map(|(shape, expr)| {142			let expr = expr.clone();143			let env = Context::enter_using(ctx, shape);144			Thunk!(move || evaluate(env, &expr))145		});146147		let field_full = full.clone();148		let value_thunk = Thunk!(move || {149			let obj = field_full.evaluate()?;150			obj.get(field_name)?.map_or_else(151				|| default_thunk.as_ref().expect("shape is checked").evaluate(),152				Ok,153			)154		});155156		if let Some(into) = &field.into {157			destruct(into, fill, value_thunk, ctx);158		} else {159			unreachable!("analyzer lowers object-destruct shorthands into `into`");160		}161	}162}163164#[allow(unused_variables)]165pub fn destruct(d: &LDestruct, fill: &LocalsFrame, value: Thunk<Val>, a_ctx: &Context) {166	match d {167		LDestruct::Full(slot) => fill.set(*slot, value),168		#[cfg(feature = "exp-destruct")]169		LDestruct::Skip => {}170		#[cfg(feature = "exp-destruct")]171		LDestruct::Array { start, rest, end } => {172			destruct_array(start, rest.as_ref(), end, fill, value, a_ctx)173		}174		#[cfg(feature = "exp-destruct")]175		LDestruct::Object { fields, rest } => destruct_object(fields, rest.as_ref(), fill, value, a_ctx),176	}177}178179pub fn fill_letrec_binds(fill: &LocalsFrame, ctx: &Context, binds: &[LBind]) {180	for bind in binds {181		let expr = bind.value.clone();182		let env = Context::enter_using(ctx, &bind.value_shape);183		destruct(184			&bind.destruct,185			fill,186			Thunk!(move || evaluate(env, &expr)),187			ctx,188		);189	}190}191192pub fn evaluate_local_expr(parent: Context, l: &LLocalExpr) -> Result<Val> {193	let ctx = parent194		.pack_captures_sup_this(&l.frame_shape)195		.enter(|fill, ctx| {196			fill_letrec_binds(fill, ctx, &l.binds);197		});198	evaluate(ctx, &l.body)199}200201pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}202impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}203204pub fn evaluate_locals_unbound(205	outer: &Context,206	frame_shape: &ClosureShape,207	this_slot: Option<LocalSlot>,208	locals: Rc<Vec<LBind>>,209) -> impl CloneableUnbound<Context> {210	#[derive(Trace, Clone)]211	struct UnboundLocals {212		captures: PackedContext,213		this_slot: Option<LocalSlot>,214		locals: Rc<Vec<LBind>>,215	}216	impl Unbound for UnboundLocals {217		type Bound = Context;218219		fn bind(&self, sup_this: SupThis) -> Result<Context> {220			Ok(self.captures.clone().enter(sup_this, |fill, ctx| {221				if let Some(slot) = self.this_slot {222					let this_obj = ctx.sup_this().expect("sup_this set above").this().clone();223					fill.set(slot, Thunk::evaluated(Val::Obj(this_obj)));224				}225				fill_letrec_binds(fill, ctx, &self.locals);226			}))227		}228	}229230	UnboundLocals {231		captures: outer.pack_captures(frame_shape),232		this_slot,233		locals,234	}235}