git.delta.rocks / jrsonnet / refs/commits / 88a0ba11fe45

difftreelog

source

crates/jrsonnet-evaluator/src/val.rs16.9 KiBsourcehistory
1use std::{cell::RefCell, fmt::Debug, rc::Rc};23use gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_types::ValType;67use crate::{8	cc_ptr_eq,9	error::{Error::*, LocError},10	function::FuncVal,11	gc::TraceBox,12	stdlib::manifest::{13		manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,14	},15	throw, ObjValue, Result, State,16};1718pub trait ThunkValue: Trace {19	type Output;20	fn get(self: Box<Self>, s: State) -> Result<Self::Output>;21}2223#[derive(Trace)]24enum ThunkInner<T> {25	Computed(T),26	Errored(LocError),27	Waiting(TraceBox<dyn ThunkValue<Output = T>>),28	Pending,29}3031#[allow(clippy::module_name_repetitions)]32#[derive(Clone, Trace)]33pub struct Thunk<T>(Cc<RefCell<ThunkInner<T>>>);34impl<T> Thunk<T>35where36	T: Clone + Trace,37{38	pub fn new(f: TraceBox<dyn ThunkValue<Output = T>>) -> Self {39		Self(Cc::new(RefCell::new(ThunkInner::Waiting(f))))40	}41	pub fn evaluated(val: T) -> Self {42		Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))43	}44	pub fn force(&self, s: State) -> Result<()> {45		self.evaluate(s)?;46		Ok(())47	}48	pub fn evaluate(&self, s: State) -> Result<T> {49		match &*self.0.borrow() {50			ThunkInner::Computed(v) => return Ok(v.clone()),51			ThunkInner::Errored(e) => return Err(e.clone()),52			ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),53			ThunkInner::Waiting(..) => (),54		};55		let value = if let ThunkInner::Waiting(value) =56			std::mem::replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)57		{58			value59		} else {60			unreachable!()61		};62		let new_value = match value.0.get(s) {63			Ok(v) => v,64			Err(e) => {65				*self.0.borrow_mut() = ThunkInner::Errored(e.clone());66				return Err(e);67			}68		};69		*self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());70		Ok(new_value)71	}72}7374impl<T: Debug> Debug for Thunk<T> {75	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {76		write!(f, "Lazy")77	}78}79impl<T> PartialEq for Thunk<T> {80	fn eq(&self, other: &Self) -> bool {81		cc_ptr_eq(&self.0, &other.0)82	}83}8485#[derive(Clone)]86pub enum ManifestFormat {87	YamlStream(Box<ManifestFormat>),88	Yaml {89		padding: usize,90		#[cfg(feature = "exp-preserve-order")]91		preserve_order: bool,92	},93	Json {94		padding: usize,95		#[cfg(feature = "exp-preserve-order")]96		preserve_order: bool,97	},98	ToString,99	String,100}101impl ManifestFormat {102	#[cfg(feature = "exp-preserve-order")]103	fn preserve_order(&self) -> bool {104		match self {105			ManifestFormat::YamlStream(s) => s.preserve_order(),106			ManifestFormat::Yaml { preserve_order, .. } => *preserve_order,107			ManifestFormat::Json { preserve_order, .. } => *preserve_order,108			ManifestFormat::ToString => false,109			ManifestFormat::String => false,110		}111	}112}113114#[derive(Debug, Clone, Trace)]115pub struct Slice {116	pub(crate) inner: ArrValue,117	pub(crate) from: u32,118	pub(crate) to: u32,119	pub(crate) step: u32,120}121impl Slice {122	const fn from(&self) -> usize {123		self.from as usize124	}125	const fn to(&self) -> usize {126		self.to as usize127	}128	const fn step(&self) -> usize {129		self.step as usize130	}131	const fn len(&self) -> usize {132		// TODO: use div_ceil133		let diff = self.to() - self.from();134		let rem = diff % self.step();135		let div = diff / self.step();136137		if rem == 0 {138			div139		} else {140			div + 1141		}142	}143}144145#[derive(Debug, Clone, Trace)]146#[force_tracking]147pub enum ArrValue {148	Bytes(#[skip_trace] Rc<[u8]>),149	Lazy(Cc<Vec<Thunk<Val>>>),150	Eager(Cc<Vec<Val>>),151	Extended(Box<(Self, Self)>),152	Range(i32, i32),153	Slice(Box<Slice>),154	Reversed(Box<Self>),155}156impl ArrValue {157	pub fn new_eager() -> Self {158		Self::Eager(Cc::new(Vec::new()))159	}160161	/// # Panics162	/// If a > b163	pub fn new_range(a: i32, b: i32) -> Self {164		assert!(a <= b);165		Self::Range(a, b)166	}167168	/// # Panics169	/// If passed numbers are incorrect170	#[must_use]171	pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {172		let len = self.len();173		let from = from.unwrap_or(0);174		let to = to.unwrap_or(len).min(len);175		let step = step.unwrap_or(1);176		assert!(from < to);177		assert!(step > 0);178179		Self::Slice(Box::new(Slice {180			inner: self,181			from: from as u32,182			to: to as u32,183			step: step as u32,184		}))185	}186187	pub fn len(&self) -> usize {188		match self {189			Self::Bytes(i) => i.len(),190			Self::Lazy(l) => l.len(),191			Self::Eager(e) => e.len(),192			Self::Extended(v) => v.0.len() + v.1.len(),193			Self::Range(a, b) => a.abs_diff(*b) as usize + 1,194			Self::Reversed(i) => i.len(),195			Self::Slice(s) => s.len(),196		}197	}198199	pub fn is_empty(&self) -> bool {200		self.len() == 0201	}202203	pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {204		match self {205			Self::Bytes(i) => i206				.get(index)207				.map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))),208			Self::Lazy(vec) => {209				if let Some(v) = vec.get(index) {210					Ok(Some(v.evaluate(s)?))211				} else {212					Ok(None)213				}214			}215			Self::Eager(vec) => Ok(vec.get(index).cloned()),216			Self::Extended(v) => {217				let a_len = v.0.len();218				if a_len > index {219					v.0.get(s, index)220				} else {221					v.1.get(s, index - a_len)222				}223			}224			Self::Range(a, _) => {225				if index >= self.len() {226					return Ok(None);227				}228				Ok(Some(Val::Num(((*a as isize) + index as isize) as f64)))229			}230			Self::Reversed(v) => {231				let len = v.len();232				if index >= len {233					return Ok(None);234				}235				v.get(s, len - index - 1)236			}237			Self::Slice(v) => {238				let index = v.from() + index * v.step();239				if index >= v.to() {240					return Ok(None);241				}242				v.inner.get(s, index as usize)243			}244		}245	}246247	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {248		match self {249			Self::Bytes(i) => i250				.get(index)251				.map(|b| Thunk::evaluated(Val::Num(f64::from(*b)))),252			Self::Lazy(vec) => vec.get(index).cloned(),253			Self::Eager(vec) => vec.get(index).cloned().map(Thunk::evaluated),254			Self::Extended(v) => {255				let a_len = v.0.len();256				if a_len > index {257					v.0.get_lazy(index)258				} else {259					v.1.get_lazy(index - a_len)260				}261			}262			Self::Range(a, _) => {263				if index >= self.len() {264					return None;265				}266				Some(Thunk::evaluated(Val::Num(267					((*a as isize) + index as isize) as f64,268				)))269			}270			Self::Reversed(v) => {271				let len = v.len();272				if index >= len {273					return None;274				}275				v.get_lazy(len - index - 1)276			}277			Self::Slice(s) => {278				let index = s.from() + index * s.step();279				if index >= s.to() {280					return None;281				}282				s.inner.get_lazy(index as usize)283			}284		}285	}286287	pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {288		Ok(match self {289			Self::Bytes(i) => {290				let mut out = Vec::with_capacity(i.len());291				for v in i.iter() {292					out.push(Val::Num(f64::from(*v)));293				}294				Cc::new(out)295			}296			Self::Lazy(vec) => {297				let mut out = Vec::with_capacity(vec.len());298				for item in vec.iter() {299					out.push(item.evaluate(s.clone())?);300				}301				Cc::new(out)302			}303			Self::Eager(vec) => vec.clone(),304			Self::Extended(_v) => {305				let mut out = Vec::with_capacity(self.len());306				for item in self.iter(s) {307					out.push(item?);308				}309				Cc::new(out)310			}311			Self::Range(a, b) => {312				let mut out = Vec::with_capacity(self.len());313				for i in *a..*b {314					out.push(Val::Num(f64::from(i)));315				}316				Cc::new(out)317			}318			Self::Reversed(r) => {319				let mut r = r.evaluated(s)?;320				Cc::update_with(&mut r, |v| v.reverse());321				r322			}323			Self::Slice(v) => {324				let mut out = Vec::with_capacity(v.inner.len());325				for v in v326					.inner327					.iter_lazy()328					.skip(v.from())329					.take(v.to() - v.from())330					.step_by(v.step())331				{332					out.push(v.evaluate(s.clone())?);333				}334				Cc::new(out)335			}336		})337	}338339	pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {340		(0..self.len()).map(move |idx| match self {341			Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),342			Self::Lazy(l) => l[idx].evaluate(s.clone()),343			Self::Eager(e) => Ok(e[idx].clone()),344			Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => {345				self.get(s.clone(), idx).map(|e| e.expect("idx < len"))346			}347		})348	}349350	pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = Thunk<Val>> + '_ {351		(0..self.len()).map(move |idx| match self {352			Self::Bytes(b) => Thunk::evaluated(Val::Num(f64::from(b[idx]))),353			Self::Lazy(l) => l[idx].clone(),354			Self::Eager(e) => Thunk::evaluated(e[idx].clone()),355			Self::Slice(..) | Self::Extended(..) | Self::Range(..) | Self::Reversed(..) => {356				self.get_lazy(idx).expect("idx < len")357			}358		})359	}360361	#[must_use]362	pub fn reversed(self) -> Self {363		Self::Reversed(Box::new(self))364	}365366	pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {367		let mut out = Vec::with_capacity(self.len());368369		for value in self.iter(s) {370			out.push(mapper(value?)?);371		}372373		Ok(Self::Eager(Cc::new(out)))374	}375376	pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {377		let mut out = Vec::with_capacity(self.len());378379		for value in self.iter(s) {380			let value = value?;381			if filter(&value)? {382				out.push(value);383			}384		}385386		Ok(Self::Eager(Cc::new(out)))387	}388389	pub fn ptr_eq(a: &Self, b: &Self) -> bool {390		match (a, b) {391			(Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),392			(Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),393			_ => false,394		}395	}396}397398impl From<Vec<Thunk<Val>>> for ArrValue {399	fn from(v: Vec<Thunk<Val>>) -> Self {400		Self::Lazy(Cc::new(v))401	}402}403404impl From<Vec<Val>> for ArrValue {405	fn from(v: Vec<Val>) -> Self {406		Self::Eager(Cc::new(v))407	}408}409410#[allow(clippy::module_name_repetitions)]411pub enum IndexableVal {412	Str(IStr),413	Arr(ArrValue),414}415416#[derive(Debug, Clone, Trace)]417pub enum Val {418	Bool(bool),419	Null,420	Str(IStr),421	Num(f64),422	Arr(ArrValue),423	Obj(ObjValue),424	Func(FuncVal),425}426427impl Val {428	pub const fn as_bool(&self) -> Option<bool> {429		match self {430			Val::Bool(v) => Some(*v),431			_ => None,432		}433	}434	pub const fn as_null(&self) -> Option<()> {435		match self {436			Val::Null => Some(()),437			_ => None,438		}439	}440	pub fn as_str(&self) -> Option<IStr> {441		match self {442			Val::Str(s) => Some(s.clone()),443			_ => None,444		}445	}446	pub const fn as_num(&self) -> Option<f64> {447		match self {448			Val::Num(n) => Some(*n),449			_ => None,450		}451	}452	pub fn as_arr(&self) -> Option<ArrValue> {453		match self {454			Val::Arr(a) => Some(a.clone()),455			_ => None,456		}457	}458	pub fn as_obj(&self) -> Option<ObjValue> {459		match self {460			Val::Obj(o) => Some(o.clone()),461			_ => None,462		}463	}464	pub fn as_func(&self) -> Option<FuncVal> {465		match self {466			Val::Func(f) => Some(f.clone()),467			_ => None,468		}469	}470471	/// Creates `Val::Num` after checking for numeric overflow.472	/// As numbers are `f64`, we can just check for their finity.473	pub fn new_checked_num(num: f64) -> Result<Self> {474		if num.is_finite() {475			Ok(Self::Num(num))476		} else {477			throw!(RuntimeError("overflow".into()))478		}479	}480481	pub const fn value_type(&self) -> ValType {482		match self {483			Self::Str(..) => ValType::Str,484			Self::Num(..) => ValType::Num,485			Self::Arr(..) => ValType::Arr,486			Self::Obj(..) => ValType::Obj,487			Self::Bool(_) => ValType::Bool,488			Self::Null => ValType::Null,489			Self::Func(..) => ValType::Func,490		}491	}492493	pub fn to_string(&self, s: State) -> Result<IStr> {494		Ok(match self {495			Self::Bool(true) => "true".into(),496			Self::Bool(false) => "false".into(),497			Self::Null => "null".into(),498			Self::Str(s) => s.clone(),499			v => manifest_json_ex(500				s,501				v,502				&ManifestJsonOptions {503					padding: "",504					mtype: ManifestType::ToString,505					newline: "\n",506					key_val_sep: ": ",507					#[cfg(feature = "exp-preserve-order")]508					preserve_order: false,509				},510			)?511			.into(),512		})513	}514515	/// Expects value to be object, outputs (key, manifested value) pairs516	pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {517		let obj = match self {518			Self::Obj(obj) => obj,519			_ => throw!(MultiManifestOutputIsNotAObject),520		};521		let keys = obj.fields(522			#[cfg(feature = "exp-preserve-order")]523			ty.preserve_order(),524		);525		let mut out = Vec::with_capacity(keys.len());526		for key in keys {527			let value = obj528				.get(s.clone(), key.clone())?529				.expect("item in object")530				.manifest(s.clone(), ty)?;531			out.push((key, value));532		}533		Ok(out)534	}535536	/// Expects value to be array, outputs manifested values537	pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result<Vec<IStr>> {538		let arr = match self {539			Self::Arr(a) => a,540			_ => throw!(StreamManifestOutputIsNotAArray),541		};542		let mut out = Vec::with_capacity(arr.len());543		for i in arr.iter(s.clone()) {544			out.push(i?.manifest(s.clone(), ty)?);545		}546		Ok(out)547	}548549	pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result<IStr> {550		Ok(match ty {551			ManifestFormat::YamlStream(format) => {552				let arr = match self {553					Self::Arr(a) => a,554					_ => throw!(StreamManifestOutputIsNotAArray),555				};556				let mut out = String::new();557558				match format as &ManifestFormat {559					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),560					ManifestFormat::String => throw!(StreamManifestCannotNestString),561					_ => {}562				};563564				if !arr.is_empty() {565					for v in arr.iter(s.clone()) {566						out.push_str("---\n");567						out.push_str(&v?.manifest(s.clone(), format)?);568						out.push('\n');569					}570					out.push_str("...");571				}572573				out.into()574			}575			ManifestFormat::Yaml {576				padding,577				#[cfg(feature = "exp-preserve-order")]578				preserve_order,579			} => self.to_yaml(580				s,581				*padding,582				#[cfg(feature = "exp-preserve-order")]583				*preserve_order,584			)?,585			ManifestFormat::Json {586				padding,587				#[cfg(feature = "exp-preserve-order")]588				preserve_order,589			} => self.to_json(590				s,591				*padding,592				#[cfg(feature = "exp-preserve-order")]593				*preserve_order,594			)?,595			ManifestFormat::ToString => self.to_string(s)?,596			ManifestFormat::String => match self {597				Self::Str(s) => s.clone(),598				_ => throw!(StringManifestOutputIsNotAString),599			},600		})601	}602603	/// For manifestification604	pub fn to_json(605		&self,606		s: State,607		padding: usize,608		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,609	) -> Result<IStr> {610		manifest_json_ex(611			s,612			self,613			&ManifestJsonOptions {614				padding: &" ".repeat(padding),615				mtype: if padding == 0 {616					ManifestType::Minify617				} else {618					ManifestType::Manifest619				},620				newline: "\n",621				key_val_sep: ": ",622				#[cfg(feature = "exp-preserve-order")]623				preserve_order,624			},625		)626		.map(Into::into)627	}628629	/// Calls `std.manifestJson`630	pub fn to_std_json(631		&self,632		s: State,633		padding: usize,634		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,635	) -> Result<Rc<str>> {636		manifest_json_ex(637			s,638			self,639			&ManifestJsonOptions {640				padding: &" ".repeat(padding),641				mtype: ManifestType::Std,642				newline: "\n",643				key_val_sep: ": ",644				#[cfg(feature = "exp-preserve-order")]645				preserve_order,646			},647		)648		.map(Into::into)649	}650651	pub fn to_yaml(652		&self,653		s: State,654		padding: usize,655		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,656	) -> Result<IStr> {657		let padding = &" ".repeat(padding);658		manifest_yaml_ex(659			s,660			self,661			&ManifestYamlOptions {662				padding,663				arr_element_padding: padding,664				quote_keys: false,665				#[cfg(feature = "exp-preserve-order")]666				preserve_order,667			},668		)669		.map(Into::into)670	}671	pub fn into_indexable(self) -> Result<IndexableVal> {672		Ok(match self {673			Val::Str(s) => IndexableVal::Str(s),674			Val::Arr(arr) => IndexableVal::Arr(arr),675			_ => throw!(ValueIsNotIndexable(self.value_type())),676		})677	}678}679680const fn is_function_like(val: &Val) -> bool {681	matches!(val, Val::Func(_))682}683684/// Native implementation of `std.primitiveEquals`685pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {686	Ok(match (val_a, val_b) {687		(Val::Bool(a), Val::Bool(b)) => a == b,688		(Val::Null, Val::Null) => true,689		(Val::Str(a), Val::Str(b)) => a == b,690		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,691		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(692			"primitiveEquals operates on primitive types, got array".into(),693		)),694		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(695			"primitiveEquals operates on primitive types, got object".into(),696		)),697		(a, b) if is_function_like(a) && is_function_like(b) => {698			throw!(RuntimeError("cannot test equality of functions".into()))699		}700		(_, _) => false,701	})702}703704/// Native implementation of `std.equals`705pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result<bool> {706	if val_a.value_type() != val_b.value_type() {707		return Ok(false);708	}709	match (val_a, val_b) {710		(Val::Arr(a), Val::Arr(b)) => {711			if ArrValue::ptr_eq(a, b) {712				return Ok(true);713			}714			if a.len() != b.len() {715				return Ok(false);716			}717			for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) {718				if !equals(s.clone(), &a?, &b?)? {719					return Ok(false);720				}721			}722			Ok(true)723		}724		(Val::Obj(a), Val::Obj(b)) => {725			if ObjValue::ptr_eq(a, b) {726				return Ok(true);727			}728			let fields = a.fields(729				#[cfg(feature = "exp-preserve-order")]730				false,731			);732			if fields733				!= b.fields(734					#[cfg(feature = "exp-preserve-order")]735					false,736				) {737				return Ok(false);738			}739			for field in fields {740				if !equals(741					s.clone(),742					&a.get(s.clone(), field.clone())?.expect("field exists"),743					&b.get(s.clone(), field)?.expect("field exists"),744				)? {745					return Ok(false);746				}747			}748			Ok(true)749		}750		(a, b) => Ok(primitive_equals(a, b)?),751	}752}