git.delta.rocks / jrsonnet / refs/commits / 20cd69c85b05

difftreelog

source

crates/jrsonnet-evaluator/src/val.rs14.9 KiBsourcehistory
1use crate::{2	builtin::manifest::{3		manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,4	},5	cc_ptr_eq,6	error::{Error::*, LocError},7	evaluate,8	function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},9	gc::TraceBox,10	native::NativeCallback,11	throw, Context, ObjValue, Result,12};13use gcmodule::{Cc, Trace};14use jrsonnet_interner::IStr;15use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};16use jrsonnet_types::ValType;17use std::{cell::RefCell, fmt::Debug, rc::Rc};1819pub trait LazyValValue: Trace {20	fn get(self: Box<Self>) -> Result<Val>;21}2223#[derive(Trace)]24enum LazyValInternals {25	Computed(Val),26	Errored(LocError),27	Waiting(TraceBox<dyn LazyValValue>),28	Pending,29}3031#[derive(Clone, Trace)]32pub struct LazyVal(Cc<RefCell<LazyValInternals>>);33impl LazyVal {34	pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {35		Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))36	}37	pub fn new_resolved(val: Val) -> Self {38		Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))39	}40	pub fn force(&self) -> Result<()> {41		self.evaluate()?;42		Ok(())43	}44	pub fn evaluate(&self) -> Result<Val> {45		match &*self.0.borrow() {46			LazyValInternals::Computed(v) => return Ok(v.clone()),47			LazyValInternals::Errored(e) => return Err(e.clone()),48			LazyValInternals::Pending => return Err(RecursiveLazyValueEvaluation.into()),49			_ => (),50		};51		let value = if let LazyValInternals::Waiting(value) =52			std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending)53		{54			value55		} else {56			unreachable!()57		};58		let new_value = match value.0.get() {59			Ok(v) => v,60			Err(e) => {61				*self.0.borrow_mut() = LazyValInternals::Errored(e.clone());62				return Err(e);63			}64		};65		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());66		Ok(new_value)67	}68}6970impl Debug for LazyVal {71	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {72		write!(f, "Lazy")73	}74}75impl PartialEq for LazyVal {76	fn eq(&self, other: &Self) -> bool {77		cc_ptr_eq(&self.0, &other.0)78	}79}8081#[derive(Debug, PartialEq, Trace)]82pub struct FuncDesc {83	pub name: IStr,84	pub ctx: Context,85	pub params: ParamsDesc,86	pub body: LocExpr,87}8889#[derive(Trace)]90pub enum FuncVal {91	/// Plain function implemented in jsonnet92	Normal(FuncDesc),93	/// Standard library function94	StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),9596	Builtin(TraceBox<dyn Builtin>),97	/// Library functions implemented in native98	NativeExt(IStr, Cc<NativeCallback>),99}100101impl Debug for FuncVal {102	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {103		match self {104			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),105			Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),106			Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),107			Self::NativeExt(arg0, arg1) => {108				f.debug_tuple("NativeExt").field(arg0).field(arg1).finish()109			}110		}111	}112}113114impl PartialEq for FuncVal {115	fn eq(&self, other: &Self) -> bool {116		match (self, other) {117			(Self::Normal(a), Self::Normal(b)) => a == b,118			(Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn),119			(Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,120			(..) => false,121		}122	}123}124impl FuncVal {125	pub fn args_len(&self) -> usize {126		match self {127			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),128			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),129			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),130			Self::NativeExt(_, n) => n.params.iter().filter(|p| p.1.is_none()).count(),131		}132	}133	pub fn name(&self) -> IStr {134		match self {135			Self::Normal(normal) => normal.name.clone(),136			Self::StaticBuiltin(builtin) => builtin.name().into(),137			Self::Builtin(builtin) => builtin.name().into(),138			Self::NativeExt(n, _) => format!("native.{}", n).into(),139		}140	}141	pub fn evaluate(142		&self,143		call_ctx: Context,144		loc: Option<&ExprLocation>,145		args: &dyn ArgsLike,146		tailstrict: bool,147	) -> Result<Val> {148		match self {149			Self::Normal(func) => {150				let ctx = parse_function_call(151					call_ctx,152					func.ctx.clone(),153					&func.params,154					args,155					tailstrict,156				)?;157				evaluate(ctx, &func.body)158			}159			Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),160			Self::Builtin(b) => b.call(call_ctx, loc, args),161			Self::NativeExt(_name, handler) => {162				let args =163					parse_function_call(call_ctx, Context::new(), &handler.params, args, true)?;164				let mut out_args = Vec::with_capacity(handler.params.len());165				for p in handler.params.0.iter() {166					out_args.push(args.binding(p.0.clone())?.evaluate()?);167				}168				Ok(handler.call(loc.expect("todo").0.clone(), &out_args)?)169			}170		}171	}172	pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {173		self.evaluate(Context::default(), None, args, true)174	}175}176177#[derive(Clone)]178pub enum ManifestFormat {179	YamlStream(Box<ManifestFormat>),180	Yaml(usize),181	Json(usize),182	ToString,183	String,184}185186#[derive(Debug, Clone, Trace)]187#[force_tracking]188pub enum ArrValue {189	Bytes(#[skip_trace] Rc<[u8]>),190	Lazy(Cc<Vec<LazyVal>>),191	Eager(Cc<Vec<Val>>),192	Extended(Box<(Self, Self)>),193}194impl ArrValue {195	pub fn new_eager() -> Self {196		Self::Eager(Cc::new(Vec::new()))197	}198199	pub fn len(&self) -> usize {200		match self {201			Self::Bytes(i) => i.len(),202			Self::Lazy(l) => l.len(),203			Self::Eager(e) => e.len(),204			Self::Extended(v) => v.0.len() + v.1.len(),205		}206	}207208	pub fn is_empty(&self) -> bool {209		self.len() == 0210	}211212	pub fn get(&self, index: usize) -> Result<Option<Val>> {213		match self {214			Self::Bytes(i) => i215				.get(index)216				.map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),217			Self::Lazy(vec) => {218				if let Some(v) = vec.get(index) {219					Ok(Some(v.evaluate()?))220				} else {221					Ok(None)222				}223			}224			Self::Eager(vec) => Ok(vec.get(index).cloned()),225			Self::Extended(v) => {226				let a_len = v.0.len();227				if a_len > index {228					v.0.get(index)229				} else {230					v.1.get(index - a_len)231				}232			}233		}234	}235236	pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {237		match self {238			Self::Bytes(i) => i239				.get(index)240				.map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),241			Self::Lazy(vec) => vec.get(index).cloned(),242			Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),243			Self::Extended(v) => {244				let a_len = v.0.len();245				if a_len > index {246					v.0.get_lazy(index)247				} else {248					v.1.get_lazy(index - a_len)249				}250			}251		}252	}253254	pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {255		Ok(match self {256			Self::Bytes(i) => {257				let mut out = Vec::with_capacity(i.len());258				for v in i.iter() {259					out.push(Val::Num(*v as f64));260				}261				Cc::new(out)262			}263			Self::Lazy(vec) => {264				let mut out = Vec::with_capacity(vec.len());265				for item in vec.iter() {266					out.push(item.evaluate()?);267				}268				Cc::new(out)269			}270			Self::Eager(vec) => vec.clone(),271			Self::Extended(_v) => {272				let mut out = Vec::with_capacity(self.len());273				for item in self.iter() {274					out.push(item?);275				}276				Cc::new(out)277			}278		})279	}280281	pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {282		(0..self.len()).map(move |idx| match self {283			Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),284			Self::Lazy(l) => l[idx].evaluate(),285			Self::Eager(e) => Ok(e[idx].clone()),286			Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),287		})288	}289290	pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {291		(0..self.len()).map(move |idx| match self {292			Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),293			Self::Lazy(l) => l[idx].clone(),294			Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),295			Self::Extended(_) => self.get_lazy(idx).unwrap(),296		})297	}298299	pub fn reversed(self) -> Self {300		match self {301			Self::Bytes(b) => {302				let mut out = b.to_vec();303				out.reverse();304				Self::Bytes(out.into())305			}306			Self::Lazy(vec) => {307				let mut out = (&vec as &Vec<_>).clone();308				out.reverse();309				Self::Lazy(Cc::new(out))310			}311			Self::Eager(vec) => {312				let mut out = (&vec as &Vec<_>).clone();313				out.reverse();314				Self::Eager(Cc::new(out))315			}316			Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),317		}318	}319320	pub fn map(self, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {321		let mut out = Vec::with_capacity(self.len());322323		for value in self.iter() {324			out.push(mapper(value?)?);325		}326327		Ok(Self::Eager(Cc::new(out)))328	}329330	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {331		let mut out = Vec::with_capacity(self.len());332333		for value in self.iter() {334			let value = value?;335			if filter(&value)? {336				out.push(value);337			}338		}339340		Ok(Self::Eager(Cc::new(out)))341	}342343	pub fn ptr_eq(a: &Self, b: &Self) -> bool {344		match (a, b) {345			(Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),346			(Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),347			_ => false,348		}349	}350}351352impl From<Vec<LazyVal>> for ArrValue {353	fn from(v: Vec<LazyVal>) -> Self {354		Self::Lazy(Cc::new(v))355	}356}357358impl From<Vec<Val>> for ArrValue {359	fn from(v: Vec<Val>) -> Self {360		Self::Eager(Cc::new(v))361	}362}363364pub enum IndexableVal {365	Str(IStr),366	Arr(ArrValue),367}368369#[derive(Debug, Clone, Trace)]370pub enum Val {371	Bool(bool),372	Null,373	Str(IStr),374	Num(f64),375	Arr(ArrValue),376	Obj(ObjValue),377	Func(Cc<FuncVal>),378}379380impl Val {381	/// Creates `Val::Num` after checking for numeric overflow.382	/// As numbers are `f64`, we can just check for their finity.383	pub fn new_checked_num(num: f64) -> Result<Self> {384		if num.is_finite() {385			Ok(Self::Num(num))386		} else {387			throw!(RuntimeError("overflow".into()))388		}389	}390391	pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {392		Ok(match self {393			Val::Null => None,394			Val::Num(num) => Some(num),395			_ => throw!(TypeMismatch(396				context,397				vec![ValType::Null, ValType::Num],398				self.value_type()399			)),400		})401	}402	pub const fn value_type(&self) -> ValType {403		match self {404			Self::Str(..) => ValType::Str,405			Self::Num(..) => ValType::Num,406			Self::Arr(..) => ValType::Arr,407			Self::Obj(..) => ValType::Obj,408			Self::Bool(_) => ValType::Bool,409			Self::Null => ValType::Null,410			Self::Func(..) => ValType::Func,411		}412	}413414	pub fn to_string(&self) -> Result<IStr> {415		Ok(match self {416			Self::Bool(true) => "true".into(),417			Self::Bool(false) => "false".into(),418			Self::Null => "null".into(),419			Self::Str(s) => s.clone(),420			v => manifest_json_ex(421				v,422				&ManifestJsonOptions {423					padding: "",424					mtype: ManifestType::ToString,425					newline: "\n",426					key_val_sep: ": ",427				},428			)?429			.into(),430		})431	}432433	/// Expects value to be object, outputs (key, manifested value) pairs434	pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {435		let obj = match self {436			Self::Obj(obj) => obj,437			_ => throw!(MultiManifestOutputIsNotAObject),438		};439		let keys = obj.fields();440		let mut out = Vec::with_capacity(keys.len());441		for key in keys {442			let value = obj443				.get(key.clone())?444				.expect("item in object")445				.manifest(ty)?;446			out.push((key, value));447		}448		Ok(out)449	}450451	/// Expects value to be array, outputs manifested values452	pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {453		let arr = match self {454			Self::Arr(a) => a,455			_ => throw!(StreamManifestOutputIsNotAArray),456		};457		let mut out = Vec::with_capacity(arr.len());458		for i in arr.iter() {459			out.push(i?.manifest(ty)?);460		}461		Ok(out)462	}463464	pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {465		Ok(match ty {466			ManifestFormat::YamlStream(format) => {467				let arr = match self {468					Self::Arr(a) => a,469					_ => throw!(StreamManifestOutputIsNotAArray),470				};471				let mut out = String::new();472473				match format as &ManifestFormat {474					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),475					ManifestFormat::String => throw!(StreamManifestCannotNestString),476					_ => {}477				};478479				if !arr.is_empty() {480					for v in arr.iter() {481						out.push_str("---\n");482						out.push_str(&v?.manifest(format)?);483						out.push('\n');484					}485					out.push_str("...");486				}487488				out.into()489			}490			ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,491			ManifestFormat::Json(padding) => self.to_json(*padding)?,492			ManifestFormat::ToString => self.to_string()?,493			ManifestFormat::String => match self {494				Self::Str(s) => s.clone(),495				_ => throw!(StringManifestOutputIsNotAString),496			},497		})498	}499500	/// For manifestification501	pub fn to_json(&self, padding: usize) -> Result<IStr> {502		manifest_json_ex(503			self,504			&ManifestJsonOptions {505				padding: &" ".repeat(padding),506				mtype: if padding == 0 {507					ManifestType::Minify508				} else {509					ManifestType::Manifest510				},511				newline: "\n",512				key_val_sep: ": ",513			},514		)515		.map(|s| s.into())516	}517518	/// Calls `std.manifestJson`519	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {520		manifest_json_ex(521			self,522			&ManifestJsonOptions {523				padding: &" ".repeat(padding),524				mtype: ManifestType::Std,525				newline: "\n",526				key_val_sep: ": ",527			},528		)529		.map(|s| s.into())530	}531532	pub fn to_yaml(&self, padding: usize) -> Result<IStr> {533		let padding = &" ".repeat(padding);534		manifest_yaml_ex(535			self,536			&ManifestYamlOptions {537				padding,538				arr_element_padding: padding,539				quote_keys: false,540			},541		)542		.map(|s| s.into())543	}544	pub fn into_indexable(self) -> Result<IndexableVal> {545		Ok(match self {546			Val::Str(s) => IndexableVal::Str(s),547			Val::Arr(arr) => IndexableVal::Arr(arr),548			_ => throw!(ValueIsNotIndexable(self.value_type())),549		})550	}551}552553const fn is_function_like(val: &Val) -> bool {554	matches!(val, Val::Func(_))555}556557/// Native implementation of `std.primitiveEquals`558pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {559	Ok(match (val_a, val_b) {560		(Val::Bool(a), Val::Bool(b)) => a == b,561		(Val::Null, Val::Null) => true,562		(Val::Str(a), Val::Str(b)) => a == b,563		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,564		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(565			"primitiveEquals operates on primitive types, got array".into(),566		)),567		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(568			"primitiveEquals operates on primitive types, got object".into(),569		)),570		(a, b) if is_function_like(a) && is_function_like(b) => {571			throw!(RuntimeError("cannot test equality of functions".into()))572		}573		(_, _) => false,574	})575}576577/// Native implementation of `std.equals`578pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {579	if val_a.value_type() != val_b.value_type() {580		return Ok(false);581	}582	match (val_a, val_b) {583		(Val::Arr(a), Val::Arr(b)) => {584			if ArrValue::ptr_eq(a, b) {585				return Ok(true);586			}587			if a.len() != b.len() {588				return Ok(false);589			}590			for (a, b) in a.iter().zip(b.iter()) {591				if !equals(&a?, &b?)? {592					return Ok(false);593				}594			}595			Ok(true)596		}597		(Val::Obj(a), Val::Obj(b)) => {598			if ObjValue::ptr_eq(a, b) {599				return Ok(true);600			}601			let fields = a.fields();602			if fields != b.fields() {603				return Ok(false);604			}605			for field in fields {606				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {607					return Ok(false);608				}609			}610			Ok(true)611		}612		(a, b) => Ok(primitive_equals(a, b)?),613	}614}