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

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs14.2 KiBsourcehistory
1use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_parser::LocExpr;67use super::ArrValue;8use crate::{9	error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,10	val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,11};1213pub trait ArrayLike: Any + Trace + Debug {14	fn len(&self) -> usize;15	fn is_empty(&self) -> bool {16		self.len() == 017	}18	fn get(&self, index: usize) -> Result<Option<Val>>;19	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;20	fn get_cheap(&self, index: usize) -> Option<Val>;2122	fn is_cheap(&self) -> bool;23}2425#[derive(Debug, Trace)]26pub struct SliceArray {27	pub(crate) inner: ArrValue,28	pub(crate) from: u32,29	pub(crate) to: u32,30	pub(crate) step: u32,31}3233impl SliceArray {34	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {35		self.inner36			.iter()37			.skip(self.from as usize)38			.take((self.to - self.from) as usize)39			.step_by(self.step as usize)40	}4142	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {43		self.inner44			.iter_lazy()45			.skip(self.from as usize)46			.take((self.to - self.from) as usize)47			.step_by(self.step as usize)48	}4950	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {51		Some(52			self.inner53				.iter_cheap()?54				.skip(self.from as usize)55				.take((self.to - self.from) as usize)56				.step_by(self.step as usize),57		)58	}59}60impl ArrayLike for SliceArray {61	fn len(&self) -> usize {62		iter::repeat(())63			.take((self.to - self.from) as usize)64			.step_by(self.step as usize)65			.count()66	}6768	fn get(&self, index: usize) -> Result<Option<Val>> {69		self.iter().nth(index).transpose()70	}7172	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {73		self.iter_lazy().nth(index)74	}7576	fn get_cheap(&self, index: usize) -> Option<Val> {77		self.iter_cheap()?.nth(index)78	}79	fn is_cheap(&self) -> bool {80		self.inner.is_cheap()81	}82}8384#[derive(Trace, Debug)]85pub struct CharArray(pub Vec<char>);86impl ArrayLike for CharArray {87	fn len(&self) -> usize {88		self.0.len()89	}9091	fn get(&self, index: usize) -> Result<Option<Val>> {92		Ok(self.get_cheap(index))93	}9495	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {96		self.get_cheap(index).map(Thunk::evaluated)97	}9899	fn get_cheap(&self, index: usize) -> Option<Val> {100		self.0.get(index).map(|v| Val::string(*v))101	}102	fn is_cheap(&self) -> bool {103		true104	}105}106107#[derive(Trace, Debug)]108pub struct BytesArray(pub IBytes);109impl ArrayLike for BytesArray {110	fn len(&self) -> usize {111		self.0.len()112	}113114	fn get(&self, index: usize) -> Result<Option<Val>> {115		Ok(self.get_cheap(index))116	}117118	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {119		self.get_cheap(index).map(Thunk::evaluated)120	}121122	fn get_cheap(&self, index: usize) -> Option<Val> {123		self.0.get(index).map(|v| Val::Num((*v).into()))124	}125	fn is_cheap(&self) -> bool {126		true127	}128}129130#[derive(Debug, Trace, Clone)]131enum ArrayThunk<T: 'static + Trace> {132	Computed(Val),133	Errored(Error),134	Waiting(T),135	Pending,136}137138#[derive(Debug, Trace, Clone)]139pub struct ExprArray {140	ctx: Context,141	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,142}143impl ExprArray {144	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {145		Self {146			ctx,147			cached: Cc::new(RefCell::new(148				items.into_iter().map(ArrayThunk::Waiting).collect(),149			)),150		}151	}152}153impl ArrayLike for ExprArray {154	fn len(&self) -> usize {155		self.cached.borrow().len()156	}157	fn get(&self, index: usize) -> Result<Option<Val>> {158		if index >= self.len() {159			return Ok(None);160		}161		match &self.cached.borrow()[index] {162			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),163			ArrayThunk::Errored(e) => return Err(e.clone()),164			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),165			ArrayThunk::Waiting(..) => {}166		};167168		let ArrayThunk::Waiting(expr) =169			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)170		else {171			unreachable!()172		};173174		let new_value = match evaluate(self.ctx.clone(), &expr) {175			Ok(v) => v,176			Err(e) => {177				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());178				return Err(e);179			}180		};181		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());182		Ok(Some(new_value))183	}184	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {185		if index >= self.len() {186			return None;187		}188		match &self.cached.borrow()[index] {189			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),190			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),191			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}192		};193194		#[derive(Trace)]195		struct ExprArrThunk {196			expr: ExprArray,197			index: usize,198		}199		impl ThunkValue for ExprArrThunk {200			type Output = Val;201202			fn get(&self) -> Result<Self::Output> {203				self.expr204					.get(self.index)205					.transpose()206					.expect("index checked")207			}208		}209210		Some(Thunk::new(ExprArrThunk {211			expr: self.clone(),212			index,213		}))214	}215	fn get_cheap(&self, _index: usize) -> Option<Val> {216		None217	}218	fn is_cheap(&self) -> bool {219		false220	}221}222223#[derive(Trace, Debug)]224pub struct ExtendedArray {225	pub a: ArrValue,226	pub b: ArrValue,227	split: usize,228	len: usize,229}230impl ExtendedArray {231	pub fn new(a: ArrValue, b: ArrValue) -> Self {232		let a_len = a.len();233		let b_len = b.len();234		Self {235			a,236			b,237			split: a_len,238			len: a_len.checked_add(b_len).expect("too large array value"),239		}240	}241}242243struct WithExactSize<I>(I, usize);244impl<I, T> Iterator for WithExactSize<I>245where246	I: Iterator<Item = T>,247{248	type Item = T;249250	fn next(&mut self) -> Option<Self::Item> {251		self.0.next()252	}253	fn nth(&mut self, n: usize) -> Option<Self::Item> {254		self.0.nth(n)255	}256	fn size_hint(&self) -> (usize, Option<usize>) {257		(self.1, Some(self.1))258	}259}260impl<I> DoubleEndedIterator for WithExactSize<I>261where262	I: DoubleEndedIterator,263{264	fn next_back(&mut self) -> Option<Self::Item> {265		self.0.next_back()266	}267	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {268		self.0.nth_back(n)269	}270}271impl<I> ExactSizeIterator for WithExactSize<I>272where273	I: Iterator,274{275	fn len(&self) -> usize {276		self.1277	}278}279impl ArrayLike for ExtendedArray {280	fn get(&self, index: usize) -> Result<Option<Val>> {281		if self.split > index {282			self.a.get(index)283		} else {284			self.b.get(index - self.split)285		}286	}287	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {288		if self.split > index {289			self.a.get_lazy(index)290		} else {291			self.b.get_lazy(index - self.split)292		}293	}294295	fn len(&self) -> usize {296		self.len297	}298299	fn get_cheap(&self, index: usize) -> Option<Val> {300		if self.split > index {301			self.a.get_cheap(index)302		} else {303			self.b.get_cheap(index - self.split)304		}305	}306	fn is_cheap(&self) -> bool {307		self.a.is_cheap() && self.b.is_cheap()308	}309}310311#[derive(Trace, Debug)]312pub struct LazyArray(pub Vec<Thunk<Val>>);313impl ArrayLike for LazyArray {314	fn len(&self) -> usize {315		self.0.len()316	}317	fn get(&self, index: usize) -> Result<Option<Val>> {318		let Some(v) = self.0.get(index) else {319			return Ok(None);320		};321		v.evaluate().map(Some)322	}323	fn get_cheap(&self, _index: usize) -> Option<Val> {324		None325	}326	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {327		self.0.get(index).cloned()328	}329	fn is_cheap(&self) -> bool {330		false331	}332}333334#[derive(Trace, Debug)]335pub struct EagerArray(pub Vec<Val>);336impl ArrayLike for EagerArray {337	fn len(&self) -> usize {338		self.0.len()339	}340341	fn get(&self, index: usize) -> Result<Option<Val>> {342		Ok(self.0.get(index).cloned())343	}344345	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {346		self.0.get(index).cloned().map(Thunk::evaluated)347	}348349	fn get_cheap(&self, index: usize) -> Option<Val> {350		self.0.get(index).cloned()351	}352	fn is_cheap(&self) -> bool {353		true354	}355}356357/// Inclusive range type358#[derive(Debug, Trace, PartialEq, Eq)]359pub struct RangeArray {360	start: i32,361	end: i32,362}363impl RangeArray {364	pub fn empty() -> Self {365		Self::new_exclusive(0, 0)366	}367	pub fn new_exclusive(start: i32, end: i32) -> Self {368		end.checked_sub(1)369			.map_or_else(Self::empty, |end| Self { start, end })370	}371	pub fn new_inclusive(start: i32, end: i32) -> Self {372		Self { start, end }373	}374	fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {375		WithExactSize(376			self.start..=self.end,377			(self.end as usize)378				.wrapping_sub(self.start as usize)379				.wrapping_add(1),380		)381	}382}383384impl ArrayLike for RangeArray {385	fn len(&self) -> usize {386		self.range().len()387	}388	fn is_empty(&self) -> bool {389		self.range().len() == 0390	}391392	fn get(&self, index: usize) -> Result<Option<Val>> {393		Ok(self.get_cheap(index))394	}395396	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {397		self.get_cheap(index).map(Thunk::evaluated)398	}399400	fn get_cheap(&self, index: usize) -> Option<Val> {401		self.range().nth(index).map(|i| Val::Num(i.into()))402	}403	fn is_cheap(&self) -> bool {404		true405	}406}407408#[derive(Debug, Trace)]409pub struct ReverseArray(pub ArrValue);410impl ArrayLike for ReverseArray {411	fn len(&self) -> usize {412		self.0.len()413	}414415	fn get(&self, index: usize) -> Result<Option<Val>> {416		self.0.get(self.0.len() - index - 1)417	}418419	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {420		self.0.get_lazy(self.0.len() - index - 1)421	}422423	fn get_cheap(&self, index: usize) -> Option<Val> {424		self.0.get_cheap(self.0.len() - index - 1)425	}426	fn is_cheap(&self) -> bool {427		self.0.is_cheap()428	}429}430431#[derive(Trace, Debug, Clone)]432pub struct MappedArray<const WITH_INDEX: bool> {433	inner: ArrValue,434	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,435	mapper: FuncVal,436}437impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {438	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {439		let len = inner.len();440		Self {441			inner,442			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),443			mapper,444		}445	}446	fn evaluate(&self, index: usize, value: Val) -> Result<Val> {447		if WITH_INDEX {448			self.mapper.evaluate_simple(&(index, value), false)449		} else {450			self.mapper.evaluate_simple(&(value,), false)451		}452	}453}454impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {455	fn len(&self) -> usize {456		self.cached.borrow().len()457	}458459	fn get(&self, index: usize) -> Result<Option<Val>> {460		if index >= self.len() {461			return Ok(None);462		}463		match &self.cached.borrow()[index] {464			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),465			ArrayThunk::Errored(e) => return Err(e.clone()),466			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),467			ArrayThunk::Waiting(..) => {}468		};469470		let ArrayThunk::Waiting(()) =471			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)472		else {473			unreachable!()474		};475476		let val = self477			.inner478			.get(index)479			.transpose()480			.expect("index checked")481			.and_then(|r| self.evaluate(index, r));482483		let new_value = match val {484			Ok(v) => v,485			Err(e) => {486				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());487				return Err(e);488			}489		};490		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());491		Ok(Some(new_value))492	}493	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {494		if index >= self.len() {495			return None;496		}497		match &self.cached.borrow()[index] {498			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),499			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),500			ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}501		};502503		#[derive(Trace)]504		struct MappedArrayThunk<const WITH_INDEX: bool> {505			arr: MappedArray<WITH_INDEX>,506			index: usize,507		}508		impl<const WITH_INDEX: bool> ThunkValue for MappedArrayThunk<WITH_INDEX> {509			type Output = Val;510511			fn get(&self) -> Result<Self::Output> {512				self.arr.get(self.index).transpose().expect("index checked")513			}514		}515516		Some(Thunk::new(MappedArrayThunk {517			arr: self.clone(),518			index,519		}))520	}521522	fn get_cheap(&self, _index: usize) -> Option<Val> {523		None524	}525	fn is_cheap(&self) -> bool {526		false527	}528}529530#[derive(Trace, Debug)]531pub struct RepeatedArray {532	data: ArrValue,533	repeats: usize,534	total_len: usize,535}536impl RepeatedArray {537	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {538		let total_len = data.len().checked_mul(repeats)?;539		Some(Self {540			data,541			repeats,542			total_len,543		})544	}545}546547impl ArrayLike for RepeatedArray {548	fn len(&self) -> usize {549		self.total_len550	}551552	fn get(&self, index: usize) -> Result<Option<Val>> {553		if index > self.total_len {554			return Ok(None);555		}556		self.data.get(index % self.data.len())557	}558559	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {560		if index > self.total_len {561			return None;562		}563		self.data.get_lazy(index % self.data.len())564	}565566	fn get_cheap(&self, index: usize) -> Option<Val> {567		if index > self.total_len {568			return None;569		}570		self.data.get_cheap(index % self.data.len())571	}572	fn is_cheap(&self) -> bool {573		self.data.is_cheap()574	}575}576577#[derive(Trace, Debug)]578pub struct PickObjectValues {579	obj: ObjValue,580	keys: Vec<IStr>,581}582583impl PickObjectValues {584	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {585		Self { obj, keys }586	}587}588589impl ArrayLike for PickObjectValues {590	fn len(&self) -> usize {591		self.keys.len()592	}593594	fn get(&self, index: usize) -> Result<Option<Val>> {595		let Some(key) = self.keys.get(index) else {596			return Ok(None);597		};598		Ok(Some(self.obj.get_or_bail(key.clone())?))599	}600601	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {602		let key = self.keys.get(index)?;603		Some(self.obj.get_lazy_or_bail(key.clone()))604	}605606	fn get_cheap(&self, _index: usize) -> Option<Val> {607		None608	}609610	fn is_cheap(&self) -> bool {611		false612	}613}614615#[derive(Trace, Debug)]616pub struct PickObjectKeyValues {617	obj: ObjValue,618	keys: Vec<IStr>,619}620621impl PickObjectKeyValues {622	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {623		Self { obj, keys }624	}625}626627#[derive(Typed)]628pub struct KeyValue {629	key: IStr,630	value: Thunk<Val>,631}632633impl ArrayLike for PickObjectKeyValues {634	fn len(&self) -> usize {635		self.keys.len()636	}637638	fn get(&self, index: usize) -> Result<Option<Val>> {639		let Some(key) = self.keys.get(index) else {640			return Ok(None);641		};642		Ok(Some(643			KeyValue::into_untyped(KeyValue {644				key: key.clone(),645				value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),646			})647			.expect("convertible"),648		))649	}650651	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {652		let key = self.keys.get(index)?;653		// Nothing can fail in the key part, yet value is still654		// lazy-evaluated655		Some(Thunk::evaluated(656			KeyValue::into_untyped(KeyValue {657				key: key.clone(),658				value: self.obj.get_lazy_or_bail(key.clone()),659			})660			.expect("convertible"),661		))662	}663664	fn get_cheap(&self, _index: usize) -> Option<Val> {665		None666	}667668	fn is_cheap(&self) -> bool {669		false670	}671}