git.delta.rocks / jrsonnet / refs/commits / 4824700b357f

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs5.2 KiBsourcehistory
1use std::{collections::HashMap, hash::BuildHasher};23use jrsonnet_interner::IStr;4use jrsonnet_ir::{BindSpec, Destruct};56use crate::{7	bail,8	error::{ErrorKind::*, Result},9	evaluate_method, evaluate_named_param, Context, Pending, Thunk, Val,10};1112#[cfg(feature = "exp-preserve-order")]13use crate::evaluate;1415#[allow(clippy::too_many_lines)]16#[allow(unused_variables)]17pub fn destruct<H: BuildHasher>(18	d: &Destruct,19	parent: Thunk<Val>,20	fctx: Pending<Context>,21	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,22) -> Result<()> {23	match d {24		Destruct::Full(v) => {25			let old = new_bindings.insert(v.clone(), parent);26			if old.is_some() {27				bail!(DuplicateLocalVar(v.clone()))28			}29		}30		#[cfg(feature = "exp-destruct")]31		Destruct::Skip => {}32		#[cfg(feature = "exp-destruct")]33		Destruct::Array { start, rest, end } => {34			use jrsonnet_ir::DestructRest;3536			let min_len = start.len() + end.len();37			let has_rest = rest.is_some();38			let full = Thunk!(move || {39				let v = parent.evaluate()?;40				let Val::Arr(arr) = v else {41					bail!("expected array");42				};43				if !has_rest {44					if arr.len() != min_len {45						bail!("expected {} elements, got {}", min_len, arr.len())46					}47				} else if arr.len() < min_len {48					bail!(49						"expected at least {} elements, but array was only {}",50						min_len,51						arr.len()52					)53				}54				Ok(arr)55			});5657			{58				for (i, d) in start.iter().enumerate() {59					let full = full.clone();60					destruct(61						d,62						Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),63						fctx.clone(),64						new_bindings,65					)?;66				}67			}6869			match rest {70				Some(DestructRest::Keep(v)) => {71					let start = start.len();72					let end = end.len();73					let full = full.clone();74					destruct(75						&Destruct::Full(v.clone()),76						Thunk!(move || {77							let full = full.evaluate()?;78							let to = full.len() - end;79							Ok(Val::Arr(full.slice(80								Some(start as i32),81								Some(to as i32),82								None,83							)))84						}),85						fctx.clone(),86						new_bindings,87					)?;88				}89				Some(DestructRest::Drop) | None => {}90			}9192			{93				for (i, d) in end.iter().enumerate() {94					let full = full.clone();95					let end = end.len();96					destruct(97						d,98						Thunk!(move || {99							let full = full.evaluate()?;100							Ok(full.get(full.len() - end + i)?.expect("length is checked"))101						}),102						fctx.clone(),103						new_bindings,104					)?;105				}106			}107		}108		#[cfg(feature = "exp-destruct")]109		Destruct::Object { fields, rest } => {110			use crate::ObjValueBuilder;111			use jrsonnet_ir::DestructRest;112			use rustc_hash::FxHashSet;113114			let captured_fields: FxHashSet<_> = fields.iter().map(|f| f.0.clone()).collect();115			let field_names: Vec<_> = fields116				.iter()117				.map(|f| (f.0.clone(), f.2.is_some()))118				.collect();119			let has_rest = rest.is_some();120			let full = Thunk!(move || {121				let v = parent.evaluate()?;122				let Val::Obj(obj) = v else {123					bail!("expected object");124				};125				for (field, has_default) in &field_names {126					if !has_default && !obj.has_field_ex(field.clone(), true) {127						bail!("missing field: {field}");128					}129				}130				if !has_rest {131					let len = obj.len();132					if len > field_names.len() {133						bail!("too many fields, and rest not found");134					}135				}136				Ok(obj)137			});138139			match rest {140				Some(DestructRest::Keep(v)) => {141					let full = full.clone();142					destruct(143						&Destruct::Full(v.clone()),144						Thunk!(move || {145							let full = full.evaluate()?;146							let mut builder = ObjValueBuilder::new();147							builder148								.reserve_cores(1)149								.extend_with_core(full.as_standalone());150							builder.with_fields_omitted(captured_fields);151							Ok(Val::Obj(builder.build()))152						}),153						fctx.clone(),154						new_bindings,155					)?;156				}157				Some(DestructRest::Drop) | None => {}158			}159160			for (field, d, default) in fields {161				let default = default.clone().map(|e| (fctx.clone(), e));162				let value = {163					let field = field.clone();164					let full = full.clone();165					Thunk!(move || {166						let full = full.evaluate()?;167						if let Some(field) = full.get(field)? {168							Ok(field)169						} else {170							let (fctx, expr) = default.as_ref().expect("shape is checked");171							Ok(crate::evaluate(fctx.clone().unwrap(), expr)?)172						}173					})174				};175176				if let Some(d) = d {177					destruct(d, value, fctx.clone(), new_bindings)?;178				} else {179					destruct(180						&Destruct::Full(field.clone()),181						value,182						fctx.clone(),183						new_bindings,184					)?;185				}186			}187		}188	}189	Ok(())190}191192pub fn evaluate_dest<H: BuildHasher>(193	d: &BindSpec,194	fctx: Pending<Context>,195	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,196) -> Result<()> {197	match d {198		BindSpec::Field { into, value } => {199			let name = into.name();200			let value = value.clone();201			let data = {202				let fctx = fctx.clone();203				Thunk!(move || evaluate_named_param(fctx.unwrap(), &value, name))204			};205			destruct(into, data, fctx, new_bindings)?;206		}207		BindSpec::Function {208			name,209			params,210			value,211		} => {212			let params = params.clone();213			let name = name.clone();214			let value = value.clone();215			let old = new_bindings.insert(name.clone(), {216				let name = name.clone();217				Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))218			});219			if old.is_some() {220				bail!(DuplicateLocalVar(name))221			}222		}223	}224	Ok(())225}