git.delta.rocks / jrsonnet / refs/commits / 6e40dd49aa72

difftreelog

feat multi and stream manifests

Lach2020-08-23parent: #020afa6.patch.diff
in: master

5 files changed

modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -161,19 +161,152 @@
 	})
 }
 
+fn multi_to_raw(multi: Vec<(Rc<str>, Rc<str>)>) -> *const c_char {
+	let mut out = Vec::new();
+	for (i, (k, v)) in multi.iter().enumerate() {
+		if i != 0 {
+			out.push(0);
+		}
+		out.extend_from_slice(k.as_bytes());
+		out.push(0);
+		out.extend_from_slice(v.as_bytes());
+	}
+	out.push(0);
+	out.push(0);
+	let v = out.as_ptr();
+	std::mem::forget(out);
+	v as *const c_char
+}
+
+/// # Safety
 #[no_mangle]
-pub extern "C" fn jsonnet_evaluate_file_multi() {
-	todo!()
+pub unsafe extern "C" fn jsonnet_evaluate_file_multi(
+	vm: &EvaluationState,
+	filename: *const c_char,
+	error: &mut c_int,
+) -> *const c_char {
+	vm.run_in_state(|| {
+		let filename = CStr::from_ptr(filename);
+		match vm
+			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest_multi(v))
+		{
+			Ok(v) => {
+				*error = 0;
+				multi_to_raw(v)
+			}
+			Err(e) => {
+				*error = 1;
+				let out = vm.stringify_err(&e);
+				CString::new(&out as &str).unwrap().into_raw()
+			}
+		}
+	})
 }
+
+/// # Safety
 #[no_mangle]
-pub extern "C" fn jsonnet_evaluate_snippet_multi() {
-	todo!()
+pub unsafe extern "C" fn jsonnet_evaluate_snippet_multi(
+	vm: &EvaluationState,
+	filename: *const c_char,
+	snippet: *const c_char,
+	error: &mut c_int,
+) -> *const c_char {
+	vm.run_in_state(|| {
+		let filename = CStr::from_ptr(filename);
+		let snippet = CStr::from_ptr(snippet);
+		match vm
+			.evaluate_snippet_raw(
+				Rc::new(PathBuf::from(filename.to_str().unwrap())),
+				snippet.to_str().unwrap().into(),
+			)
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest_multi(v))
+		{
+			Ok(v) => {
+				*error = 0;
+				multi_to_raw(v)
+			}
+			Err(e) => {
+				*error = 1;
+				let out = vm.stringify_err(&e);
+				CString::new(&out as &str).unwrap().into_raw()
+			}
+		}
+	})
 }
+
+fn stream_to_raw(multi: Vec<Rc<str>>) -> *const c_char {
+	let mut out = Vec::new();
+	for (i, v) in multi.iter().enumerate() {
+		if i != 0 {
+			out.push(0);
+		}
+		out.extend_from_slice(v.as_bytes());
+	}
+	out.push(0);
+	out.push(0);
+	let v = out.as_ptr();
+	std::mem::forget(out);
+	v as *const c_char
+}
+
+/// # Safety
 #[no_mangle]
-pub extern "C" fn jsonnet_evaluate_file_stream() {
-	todo!()
+pub unsafe extern "C" fn jsonnet_evaluate_file_stream(
+	vm: &EvaluationState,
+	filename: *const c_char,
+	error: &mut c_int,
+) -> *const c_char {
+	vm.run_in_state(|| {
+		let filename = CStr::from_ptr(filename);
+		match vm
+			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest_stream(v))
+		{
+			Ok(v) => {
+				*error = 0;
+				stream_to_raw(v)
+			}
+			Err(e) => {
+				*error = 1;
+				let out = vm.stringify_err(&e);
+				CString::new(&out as &str).unwrap().into_raw()
+			}
+		}
+	})
 }
+
+/// # Safety
 #[no_mangle]
