git.delta.rocks / jrsonnet / refs/commits / 5fc1275a239d

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};56#[cfg(feature = "exp-preserve-order")]7use crate::evaluate;8use crate::{9	Context, Pending, Thunk, Val, bail,10	error::{ErrorKind::*, Result},11	evaluate_method, evaluate_named_param,12};1314#[allow(clippy::too_many_lines)]15#[allow(unused_variables)]16pub fn destruct<H: BuildHasher>(17	d: &Destruct,18	parent: Thunk<Val>,19	fctx: Pending<Context>,20	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,21) -> Result<()> {22	match d {23		Destruct::Full(v) => {24			let old = new_bindings.insert(v.clone(), parent);25			if old.is_some() {26				bail!(DuplicateLocalVar(v.clone()))27			}28		}29		#[cfg(feature = "exp-destruct")]30		Destruct::Skip => {}31		#[cfg(feature = "exp-destruct")]32		Destruct::Array { start, rest, end } => {33			use jrsonnet_ir::DestructRest;3435			let min_len = start.len() + end.len();36			let has_rest = rest.is_some();37			let full = Thunk!(move || {38				let v = parent.evaluate()?;39				let Val::Arr(arr) = v else {40					bail!("expected array");41				};42				if !has_rest {43					if arr.len() != min_len {44						bail!("expected {} elements, got {}", min_len, arr.len())45					}46				} else if arr.len() < min_len {47					bail!(48						"expected at least {} elements, but array was only {}",49						min_len,50						arr.len()51					)52				}53				Ok(arr)54			});5556			{57				for (i, d) in start.iter().enumerate() {58					let full = full.clone();59					destruct(60						d,61						Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),62						fctx.clone(),63						new_bindings,64					)?;65				}66			}6768			match rest {69				Some(DestructRest::Keep(v)) => {70					let start = start.len();71					let end = end.len();72					let full = full.clone();73					destruct(74						&Destruct::Full(v.clone()),75						Thunk!(move || {76							let full = full.evaluate()?;77							let to = full.len() - end;78							Ok(Val::Arr(full.slice(79								Some(start as i32),80								Some(to as i32),81								None,82							)))83						}),84						fctx.clone(),85						new_bindings,86					)?;87				}88				Some(DestructRest::Drop) | None => {}89			}9091			{92				for (i, d) in end.iter().enumerate() {93					let full = full.clone();94					let end = end.len();95					destruct(96						d,97						Thunk!(move || {98							let full = full.evaluate()?;99							Ok(full.get(full.len() - end + i)?.expect("length is checked"))100						}),101						fctx.clone(),102						new_bindings,103					)?;104				}105			}106		}107		#[cfg(feature = "exp-destruct")]108		Destruct::Object { fields, rest } => {109			use jrsonnet_ir::DestructRest;110			use rustc_hash::FxHashSet;111112			use crate::ObjValueBuilder;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}