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

difftreelog

source

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