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

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs4.5 KiBsourcehistory
1use jrsonnet_interner::IStr;2use jrsonnet_parser::{BindSpec, Destruct};34use crate::{5	bail,6	error::{ErrorKind::*, Result},7	evaluate, evaluate_method, evaluate_named,8	gc::GcHashMap,9	Context, Pending, Thunk, Val,10};1112#[allow(clippy::too_many_lines)]13#[allow(unused_variables)]14pub fn destruct(15	d: &Destruct,16	parent: Thunk<Val>,17	fctx: Pending<Context>,18	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,19) -> Result<()> {20	match d {21		Destruct::Full(v) => {22			let old = new_bindings.insert(v.clone(), parent);23			if old.is_some() {24				bail!(DuplicateLocalVar(v.clone()))25			}26		}27		#[cfg(feature = "exp-destruct")]28		Destruct::Skip => {}29		#[cfg(feature = "exp-destruct")]30		Destruct::Array { start, rest, end } => {31			use jrsonnet_parser::DestructRest;3233			let min_len = start.len() + end.len();34			let has_rest = rest.is_some();35			let full = Thunk!(move || {36				let v = parent.evaluate()?;37				let Val::Arr(arr) = v else {38					bail!("expected array");39				};40				if !has_rest {41					if arr.len() != min_len {42						bail!("expected {} elements, got {}", min_len, arr.len())43					}44				} else if arr.len() < min_len {45					bail!(46						"expected at least {} elements, but array was only {}",47						min_len,48						arr.len()49					)50				}51				Ok(arr)52			});5354			{55				for (i, d) in start.iter().enumerate() {56					let full = full.clone();57					destruct(58						d,59						Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),60						fctx.clone(),61						new_bindings,62					)?;63				}64			}6566			match rest {67				Some(DestructRest::Keep(v)) => {68					let start = start.len();69					let end = end.len();70					let full = full.clone();71					destruct(72						&Destruct::Full(v.clone()),73						Thunk!(move || {74							let full = full.evaluate()?;75							let to = full.len() - end;76							Ok(Val::Arr(full.slice(77								Some(start as i32),78								Some(to as i32),79								None,80							)))81						}),82						fctx.clone(),83						new_bindings,84					)?;85				}86				Some(DestructRest::Drop) | None => {}87			}8889			{90				for (i, d) in end.iter().enumerate() {91					let full = full.clone();92					let end = end.len();93					destruct(94						d,95						Thunk!(move || {96							let full = full.evaluate()?;97							Ok(full.get(full.len() - end + i)?.expect("length is checked"))98						}),99						fctx.clone(),100						new_bindings,101					)?;102				}103			}104		}105		#[cfg(feature = "exp-destruct")]106		Destruct::Object { fields, rest } => {107			let field_names: Vec<_> = fields108				.iter()109				.map(|f| (f.0.clone(), f.2.is_some()))110				.collect();111			let has_rest = rest.is_some();112			let full = Thunk!(move || {113				let v = parent.evaluate()?;114				let Val::Obj(obj) = v else {115					bail!("expected object");116				};117				for (field, has_default) in &field_names {118					if !has_default && !obj.has_field_ex(field.clone(), true) {119						bail!("missing field: {field}");120					}121				}122				if !has_rest {123					let len = obj.len();124					if len > field_names.len() {125						bail!("too many fields, and rest not found");126					}127				}128				Ok(obj)129			});130131			for (field, d, default) in fields {132				let default = default.clone().map(|e| (fctx.clone(), e));133				let value = {134					let field = field.clone();135					let full = full.clone();136					Thunk!(move || {137						let full = full.evaluate()?;138						if let Some(field) = full.get(field)? {139							Ok(field)140						} else {141							let (fctx, expr) = default.as_ref().expect("shape is checked");142							Ok(evaluate(fctx.clone().unwrap(), expr)?)143						}144					})145				};146147				if let Some(d) = d {148					destruct(d, value, fctx.clone(), new_bindings)?;149				} else {150					destruct(151						&Destruct::Full(field.clone()),152						value,153						fctx.clone(),154						new_bindings,155					)?;156				}157			}158		}159	}160	Ok(())161}162163pub fn evaluate_dest(164	d: &BindSpec,165	fctx: Pending<Context>,166	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,167) -> Result<()> {168	match d {169		BindSpec::Field { into, value } => {170			let name = into.name();171			let value = value.clone();172			let data = {173				let fctx = fctx.clone();174				Thunk!(move || name.map_or_else(175					|| evaluate(fctx.unwrap(), &value),176					|name| evaluate_named(fctx.unwrap(), &value, name),177				))178			};179			destruct(into, data, fctx, new_bindings)?;180		}181		BindSpec::Function {182			name,183			params,184			value,185		} => {186			let params = params.clone();187			let name = name.clone();188			let value = value.clone();189			let old = new_bindings.insert(name.clone(), {190				let name = name.clone();191				Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))192			});193			if old.is_some() {194				bail!(DuplicateLocalVar(name))195			}196		}197	}198	Ok(())199}