-pub extern "C" fn jsonnet_evaluate_snippet_stream() {
-	todo!()
+pub unsafe extern "C" fn jsonnet_evaluate_snippet_stream(
+	vm: &EvaluationState,
+	filename: *const c_char,
+	snippet: *const c_char,
+	error: &mut c_int,
+) -> *const c_char {
+	vm.run_in_state(|| {
+		let filename = CStr::from_ptr(filename);
+		let snippet = CStr::from_ptr(snippet);
+		match vm
+			.evaluate_snippet_raw(
+				Rc::new(PathBuf::from(filename.to_str().unwrap())),
+				snippet.to_str().unwrap().into(),
+			)
+			.and_then(|v| vm.with_tla(v))
+			.and_then(|v| vm.manifest_stream(v))
+		{
+			Ok(v) => {
+				*error = 0;
+				stream_to_raw(v)
+			}
+			Err(e) => {
+				*error = 1;
+				let out = vm.stringify_err(&e);
+				CString::new(&out as &str).unwrap().into_raw()
+			}
+		}
+	})
 }
modifiedcrates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -470,7 +470,7 @@
 	let mut tmp_out = String::new();
 
 	match code.convtype {
-		ConvTypeV::String => tmp_out.push_str(&value.clone().into_string()?),
+		ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),
 		ConvTypeV::Decimal => {
 			let value = value.clone().try_cast_num("%d/%u/%i requires number")?;
 			render_decimal(
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -95,8 +95,8 @@
 		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),
 		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),
 
-		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_string()?).into()),
-		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_string()?, s).into()),
+		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),
+		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),
 
 		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
 		(Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),
