git.delta.rocks / jrsonnet / refs/commits / 8a6eedd4997f

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs12.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,10	evaluate,11	function::FuncVal,12	val::{StrValue, ThunkValue},13	Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Any + Trace + Debug {17	fn len(&self) -> usize;18	fn is_empty(&self) -> bool {19		self.len() == 020	}21	fn get(&self, index: usize) -> Result<Option<Val>>;22	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;23	fn get_cheap(&self, index: usize) -> Option<Val>;2425	fn is_cheap(&self) -> bool;26}2728#[derive(Debug, Trace)]29pub struct SliceArray {30	pub(crate) inner: ArrValue,31	pub(crate) from: u32,32	pub(crate) to: u32,33	pub(crate) step: u32,34}3536impl SliceArray {37	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {38		self.inner39			.iter()40			.skip(self.from as usize)41			.take((self.to - self.from) as usize)42			.step_by(self.step as usize)43	}4445	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {46		self.inner47			.iter_lazy()48			.skip(self.from as usize)49			.take((self.to - self.from) as usize)50			.step_by(self.step as usize)51	}5253	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {54		Some(55			self.inner56				.iter_cheap()?57				.skip(self.from as usize)58				.take((self.to - self.from) as usize)59				.step_by(self.step as usize),60		)61	}62}63impl ArrayLike for SliceArray {64	fn len(&self) -> usize {65		iter::repeat(())66			.take((self.to - self.from) as usize)67			.step_by(self.step as usize)68			.count()69	}7071	fn get(&self, index: usize) -> Result<Option<Val>> {72		self.iter().nth(index).transpose()73	}7475	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76		self.iter_lazy().nth(index)77	}7879	fn get_cheap(&self, index: usize) -> Option<Val> {80		self.iter_cheap()?.nth(index)81	}82	fn is_cheap(&self) -> bool {83		self.inner.is_cheap()84	}85}8687#[derive(Trace, Debug)]88pub struct CharArray(pub Vec<char>);89impl ArrayLike for CharArray {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.0104			.get(index)105			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))106	}107	fn is_cheap(&self) -> bool {108		true109	}110}111112#[derive(Trace, Debug)]113pub struct BytesArray(pub IBytes);114impl ArrayLike for BytesArray {115	fn len(&self) -> usize {116		self.0.len()117	}118119	fn get(&self, index: usize) -> Result<Option<Val>> {120		Ok(self.get_cheap(index))121	}122123	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {124		self.get_cheap(index).map(Thunk::evaluated)125	}126127	fn get_cheap(&self, index: usize) -> Option<Val> {128		self.0.get(index).map(|v| Val::Num(f64::from(*v)))129	}130	fn is_cheap(&self) -> bool {131		true132	}133}134135#[derive(Debug, Trace, Clone)]136enum ArrayThunk<T: 'static + Trace> {137	Computed(Val),138	Errored(Error),139	Waiting(T),140	Pending,141}142143#[derive(Debug, Trace, Clone)]144pub struct ExprArray {145	ctx: Context,146	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,147}148impl ExprArray {149	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {150		Self {151			ctx,152			cached: Cc::new(RefCell::new(153				items.into_iter().map(ArrayThunk::Waiting).collect(),154			)),155		}156	}157}158impl ArrayLike for ExprArray {159	fn len(&self) -> usize {160		self.cached.borrow().len()161	}162	fn get(&self, index: usize) -> Result<Option<Val>> {163		if index >= self.len() {164			return Ok(None);165		}166		match &self.cached.borrow()[index] {167			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),168			ArrayThunk::Errored(e) => return Err(e.clone()),169			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),170			ArrayThunk::Waiting(..) => {}171		};172173		let ArrayThunk::Waiting(expr) =174			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)175		else {176			unreachable!()177		};178179		let new_value = match evaluate(self.ctx.clone(), &expr) {180			Ok(v) => v,181			Err(e) => {182				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());183				return Err(e);184			}185		};186		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());187		Ok(Some(new_value))188	}189	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {190		#[derive(Trace)]191		struct ArrayElement {192			arr_thunk: ExprArray,193			index: usize,194		}195196		impl ThunkValue for ArrayElement {197			type Output = Val;198199			fn get(self: Box<Self>) -> Result<Self::Output> {200				self.arr_thunk201					.get(self.index)202					.transpose()203					.expect("index checked")204			}205		}206207		if index >= self.len() {208			return None;209		}210		match &self.cached.borrow()[index] {211			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),212			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),213			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}214		};215216		Some(Thunk::new(ArrayElement {217			arr_thunk: self.clone(),218			index,219		}))220	}221	fn get_cheap(&self, _index: usize) -> Option<Val> {222		None223	}224	fn is_cheap(&self) -> bool {225		false226	}227}228229#[derive(Trace, Debug)]230pub struct ExtendedArray {231	pub a: ArrValue,232	pub b: ArrValue,233	split: usize,234	len: usize,235}236impl ExtendedArray {237	pub fn new(a: ArrValue, b: ArrValue) -> Self {238		let a_len = a.len();239		let b_len = b.len();240		Self {241			a,242			b,243			split: a_len,244			len: a_len.checked_add(b_len).expect("too large array value"),245		}246	}247}248249struct WithExactSize<I>(I, usize);250impl<I, T> Iterator for WithExactSize<I>251where252	I: Iterator<Item = T>,253{254	type Item = T;255256	fn next(&mut self) -> Option<Self::Item> {257		self.0.next()258	}259	fn nth(&mut self, n: usize) -> Option<Self::Item> {260		self.0.nth(n)261	}262	fn size_hint(&self) -> (usize, Option<usize>) {263		(self.1, Some(self.1))264	}265}266impl<I> DoubleEndedIterator for WithExactSize<I>267where268	I: DoubleEndedIterator,269{270	fn next_back(&mut self) -> Option<Self::Item> {271		self.0.next_back()272	}273	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {274		self.0.nth_back(n)275	}276}277impl<I> ExactSizeIterator for WithExactSize<I>278where279	I: Iterator,280{281	fn len(&self) -> usize {282		self.1283	}284}285impl ArrayLike for ExtendedArray {286	fn get(&self, index: usize) -> Result<Option<Val>> {287		if self.split > index {288			self.a.get(index)289		} else {290			self.b.get(index - self.split)291		}292	}293	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {294		if self.split > index {295			self.a.get_lazy(index)296		} else {297			self.b.get_lazy(index - self.split)298		}299	}300301	fn len(&self) -> usize {302		self.len303	}304305	fn get_cheap(&self, index: usize) -> Option<Val> {306		if self.split > index {307			self.a.get_cheap(index)308		} else {309			self.b.get_cheap(index - self.split)310		}311	}312	fn is_cheap(&self) -> bool {313		self.a.is_cheap() && self.b.is_cheap()314	}315}316317#[derive(Trace, Debug)]318pub struct LazyArray(pub Vec<Thunk<Val>>);319impl ArrayLike for LazyArray {320	fn len(&self) -> usize {321		self.0.len()322	}323	fn get(&self, index: usize) -> Result<Option<Val>> {324		let Some(v) = self.0.get(index) else {325			return Ok(None);326		};327		v.evaluate().map(Some)328	}329	fn get_cheap(&self, _index: usize) -> Option<Val> {330		None331	}332	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {333		self.0.get(index).cloned()334	}335	fn is_cheap(&self) -> bool {336		false337	}338}339340#[derive(Trace, Debug)]341pub struct EagerArray(pub Vec<Val>);342impl ArrayLike for EagerArray {343	fn len(&self) -> usize {344		self.0.len()345	}346347	fn get(&self, index: usize) -> Result<Option<Val>> {348		Ok(self.0.get(index).cloned())349	}350351	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {352		self.0.get(index).cloned().map(Thunk::evaluated)353	}354355	fn get_cheap(&self, index: usize) -> Option<Val> {356		self.0.get(index).cloned()357	}358	fn is_cheap(&self) -> bool {359		true360	}361}362363/// Inclusive range type364#[derive(Debug, Trace, PartialEq, Eq)]365pub struct RangeArray {366	start: i32,367	end: i32,368}369impl RangeArray {370	pub fn empty() -> Self {371		Self::new_exclusive(0, 0)372	}373	pub fn new_exclusive(start: i32, end: i32) -> Self {374		end.checked_sub(1)375			.map_or_else(Self::empty, |end| Self { start, end })376	}377	pub fn new_inclusive(start: i32, end: i32) -> Self {378		Self { start, end }379	}380	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {381		WithExactSize(382			self.start..=self.end,383			(self.end as usize)384				.wrapping_sub(self.start as usize)385				.wrapping_add(1),386		)387	}388}389390impl ArrayLike for RangeArray {391	fn len(&self) -> usize {392		self.range().len()393	}394	fn is_empty(&self) -> bool {395		self.range().len() == 0396	}397398	fn get(&self, index: usize) -> Result<Option<Val>> {399		Ok(self.get_cheap(index))400	}401402	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {403		self.get_cheap(index).map(Thunk::evaluated)404	}405406	fn get_cheap(&self, index: usize) -> Option<Val> {407		self.range().nth(index).map(|i| Val::Num(f64::from(i)))408	}409	fn is_cheap(&self) -> bool {410		true411	}412}413414#[derive(Debug, Trace)]415pub struct ReverseArray(pub ArrValue);416impl ArrayLike for ReverseArray {417	fn len(&self) -> usize {418		self.0.len()419	}420421	fn get(&self, index: usize) -> Result<Option<Val>> {422		self.0.get(self.0.len() - index - 1)423	}424425	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {426		self.0.get_lazy(self.0.len() - index - 1)427	}428429	fn get_cheap(&self, index: usize) -> Option<Val> {430		self.0.get_cheap(self.0.len() - index - 1)431	}432	fn is_cheap(&self) -> bool {433		self.0.is_cheap()434	}435}436437#[derive(Trace, Debug, Clone)]438pub struct MappedArray {439	inner: ArrValue,440	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,441	mapper: FuncVal,442}443impl MappedArray {444	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {445		let len = inner.len();446		Self {447			inner,448			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),449			mapper,450		}451	}452}453impl ArrayLike for MappedArray {454	fn len(&self) -> usize {455		self.cached.borrow().len()456	}457458	fn get(&self, index: usize) -> Result<Option<Val>> {459		if index >= self.len() {460			return Ok(None);461		}462		match &self.cached.borrow()[index] {463			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),464			ArrayThunk::Errored(e) => return Err(e.clone()),465			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),466			ArrayThunk::Waiting(..) => {}467		};468469		let ArrayThunk::Waiting(_) =470			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)471		else {472			unreachable!()473		};474475		let val = self476			.inner477			.get(index)478			.transpose()479			.expect("index checked")480			.and_then(|r| self.mapper.evaluate_simple(&(r,), false));481482		let new_value = match val {483			Ok(v) => v,484			Err(e) => {485				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());486				return Err(e);487			}488		};489		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());490		Ok(Some(new_value))491	}492	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {493		#[derive(Trace)]494		struct ArrayElement {495			arr_thunk: MappedArray,496			index: usize,497		}498499		impl ThunkValue for ArrayElement {500			type Output = Val;501502			fn get(self: Box<Self>) -> Result<Self::Output> {503				self.arr_thunk504					.get(self.index)505					.transpose()506					.expect("index checked")507			}508		}509510		if index >= self.len() {511			return None;512		}513		match &self.cached.borrow()[index] {514			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),515			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),516			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}517		};518519		Some(Thunk::new(ArrayElement {520			arr_thunk: self.clone(),521			index,522		}))523	}524525	fn get_cheap(&self, _index: usize) -> Option<Val> {526		None527	}528	fn is_cheap(&self) -> bool {529		false530	}531}532533#[derive(Trace, Debug)]534pub struct RepeatedArray {535	data: ArrValue,536	repeats: usize,537	total_len: usize,538}539impl RepeatedArray {540	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {541		let total_len = data.len().checked_mul(repeats)?;542		Some(Self {543			data,544			repeats,545			total_len,546		})547	}548}549550impl ArrayLike for RepeatedArray {551	fn len(&self) -> usize {552		self.total_len553	}554555	fn get(&self, index: usize) -> Result<Option<Val>> {556		if index > self.total_len {557			return Ok(None);558		}559		self.data.get(index % self.data.len())560	}561562	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {563		if index > self.total_len {564			return None;565		}566		self.data.get_lazy(index % self.data.len())567	}568569	fn get_cheap(&self, index: usize) -> Option<Val> {570		if index > self.total_len {571			return None;572		}573		self.data.get_cheap(index % self.data.len())574	}575	fn is_cheap(&self) -> bool {576		self.data.is_cheap()577	}578}