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

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							builder.extend_with_core(full.as_standalone());148							builder.with_fields_omitted(captured_fields);149							Ok(Val::Obj(builder.build()))150						}),151						fctx.clone(),152						new_bindings,153					)?;154				}155				Some(DestructRest::Drop) | None => {}156			}157158			for (field, d, default) in fields {159				let default = default.clone().map(|e| (fctx.clone(), e));160				let value = {161					let field = field.clone();162					let full = full.clone();163					Thunk!(move || {164						let full = full.evaluate()?;165						if let Some(field) = full.get(field)? {166							Ok(field)167						} else {168							let (fctx, expr) = default.as_ref().expect("shape is checked");169							Ok(crate::evaluate(fctx.clone().unwrap(), expr)?)170						}171					})172				};173174				if let Some(d) = d {175					destruct(d, value, fctx.clone(), new_bindings)?;176				} else {177					destruct(178						&Destruct::Full(field.clone()),179						value,180						fctx.clone(),181						new_bindings,182					)?;183				}184			}185		}186	}187	Ok(())188}189190pub fn evaluate_dest<H: BuildHasher>(191	d: &BindSpec,192	fctx: Pending<Context>,193	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,194) -> Result<()> {195	match d {196		BindSpec::Field { into, value } => {197			let name = into.name();198			let value = value.clone();199			let data = {200				let fctx = fctx.clone();201				Thunk!(move || evaluate_named_param(fctx.unwrap(), &value, name))202			};203			destruct(into, data, fctx, new_bindings)?;204		}205		BindSpec::Function {206			name,207			params,208			value,209		} => {210			let params = params.clone();211			let name = name.clone();212			let value = value.clone();213			let old = new_bindings.insert(name.clone(), {214				let name = name.clone();215				Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))216			});217			if old.is_some() {218				bail!(DuplicateLocalVar(name))219			}220		}221	}222	Ok(())223}