@@ -975,9 +975,9 @@
 			if assertion_result {
 				evaluate(context, returned)?
 			} else if let Some(msg) = msg {
-				throw!(AssertionFailed(evaluate(context, msg)?));
+				throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));
 			} else {
-				throw!(AssertionFailed(Val::Null));
+				throw!(AssertionFailed(Val::Null.to_string()?));
 			}
 		}
 		ErrorStmt(e) => push(
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -321,6 +321,12 @@
 	pub fn manifest(&self, val: Val) -> Result<Rc<str>> {
 		self.run_in_state(|| val.manifest(&self.manifest_format()))
 	}
+	pub fn manifest_multi(&self, val: Val) -> Result<Vec<(Rc<str>, Rc<str>)>> {
+		self.run_in_state(|| val.manifest_multi(&self.manifest_format()))
+	}
+	pub fn manifest_stream(&self, val: Val) -> Result<Vec<Rc<str>>> {
+		self.run_in_state(|| val.manifest_stream(&self.manifest_format()))
+	}
 
 	/// If passed value is function - call with set TLA
 	pub fn with_tla(&self, val: Val) -> Result<Val> {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/val.rs
1use crate::{2	builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3	error::Error::*,4	evaluate,5	function::{parse_function_call, parse_function_call_map, place_args},6	native::NativeCallback,7	throw, with_state, Context, ObjValue, Result,8};9use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};10use std::{11	cell::RefCell,12	collections::HashMap,13	fmt::{Debug, Display},14	rc::Rc,15};1617enum LazyValInternals {18	Computed(Val),19	Waiting(Box<dyn Fn() -> Result<Val>>),20}21#[derive(Clone)]22pub struct LazyVal(Rc<RefCell<LazyValInternals>>);23impl LazyVal {24	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {25		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))26	}27	pub fn new_resolved(val: Val) -> Self {28		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))29	}30	pub fn evaluate(&self) -> Result<Val> {31		let new_value = match &*self.0.borrow() {32			LazyValInternals::Computed(v) => return Ok(v.clone()),33			LazyValInternals::Waiting(f) => f()?,34		};35		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());36		Ok(new_value)37	}38}3940#[macro_export]41macro_rules! lazy_val {42	($f: expr) => {43		$crate::LazyVal::new(Box::new($f))44	};45}46#[macro_export]47macro_rules! resolved_lazy_val {48	($f: expr) => {49		$crate::LazyVal::new_resolved($f)50	};51}52impl Debug for LazyVal {53	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {54		write!(f, "Lazy")55	}56}57impl PartialEq for LazyVal {58	fn eq(&self, other: &Self) -> bool {59		Rc::ptr_eq(&self.0, &other.0)60	}61}6263#[derive(Debug, PartialEq)]64pub struct FuncDesc {65	pub name: Rc<str>,66	pub ctx: Context,67	pub params: ParamsDesc,68	pub body: LocExpr,69}70impl FuncDesc {71	/// This function is always inlined to make tailstrict work72	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {73		let ctx = parse_function_call(74			call_ctx,75			Some(self.ctx.clone()),76			&self.params,77			args,78			tailstrict,79		)?;80		evaluate(ctx, &self.body)81	}8283	pub fn evaluate_map(84		&self,85		call_ctx: Context,86		args: &HashMap<Rc<str>, Val>,87		tailstrict: bool,88	) -> Result<Val> {89		let ctx = parse_function_call_map(90			call_ctx,91			Some(self.ctx.clone()),92			&self.params,93			args,94			tailstrict,95		)?;96		evaluate(ctx, &self.body)97	}9899	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {100		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;101		evaluate(ctx, &self.body)102	}103}104105#[derive(Debug, Clone, Copy, PartialEq)]106pub enum ValType {107	Bool,108	Null,109	Str,110	Num,111	Arr,112	Obj,113	Func,114}115impl ValType {116	pub fn name(&self) -> &'static str {117		use ValType::*;118		match self {119			Bool => "boolean",120			Null => "null",121			Str => "string",122			Num => "number",123			Arr => "array",124			Obj => "object",125			Func => "function",126		}127	}128}129impl Display for ValType {130	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {131		write!(f, "{}", self.name())132	}133}134135#[derive(Clone)]136pub enum ManifestFormat {137	YamlStream(Box<ManifestFormat>),138	Yaml(usize),139	Json(usize),140	String,141}142143#[derive(Debug, Clone)]144pub enum Val {145	Bool(bool),146	Null,147	Str(Rc<str>),148	Num(f64),149	Lazy(LazyVal),150	Arr(Rc<Vec<Val>>),151	Obj(ObjValue),152	Func(Rc<FuncDesc>),153154	// Library functions implemented in native155	Intristic(Rc<str>, Rc<str>),156	NativeExt(Rc<str>, Rc<NativeCallback>),157}158macro_rules! matches_unwrap {159	($e: expr, $p: pat, $r: expr) => {160		match $e {161			$p => $r,162			_ => panic!("no match"),163			}164	};165}166impl Val {167	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity168	pub fn new_checked_num(num: f64) -> Result<Val> {169		if num.is_finite() {170			Ok(Val::Num(num))171		} else {172			throw!(RuntimeError("overflow".into()))173		}174	}175176	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {177		let this_type = self.value_type()?;178		if this_type != val_type {179			throw!(TypeMismatch(context, vec![val_type], this_type))180		} else {181			Ok(())182		}183	}184	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {185		self.assert_type(context, ValType::Bool)?;186		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))187	}188	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {189		self.assert_type(context, ValType::Str)?;190		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))191	}192	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {193		self.assert_type(context, ValType::Num)?;194		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))195	}196	pub fn unwrap_if_lazy(&self) -> Result<Self> {197		Ok(if let Val::Lazy(v) = self {198			v.evaluate()?.unwrap_if_lazy()?199		} else {200			self.clone()201		})202	}203	pub fn value_type(&self) -> Result<ValType> {204		Ok(match self {205			Val::Str(..) => ValType::Str,206			Val::Num(..) => ValType::Num,207			Val::Arr(..) => ValType::Arr,208			Val::Obj(..) => ValType::Obj,209			Val::Bool(_) => ValType::Bool,210			Val::Null => ValType::Null,211			Val::Func(..) | Val::Intristic(_, _) | Val::NativeExt(_, _) => ValType::Func,212			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,213		})214	}215216	pub fn into_string(self) -> Result<Rc<str>> {217		Ok(match self.unwrap_if_lazy()? {218			Val::Bool(true) => "true".into(),219			Val::Bool(false) => "false".into(),220			Val::Null => "null".into(),221			Val::Str(s) => s,222			v => manifest_json_ex(223				&v,224				&ManifestJsonOptions {225					padding: &"",226					mtype: ManifestType::ToString,227				},228			)?229			.into(),230		})231	}232233234	pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {235		Ok(match ty {236			ManifestFormat::YamlStream(format) => {237				let arr = match self {238					Val::Arr(a) => a,239					_ => throw!(StreamManifestOutputIsNotAArray),240				};241				let mut out = String::new();242243				match format as &ManifestFormat {244					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),245					ManifestFormat::String => throw!(StreamManifestCannotNestString),246					_ => {}247				};248249				if !arr.is_empty() {250					for v in arr.iter() {251						out.push_str("---\n");252						out.push_str(&v.manifest(format)?);253						out.push_str("\n");254					}255					out.push_str("...");256				}257258				out.into()259			}260			ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,261			ManifestFormat::Json(padding) => self.to_json(*padding)?,262			ManifestFormat::String => match self {263				Val::Str(s) => s.clone(),264				_ => throw!(StringManifestOutputIsNotAString),265			},266		})267	}268269	/// For manifestification270	pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {271		manifest_json_ex(272			self,273			&ManifestJsonOptions {274				padding: &" ".repeat(padding),275				mtype: if padding == 0 {276					ManifestType::Minify277				} else {278					ManifestType::Manifest279				},280			},281		)282		.map(|s| s.into())283	}284285	/// Calls std.manifestJson286	#[cfg(feature = "faster")]287	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {288		manifest_json_ex(289			&self,290			&ManifestJsonOptions {291				padding: &" ".repeat(padding),292				mtype: ManifestType::Std,293			},294		)295		.map(|s| s.into())296	}297298	/// Calls std.manifestJson299	#[cfg(not(feature = "faster"))]300	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {301		with_state(|s| {302			let ctx = s303				.create_default_context()?304				.with_var("__tmp__to_json__".into(), self.clone())?;305			Ok(evaluate(306				ctx,307				&el!(Expr::Apply(308					el!(Expr::Index(309						el!(Expr::Var("std".into())),310						el!(Expr::Str("manifestJsonEx".into()))311					)),312					ArgsDesc(vec![313						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),314						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))315					]),316					false317				)),318			)?319			.try_cast_str("to json")?)320		})321	}322	pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {323		with_state(|s| {324			let ctx = s325				.create_default_context()?326				.with_var("__tmp__to_json__".into(), self.clone());327			Ok(evaluate(328				ctx,329				&el!(Expr::Apply(330					el!(Expr::Index(331						el!(Expr::Var("std".into())),332						el!(Expr::Str("manifestYamlDoc".into()))333					)),334					ArgsDesc(vec![335						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),336						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))337					]),338					false339				)),340			)?341			.try_cast_str("to json")?)342		})343	}344}345346fn is_function_like(val: &Val) -> bool {347	matches!(val, Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _))348}349350/// Implements std.primitiveEquals builtin351pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {352	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {353		(Val::Bool(a), Val::Bool(b)) => a == b,354		(Val::Null, Val::Null) => true,355		(Val::Str(a), Val::Str(b)) => a == b,356		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,357		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(358			"primitiveEquals operates on primitive types, got array".into(),359		)),360		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(361			"primitiveEquals operates on primitive types, got object".into(),362		)),363		(a, b) if is_function_like(&a) && is_function_like(&b) => {364			throw!(RuntimeError("cannot test equality of functions".into()))365		}366		(_, _) => false,367	})368}369370/// Native implementation of std.equals371pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {372	let val_a = val_a.unwrap_if_lazy()?;373	let val_b = val_b.unwrap_if_lazy()?;374375	if val_a.value_type()? != val_b.value_type()? {376		return Ok(false);377	}378	match (val_a, val_b) {379		// Cant test for ptr equality, because all fields needs to be evaluated380		(Val::Arr(a), Val::Arr(b)) => {381			if a.len() != b.len() {382				return Ok(false);383			}384			for (a, b) in a.iter().zip(b.iter()) {385				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {386					return Ok(false);387				}388			}389			Ok(true)390		}391		(Val::Obj(a), Val::Obj(b)) => {392			let fields = a.visible_fields();393			if fields != b.visible_fields() {394				return Ok(false);395			}396			for field in fields {397				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {398					return Ok(false);399				}400			}401			Ok(true)402		}403		(a, b) => Ok(primitive_equals(&a, &b)?),404	}405}
after · crates/jrsonnet-evaluator/src/val.rs
1use crate::{2	builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3	error::Error::*,4	evaluate,5	function::{parse_function_call, parse_function_call_map, place_args},6	native::NativeCallback,7	throw, with_state, Context, ObjValue, Result,8};9use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};10use std::{11	cell::RefCell,12	collections::HashMap,13	fmt::{Debug, Display},14	rc::Rc,15};1617enum LazyValInternals {18	Computed(Val),19	Waiting(Box<dyn Fn() -> Result<Val>>),20}21#[derive(Clone)]22pub struct LazyVal(Rc<RefCell<LazyValInternals>>);23impl LazyVal {24	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {25		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))26	}27	pub fn new_resolved(val: Val) -> Self {28		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))29	}30	pub fn evaluate(&self) -> Result<Val> {31		let new_value = match &*self.0.borrow() {32			LazyValInternals::Computed(v) => return Ok(v.clone()),33			LazyValInternals::Waiting(f) => f()?,34		};35		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());36		Ok(new_value)37	}38}3940#[macro_export]41macro_rules! lazy_val {42	($f: expr) => {43		$crate::LazyVal::new(Box::new($f))44	};45}46#[macro_export]47macro_rules! resolved_lazy_val {48	($f: expr) => {49		$crate::LazyVal::new_resolved($f)50	};51}52impl Debug for LazyVal {53	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {54		write!(f, "Lazy")55	}56}57impl PartialEq for LazyVal {58	fn eq(&self, other: &Self) -> bool {59		Rc::ptr_eq(&self.0, &other.0)60	}61}6263#[derive(Debug, PartialEq)]64pub struct FuncDesc {65	pub name: Rc<str>,66	pub ctx: Context,67	pub params: ParamsDesc,68	pub body: LocExpr,69}70impl FuncDesc {71	/// This function is always inlined to make tailstrict work72	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {73		let ctx = parse_function_call(74			call_ctx,75			Some(self.ctx.clone()),76			&self.params,77			args,78			tailstrict,79		)?;80		evaluate(ctx, &self.body)81	}8283	pub fn evaluate_map(84		&self,85		call_ctx: Context,86		args: &HashMap<Rc<str>, Val>,87		tailstrict: bool,88	) -> Result<Val> {89		let ctx = parse_function_call_map(90			call_ctx,91			Some(self.ctx.clone()),92			&self.params,93			args,94			tailstrict,95		)?;96		evaluate(ctx, &self.body)97	}9899	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {100		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;101		evaluate(ctx, &self.body)102	}103}104105#[derive(Debug, Clone, Copy, PartialEq)]106pub enum ValType {107	Bool,108	Null,109	Str,110	Num,111	Arr,112	Obj,113	Func,114}115impl ValType {116	pub fn name(&self) -> &'static str {117		use ValType::*;118		match self {119			Bool => "boolean",120			Null => "null",121			Str => "string",122			Num => "number",123			Arr => "array",124			Obj => "object",125			Func => "function",126		}127	}128}129impl Display for ValType {130	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {131		write!(f, "{}", self.name())132	}133}134135#[derive(Clone)]136pub enum ManifestFormat {137	YamlStream(Box<ManifestFormat>),138	Yaml(usize),139	Json(usize),140	String,141}142143#[derive(Debug, Clone)]144pub enum Val {145	Bool(bool),146	Null,147	Str(Rc<str>),148	Num(f64),149	Lazy(LazyVal),150	Arr(Rc<Vec<Val>>),151	Obj(ObjValue),152	Func(Rc<FuncDesc>),153154	// Library functions implemented in native155	Intristic(Rc<str>, Rc<str>),156	NativeExt(Rc<str>, Rc<NativeCallback>),157}158macro_rules! matches_unwrap {159	($e: expr, $p: pat, $r: expr) => {160		match $e {161			$p => $r,162			_ => panic!("no match"),163			}164	};165}166impl Val {167	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity168	pub fn new_checked_num(num: f64) -> Result<Val> {169		if num.is_finite() {170			Ok(Val::Num(num))171		} else {172			throw!(RuntimeError("overflow".into()))173		}174	}175176	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {177		let this_type = self.value_type()?;178		if this_type != val_type {179			throw!(TypeMismatch(context, vec![val_type], this_type))180		} else {181			Ok(())182		}183	}184	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {185		self.assert_type(context, ValType::Bool)?;186		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))187	}188	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {189		self.assert_type(context, ValType::Str)?;190		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))191	}192	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {193		self.assert_type(context, ValType::Num)?;194		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))195	}196	pub fn unwrap_if_lazy(&self) -> Result<Self> {197		Ok(if let Val::Lazy(v) = self {198			v.evaluate()?.unwrap_if_lazy()?199		} else {200			self.clone()201		})202	}203	pub fn value_type(&self) -> Result<ValType> {204		Ok(match self {205			Val::Str(..) => ValType::Str,206			Val::Num(..) => ValType::Num,207			Val::Arr(..) => ValType::Arr,208			Val::Obj(..) => ValType::Obj,209			Val::Bool(_) => ValType::Bool,210			Val::Null => ValType::Null,211			Val::Func(..) | Val::Intristic(_, _) | Val::NativeExt(_, _) => ValType::Func,212			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,213		})214	}215216	pub fn to_string(&self) -> Result<Rc<str>> {217		Ok(match self.unwrap_if_lazy()? {218			Val::Bool(true) => "true".into(),219			Val::Bool(false) => "false".into(),220			Val::Null => "null".into(),221			Val::Str(s) => s,222			v => manifest_json_ex(223				&v,224				&ManifestJsonOptions {225					padding: &"",226					mtype: ManifestType::ToString,227				},228			)?229			.into(),230		})231	}232233	/// Expects value to be object, outputs (key, manifested value) pairs234	pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(Rc<str>, Rc<str>)>> {235		let obj = match self {236			Val::Obj(obj) => obj,237			_ => throw!(MultiManifestOutputIsNotAObject),238		};239		let keys = obj.visible_fields();240		let mut out = Vec::with_capacity(keys.len());241		for key in keys {242			let value = obj243				.get(key.clone())?244				.expect("item in object")245				.manifest(ty)?;246			out.push((key, value));247		}248		Ok(out)249	}250251	/// Expects value to be array, outputs manifested values252	pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<Rc<str>>> {253		let arr = match self {254			Val::Arr(a) => a,255			_ => throw!(StreamManifestOutputIsNotAArray),256		};257		let mut out = Vec::with_capacity(arr.len());258		for i in arr.iter() {259			out.push(i.manifest(ty)?);260		}261		Ok(out)262	}263264	pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {265		Ok(match ty {266			ManifestFormat::YamlStream(format) => {267				let arr = match self {268					Val::Arr(a) => a,269					_ => throw!(StreamManifestOutputIsNotAArray),270				};271				let mut out = String::new();272273				match format as &ManifestFormat {274					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),275					ManifestFormat::String => throw!(StreamManifestCannotNestString),276					_ => {}277				};278279				if !arr.is_empty() {280					for v in arr.iter() {281						out.push_str("---\n");282						out.push_str(&v.manifest(format)?);283						out.push_str("\n");284					}285					out.push_str("...");286				}287288				out.into()289			}290			ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,291			ManifestFormat::Json(padding) => self.to_json(*padding)?,292			ManifestFormat::String => match self {293				Val::Str(s) => s.clone(),294				_ => throw!(StringManifestOutputIsNotAString),295			},296		})297	}298299	/// For manifestification300	pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {301		manifest_json_ex(302			self,303			&ManifestJsonOptions {304				padding: &" ".repeat(padding),305				mtype: if padding == 0 {306					ManifestType::Minify307				} else {308					ManifestType::Manifest309				},310			},311		)312		.map(|s| s.into())313	}314315	/// Calls std.manifestJson316	#[cfg(feature = "faster")]317	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {318		manifest_json_ex(319			&self,320			&ManifestJsonOptions {321				padding: &" ".repeat(padding),322				mtype: ManifestType::Std,323			},324		)325		.map(|s| s.into())326	}327328	/// Calls std.manifestJson329	#[cfg(not(feature = "faster"))]330	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {331		with_state(|s| {332			let ctx = s333				.create_default_context()?334				.with_var("__tmp__to_json__".into(), self.clone())?;335			Ok(evaluate(336				ctx,337				&el!(Expr::Apply(338					el!(Expr::Index(339						el!(Expr::Var("std".into())),340						el!(Expr::Str("manifestJsonEx".into()))341					)),342					ArgsDesc(vec![343						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),344						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))345					]),346					false347				)),348			)?349			.try_cast_str("to json")?)350		})351	}352	pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {353		with_state(|s| {354			let ctx = s355				.create_default_context()?356				.with_var("__tmp__to_json__".into(), self.clone());357			Ok(evaluate(358				ctx,359				&el!(Expr::Apply(360					el!(Expr::Index(361						el!(Expr::Var("std".into())),362						el!(Expr::Str("manifestYamlDoc".into()))363					)),364					ArgsDesc(vec![365						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),366						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))367					]),368					false369				)),370			)?371			.try_cast_str("to json")?)372		})373	}374}375376fn is_function_like(val: &Val) -> bool {377	matches!(val, Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _))378}379380/// Implements std.primitiveEquals builtin381pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {382	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {383		(Val::Bool(a), Val::Bool(b)) => a == b,384		(Val::Null, Val::Null) => true,385		(Val::Str(a), Val::Str(b)) => a == b,386		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,387		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(388			"primitiveEquals operates on primitive types, got array".into(),389		)),390		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(391			"primitiveEquals operates on primitive types, got object".into(),392		)),393		(a, b) if is_function_like(&a) && is_function_like(&b) => {394			throw!(RuntimeError("cannot test equality of functions".into()))395		}396		(_, _) => false,397	})398}399400/// Native implementation of std.equals401pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {402	let val_a = val_a.unwrap_if_lazy()?;403	let val_b = val_b.unwrap_if_lazy()?;404405	if val_a.value_type()? != val_b.value_type()? {406		return Ok(false);407	}408	match (val_a, val_b) {409		// Cant test for ptr equality, because all fields needs to be evaluated410		(Val::Arr(a), Val::Arr(b)) => {411			if a.len() != b.len() {412				return Ok(false);413			}414			for (a, b) in a.iter().zip(b.iter()) {415				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {416					return Ok(false);417				}418			}419			Ok(true)420		}421		(Val::Obj(a), Val::Obj(b)) => {422			let fields = a.visible_fields();423			if fields != b.visible_fields() {424				return Ok(false);425			}426			for field in fields {427				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {428					return Ok(false);429				}430			}431			Ok(true)432		}433		(a, b) => Ok(primitive_equals(&a, &b)?),434	}435}