git.delta.rocks / jrsonnet / refs/commits / 7da68eaa8a4d

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs13.6 KiBsourcehistory
1use std::rc::Rc;2use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};34use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::{IBytes, IStr};6use jrsonnet_ir::Expr;78use super::ArrValue;9use crate::function::NativeFn;10use crate::{11	error::ErrorKind::InfiniteRecursionDetected,12	evaluate,13	typed::{IntoUntyped, Typed},14	val::ThunkValue,15	Context, Error, ObjValue, Result, Thunk, Val,16};1718pub trait ArrayLike: Any + Trace + Debug {19	fn len(&self) -> usize;20	fn is_empty(&self) -> bool {21		self.len() == 022	}23	fn get(&self, index: usize) -> Result<Option<Val>>;24	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;25	fn get_cheap(&self, index: usize) -> Option<Val>;2627	fn is_cheap(&self) -> bool;28}2930#[derive(Debug, Trace)]31pub struct SliceArray {32	pub(crate) inner: ArrValue,33	pub(crate) from: u32,34	pub(crate) to: u32,35	pub(crate) step: u32,36}3738impl SliceArray {39	fn map_idx(&self, index: usize) -> usize {40		self.from as usize + self.step as usize * index41	}42}43impl ArrayLike for SliceArray {44	fn len(&self) -> usize {45		(self.to - self.from).div_ceil(self.step) as usize46	}4748	fn get(&self, index: usize) -> Result<Option<Val>> {49		self.inner.get(self.map_idx(index))50	}5152	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {53		self.inner.get_lazy(self.map_idx(index))54	}5556	fn get_cheap(&self, index: usize) -> Option<Val> {57		self.inner.get_cheap(self.map_idx(index))58	}59	fn is_cheap(&self) -> bool {60		self.inner.is_cheap()61	}62}6364#[derive(Trace, Debug)]65pub struct CharArray(pub Vec<char>);66impl ArrayLike for CharArray {67	fn len(&self) -> usize {68		self.0.len()69	}7071	fn get(&self, index: usize) -> Result<Option<Val>> {72		Ok(self.get_cheap(index))73	}7475	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76		self.get_cheap(index).map(Thunk::evaluated)77	}7879	fn get_cheap(&self, index: usize) -> Option<Val> {80		self.0.get(index).map(|v| Val::string(*v))81	}82	fn is_cheap(&self) -> bool {83		true84	}85}8687#[derive(Trace, Debug)]88pub struct BytesArray(pub IBytes);89impl ArrayLike for BytesArray {90	fn len(&self) -> usize {91		self.0.len()92	}9394	fn get(&self, index: usize) -> Result<Option<Val>> {95		Ok(self.get_cheap(index))96	}9798	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99		self.get_cheap(index).map(Thunk::evaluated)100	}101102	fn get_cheap(&self, index: usize) -> Option<Val> {103		self.0.get(index).map(|v| Val::Num((*v).into()))104	}105	fn is_cheap(&self) -> bool {106		true107	}108}109110#[derive(Debug, Trace, Clone)]111enum ArrayThunk {112	Computed(Val),113	Errored(Error),114	Waiting,115	Pending,116}117118#[derive(Debug, Trace, Clone)]119pub struct ExprArray {120	ctx: Context,121	src: Rc<Vec<Expr>>,122	cached: Cc<RefCell<Vec<ArrayThunk>>>,123}124impl ExprArray {125	pub fn new(ctx: Context, src: Rc<Vec<Expr>>) -> Self {126		Self {127			ctx,128			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),129			src,130		}131	}132}133impl ArrayLike for ExprArray {134	fn len(&self) -> usize {135		self.cached.borrow().len()136	}137	fn get(&self, index: usize) -> Result<Option<Val>> {138		if index >= self.len() {139			return Ok(None);140		}141		match &self.cached.borrow()[index] {142			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),143			ArrayThunk::Errored(e) => return Err(e.clone()),144			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),145			ArrayThunk::Waiting => {}146		}147148		let ArrayThunk::Waiting =149			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)150		else {151			unreachable!()152		};153154		let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {155			Ok(v) => v,156			Err(e) => {157				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());158				return Err(e);159			}160		};161		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());162		Ok(Some(new_value))163	}164	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {165		#[derive(Trace)]166		struct ExprArrThunk {167			expr: ExprArray,168			index: usize,169		}170		impl ThunkValue for ExprArrThunk {171			type Output = Val;172173			fn get(&self) -> Result<Self::Output> {174				self.expr175					.get(self.index)176					.transpose()177					.expect("index checked")178			}179		}180181		if index >= self.len() {182			return None;183		}184		match &self.cached.borrow()[index] {185			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),186			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),187			ArrayThunk::Waiting | ArrayThunk::Pending => {}188		}189190		Some(Thunk::new(ExprArrThunk {191			expr: self.clone(),192			index,193		}))194	}195	fn get_cheap(&self, _index: usize) -> Option<Val> {196		None197	}198	fn is_cheap(&self) -> bool {199		false200	}201}202203#[derive(Trace, Debug)]204pub struct ExtendedArray {205	pub a: ArrValue,206	pub b: ArrValue,207	split: usize,208	len: usize,209}210impl ExtendedArray {211	pub fn new(a: ArrValue, b: ArrValue) -> Self {212		let a_len = a.len();213		let b_len = b.len();214		Self {215			a,216			b,217			split: a_len,218			len: a_len.checked_add(b_len).expect("too large array value"),219		}220	}221}222223struct WithExactSize<I>(I, usize);224impl<I, T> Iterator for WithExactSize<I>225where226	I: Iterator<Item = T>,227{228	type Item = T;229230	fn next(&mut self) -> Option<Self::Item> {231		self.0.next()232	}233	fn nth(&mut self, n: usize) -> Option<Self::Item> {234		self.0.nth(n)235	}236	fn size_hint(&self) -> (usize, Option<usize>) {237		(self.1, Some(self.1))238	}239}240impl<I> DoubleEndedIterator for WithExactSize<I>241where242	I: DoubleEndedIterator,243{244	fn next_back(&mut self) -> Option<Self::Item> {245		self.0.next_back()246	}247	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {248		self.0.nth_back(n)249	}250}251impl<I> ExactSizeIterator for WithExactSize<I>252where253	I: Iterator,254{255	fn len(&self) -> usize {256		self.1257	}258}259impl ArrayLike for ExtendedArray {260	fn get(&self, index: usize) -> Result<Option<Val>> {261		if self.split > index {262			self.a.get(index)263		} else {264			self.b.get(index - self.split)265		}266	}267	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {268		if self.split > index {269			self.a.get_lazy(index)270		} else {271			self.b.get_lazy(index - self.split)272		}273	}274275	fn len(&self) -> usize {276		self.len277	}278279	fn get_cheap(&self, index: usize) -> Option<Val> {280		if self.split > index {281			self.a.get_cheap(index)282		} else {283			self.b.get_cheap(index - self.split)284		}285	}286	fn is_cheap(&self) -> bool {287		self.a.is_cheap() && self.b.is_cheap()288	}289}290291#[derive(Trace, Debug)]292pub struct LazyArray(pub Vec<Thunk<Val>>);293impl ArrayLike for LazyArray {294	fn len(&self) -> usize {295		self.0.len()296	}297	fn get(&self, index: usize) -> Result<Option<Val>> {298		let Some(v) = self.0.get(index) else {299			return Ok(None);300		};301		v.evaluate().map(Some)302	}303	fn get_cheap(&self, _index: usize) -> Option<Val> {304		None305	}306	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {307		self.0.get(index).cloned()308	}309	fn is_cheap(&self) -> bool {310		false311	}312}313314#[derive(Trace, Debug)]315pub struct EagerArray(pub Vec<Val>);316impl ArrayLike for EagerArray {317	fn len(&self) -> usize {318		self.0.len()319	}320321	fn get(&self, index: usize) -> Result<Option<Val>> {322		Ok(self.0.get(index).cloned())323	}324325	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {326		self.0.get(index).cloned().map(Thunk::evaluated)327	}328329	fn get_cheap(&self, index: usize) -> Option<Val> {330		self.0.get(index).cloned()331	}332	fn is_cheap(&self) -> bool {333		true334	}335}336337/// Inclusive range type338#[derive(Debug, Trace, PartialEq, Eq)]339pub struct RangeArray {340	start: i32,341	end: i32,342}343impl RangeArray {344	pub fn empty() -> Self {345		Self::new_exclusive(0, 0)346	}347	pub fn new_exclusive(start: i32, end: i32) -> Self {348		end.checked_sub(1)349			.map_or_else(Self::empty, |end| Self { start, end })350	}351	pub fn new_inclusive(start: i32, end: i32) -> Self {352		Self { start, end }353	}354	fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {355		WithExactSize(356			self.start..=self.end,357			(self.end as usize)358				.wrapping_sub(self.start as usize)359				.wrapping_add(1),360		)361	}362}363364impl ArrayLike for RangeArray {365	fn len(&self) -> usize {366		self.range().len()367	}368	fn is_empty(&self) -> bool {369		self.range().len() == 0370	}371372	fn get(&self, index: usize) -> Result<Option<Val>> {373		Ok(self.get_cheap(index))374	}375376	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {377		self.get_cheap(index).map(Thunk::evaluated)378	}379380	fn get_cheap(&self, index: usize) -> Option<Val> {381		self.range().nth(index).map(|i| Val::Num(i.into()))382	}383	fn is_cheap(&self) -> bool {384		true385	}386}387388#[derive(Debug, Trace)]389pub struct ReverseArray(pub ArrValue);390impl ArrayLike for ReverseArray {391	fn len(&self) -> usize {392		self.0.len()393	}394395	fn get(&self, index: usize) -> Result<Option<Val>> {396		self.0.get(self.0.len() - index - 1)397	}398399	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {400		self.0.get_lazy(self.0.len() - index - 1)401	}402403	fn get_cheap(&self, index: usize) -> Option<Val> {404		self.0.get_cheap(self.0.len() - index - 1)405	}406	fn is_cheap(&self) -> bool {407		self.0.is_cheap()408	}409}410411#[derive(Trace, Clone, Debug)]412pub enum ArrayMapper {413	Plain(NativeFn!((Val) -> Val)),414	WithIndex(NativeFn!((u32, Val) -> Val)),415}416417#[derive(Trace, Debug, Clone)]418pub struct MappedArray {419	inner: ArrValue,420	cached: Cc<RefCell<Vec<ArrayThunk>>>,421	mapper: ArrayMapper,422}423impl MappedArray {424	pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {425		let len = inner.len();426		Self {427			inner,428			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),429			mapper,430		}431	}432	fn evaluate(&self, index: usize, value: Val) -> Result<Val> {433		match &self.mapper {434			ArrayMapper::Plain(f) => f.call(value),435			ArrayMapper::WithIndex(f) => f.call(index as u32, value),436		}437	}438}439impl ArrayLike for MappedArray {440	fn len(&self) -> usize {441		self.cached.borrow().len()442	}443444	fn get(&self, index: usize) -> Result<Option<Val>> {445		if index >= self.len() {446			return Ok(None);447		}448		match &self.cached.borrow()[index] {449			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),450			ArrayThunk::Errored(e) => return Err(e.clone()),451			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),452			ArrayThunk::Waiting => {}453		}454455		let ArrayThunk::Waiting =456			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)457		else {458			unreachable!()459		};460461		let val = self462			.inner463			.get(index)464			.transpose()465			.expect("index checked")466			.and_then(|r| self.evaluate(index, r));467468		let new_value = match val {469			Ok(v) => v,470			Err(e) => {471				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());472				return Err(e);473			}474		};475		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());476		Ok(Some(new_value))477	}478	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {479		#[derive(Trace)]480		struct MappedArrayThunk {481			arr: MappedArray,482			index: usize,483		}484		impl ThunkValue for MappedArrayThunk {485			type Output = Val;486487			fn get(&self) -> Result<Self::Output> {488				self.arr.get(self.index).transpose().expect("index checked")489			}490		}491492		if index >= self.len() {493			return None;494		}495		match &self.cached.borrow()[index] {496			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),497			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),498			ArrayThunk::Waiting | ArrayThunk::Pending => {}499		}500501		Some(Thunk::new(MappedArrayThunk {502			arr: self.clone(),503			index,504		}))505	}506507	fn get_cheap(&self, _index: usize) -> Option<Val> {508		None509	}510	fn is_cheap(&self) -> bool {511		false512	}513}514515#[derive(Trace, Debug)]516pub struct RepeatedArray {517	data: ArrValue,518	repeats: usize,519	total_len: usize,520}521impl RepeatedArray {522	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {523		let total_len = data.len().checked_mul(repeats)?;524		Some(Self {525			data,526			repeats,527			total_len,528		})529	}530}531532impl ArrayLike for RepeatedArray {533	fn len(&self) -> usize {534		self.total_len535	}536537	fn get(&self, index: usize) -> Result<Option<Val>> {538		if index > self.total_len {539			return Ok(None);540		}541		self.data.get(index % self.data.len())542	}543544	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {545		if index > self.total_len {546			return None;547		}548		self.data.get_lazy(index % self.data.len())549	}550551	fn get_cheap(&self, index: usize) -> Option<Val> {552		if index > self.total_len {553			return None;554		}555		self.data.get_cheap(index % self.data.len())556	}557	fn is_cheap(&self) -> bool {558		self.data.is_cheap()559	}560}561562#[derive(Trace, Debug)]563pub struct PickObjectValues {564	obj: ObjValue,565	keys: Vec<IStr>,566}567568impl PickObjectValues {569	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {570		Self { obj, keys }571	}572}573574impl ArrayLike for PickObjectValues {575	fn len(&self) -> usize {576		self.keys.len()577	}578579	fn get(&self, index: usize) -> Result<Option<Val>> {580		let Some(key) = self.keys.get(index) else {581			return Ok(None);582		};583		Ok(Some(self.obj.get_or_bail(key.clone())?))584	}585586	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {587		let key = self.keys.get(index)?;588		Some(self.obj.get_lazy_or_bail(key.clone()))589	}590591	fn get_cheap(&self, _index: usize) -> Option<Val> {592		None593	}594595	fn is_cheap(&self) -> bool {596		false597	}598}599600#[derive(Trace, Debug)]601pub struct PickObjectKeyValues {602	obj: ObjValue,603	keys: Vec<IStr>,604}605606impl PickObjectKeyValues {607	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {608		Self { obj, keys }609	}610}611612#[derive(Typed, IntoUntyped)]613pub struct KeyValue {614	key: IStr,615	value: Thunk<Val>,616}617618impl ArrayLike for PickObjectKeyValues {619	fn len(&self) -> usize {620		self.keys.len()621	}622623	fn get(&self, index: usize) -> Result<Option<Val>> {624		let Some(key) = self.keys.get(index) else {625			return Ok(None);626		};627		Ok(Some(628			KeyValue::into_untyped(KeyValue {629				key: key.clone(),630				value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),631			})632			.expect("convertible"),633		))634	}635636	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {637		let key = self.keys.get(index)?;638		// Nothing can fail in the key part, yet value is still639		// lazy-evaluated640		Some(Thunk::evaluated(641			KeyValue::into_untyped(KeyValue {642				key: key.clone(),643				value: self.obj.get_lazy_or_bail(key.clone()),644			})645			.expect("convertible"),646		))647	}648649	fn get_cheap(&self, _index: usize) -> Option<Val> {650		None651	}652653	fn is_cheap(&self) -> bool {654		false655	}656}