git.delta.rocks / jrsonnet / refs/commits / d44593872785

difftreelog

refactor(evaluator) extract json to module

Lach2020-08-15parent: #219ced5.patch.diff
in: master

4 files changed

addedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -0,0 +1,150 @@
+use crate::error::Error::*;
+use crate::error::Result;
+use crate::{throw, Val};
+
+#[derive(PartialEq)]
+pub enum ManifestType {
+	// Applied in manifestification
+	Manifest,
+	/// Used for std.manifestJson
+	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest
+	Std,
+	// No line breaks, used in `obj+''`
+	ToString,
+}
+
+pub struct ManifestJsonOptions<'s> {
+	pub padding: &'s str,
+	pub mtype: ManifestType,
+}
+
+pub(crate) fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {
+	let mut out = String::new();
+	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;
+	Ok(out)
+}
+fn manifest_json_ex_buf(
+	val: &Val,
+	buf: &mut String,
+	cur_padding: &mut String,
+	options: &ManifestJsonOptions<'_>,
+) -> Result<()> {
+	use std::fmt::Write;
+	match val.unwrap_if_lazy()? {
+		Val::Bool(v) => {
+			if v {
+				buf.push_str("true");
+			} else {
+				buf.push_str("false");
+			}
+		}
+		Val::Null => buf.push_str("null"),
+		Val::Str(s) => buf.push_str(&escape_string_json(&s)),
+		Val::Num(n) => write!(buf, "{}", n).unwrap(),
+		Val::Arr(items) => {
+			buf.push('[');
+			if !items.is_empty() {
+				if options.mtype != ManifestType::ToString {
+					buf.push('\n');
+				}
+
+				let old_len = cur_padding.len();
+				cur_padding.push_str(options.padding);
+				for (i, item) in items.iter().enumerate() {
+					if i != 0 {
+						buf.push(',');
+						if options.mtype == ManifestType::ToString {
+							buf.push(' ');
+						} else {
+							buf.push('\n');
+						}
+					}
+					buf.push_str(cur_padding);
+					manifest_json_ex_buf(item, buf, cur_padding, options)?;
+				}
+				cur_padding.truncate(old_len);
+
+				if options.mtype != ManifestType::ToString {
+					buf.push('\n');
+					buf.push_str(cur_padding);
+				}
+			} else if options.mtype == ManifestType::Std {
+				buf.push_str("\n\n");
+				buf.push_str(cur_padding);
+			} else if options.mtype == ManifestType::ToString {
+				buf.push(' ');
+			}
+			buf.push(']');
+		}
+		Val::Obj(obj) => {
+			buf.push('{');
+			let fields = obj.visible_fields();
+			if !fields.is_empty() {
+				if options.mtype != ManifestType::ToString {
+					buf.push('\n');
+				}
+
+				let old_len = cur_padding.len();
+				cur_padding.push_str(options.padding);
+				for (i, field) in fields.into_iter().enumerate() {
+					if i != 0 {
+						buf.push(',');
+						if options.mtype == ManifestType::ToString {
+							buf.push(' ');
+						} else {
+							buf.push('\n');
+						}
+					}
+					buf.push_str(cur_padding);
+					buf.push_str(&escape_string_json(&field));
+					buf.push_str(": ");
+					manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, cur_padding, options)?;
+				}
+				cur_padding.truncate(old_len);
+
+				if options.mtype != ManifestType::ToString {
+					buf.push('\n');
+					buf.push_str(cur_padding);
+				}
+			} else if options.mtype == ManifestType::Std {
+				buf.push_str("\n\n");
+				buf.push_str(cur_padding);
+			} else if options.mtype == ManifestType::ToString {
+				buf.push(' ');
+			}
+			buf.push('}');
+		}
+		Val::Func(_) | Val::Intristic(_, _) => {
+			throw!(RuntimeError("tried to manifest function".into()))
+		}
+		Val::Lazy(_) => unreachable!(),
+	};
+	Ok(())
+}
+pub fn escape_string_json(s: &str) -> String {
+	use std::fmt::Write;
+	let mut out = String::new();
+	out.push('"');
+	for c in s.chars() {
+		match c {
+			'"' => out.push_str("\\\""),
+			'\\' => out.push_str("\\\\"),
+			'\u{0008}' => out.push_str("\\b"),
+			'\u{000c}' => out.push_str("\\f"),
+			'\n' => out.push_str("\\n"),
+			'\r' => out.push_str("\\r"),
+			'\t' => out.push_str("\\t"),
+			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {
+				write!(out, "\\u{:04x}", c as u32).unwrap()
+			}
+			c => out.push(c),
+		}
+	}
+	out.push('"');
+	out
+}
+
+#[test]
+fn json_test() {
+	assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"")
+}
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -2,3 +2,4 @@
 pub use stdlib::*;
 
 pub mod format;
+pub mod manifest;
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -1,12 +1,13 @@
 use crate::{
-	builtin::format::{format_arr, format_obj},
+	builtin::{
+		format::{format_arr, format_obj},
+		manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType},
+	},
 	context_creator, equals,
 	error::Error::*,
-	escape_string_json, future_wrapper, lazy_val, manifest_json_ex, parse_args, primitive_equals,
-	push, throw,
-	val::ManifestJsonOptions,
-	with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ManifestType,
-	ObjMember, ObjValue, Result, Val, ValType,
+	future_wrapper, lazy_val, parse_args, primitive_equals, push, throw, with_state, Context,
+	ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ObjMember, ObjValue, Result, Val,
+	ValType,
 };
 use closure::closure;
 use jrsonnet_parser::{
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/val.rs
1use crate::{2	error::Error::*,3	evaluate,4	function::{parse_function_call, parse_function_call_map, place_args},5	throw, with_state, Context, ObjValue, Result,6};7use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};8use std::{9	cell::RefCell,10	collections::HashMap,11	fmt::{Debug, Display},12	rc::Rc,13};1415enum LazyValInternals {16	Computed(Val),17	Waiting(Box<dyn Fn() -> Result<Val>>),18}19#[derive(Clone)]20pub struct LazyVal(Rc<RefCell<LazyValInternals>>);21impl LazyVal {22	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {23		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))24	}25	pub fn new_resolved(val: Val) -> Self {26		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))27	}28	pub fn evaluate(&self) -> Result<Val> {29		let new_value = match &*self.0.borrow() {30			LazyValInternals::Computed(v) => return Ok(v.clone()),31			LazyValInternals::Waiting(f) => f()?,32		};33		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());34		Ok(new_value)35	}36}3738#[macro_export]39macro_rules! lazy_val {40	($f: expr) => {41		$crate::LazyVal::new(Box::new($f))42	};43}44#[macro_export]45macro_rules! resolved_lazy_val {46	($f: expr) => {47		$crate::LazyVal::new_resolved($f)48	};49}50impl Debug for LazyVal {51	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {52		write!(f, "Lazy")53	}54}55impl PartialEq for LazyVal {56	fn eq(&self, other: &Self) -> bool {57		Rc::ptr_eq(&self.0, &other.0)58	}59}6061#[derive(Debug, PartialEq)]62pub struct FuncDesc {63	pub name: Rc<str>,64	pub ctx: Context,65	pub params: ParamsDesc,66	pub body: LocExpr,67}68impl FuncDesc {69	/// This function is always inlined to make tailstrict work70	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {71		let ctx = parse_function_call(72			call_ctx,73			Some(self.ctx.clone()),74			&self.params,75			args,76			tailstrict,77		)?;78		evaluate(ctx, &self.body)79	}8081	pub fn evaluate_map(82		&self,83		call_ctx: Context,84		args: &HashMap<Rc<str>, Val>,85		tailstrict: bool,86	) -> Result<Val> {87		let ctx = parse_function_call_map(88			call_ctx,89			Some(self.ctx.clone()),90			&self.params,91			args,92			tailstrict,93		)?;94		evaluate(ctx, &self.body)95	}9697	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {98		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;99		evaluate(ctx, &self.body)100	}101}102103#[derive(Debug, Clone, Copy, PartialEq)]104pub enum ValType {105	Bool,106	Null,107	Str,108	Num,109	Arr,110	Obj,111	Func,112}113impl ValType {114	pub fn name(&self) -> &'static str {115		use ValType::*;116		match self {117			Bool => "boolean",118			Null => "null",119			Str => "string",120			Num => "number",121			Arr => "array",122			Obj => "object",123			Func => "function",124		}125	}126}127impl Display for ValType {128	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {129		write!(f, "{}", self.name())130	}131}132133#[derive(Debug, Clone)]134pub enum Val {135	Bool(bool),136	Null,137	Str(Rc<str>),138	Num(f64),139	Lazy(LazyVal),140	Arr(Rc<Vec<Val>>),141	Obj(ObjValue),142	Func(Rc<FuncDesc>),143144	// Library functions implemented in native145	Intristic(Rc<str>, Rc<str>),146}147macro_rules! matches_unwrap {148	($e: expr, $p: pat, $r: expr) => {149		match $e {150			$p => $r,151			_ => panic!("no match"),152			}153	};154}155impl Val {156	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity157	pub fn new_checked_num(num: f64) -> Result<Val> {158		if num.is_finite() {159			Ok(Val::Num(num))160		} else {161			throw!(RuntimeError("overflow".into()))162		}163	}164165	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {166		let this_type = self.value_type()?;167		if this_type != val_type {168			throw!(TypeMismatch(context, vec![val_type], this_type))169		} else {170			Ok(())171		}172	}173	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {174		self.assert_type(context, ValType::Bool)?;175		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))176	}177	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {178		self.assert_type(context, ValType::Str)?;179		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))180	}181	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {182		self.assert_type(context, ValType::Num)?;183		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))184	}185	pub fn unwrap_if_lazy(&self) -> Result<Self> {186		Ok(if let Val::Lazy(v) = self {187			v.evaluate()?.unwrap_if_lazy()?188		} else {189			self.clone()190		})191	}192	pub fn value_type(&self) -> Result<ValType> {193		Ok(match self {194			Val::Str(..) => ValType::Str,195			Val::Num(..) => ValType::Num,196			Val::Arr(..) => ValType::Arr,197			Val::Obj(..) => ValType::Obj,198			Val::Func(..) => ValType::Func,199			Val::Bool(_) => ValType::Bool,200			Val::Null => ValType::Null,201			Val::Intristic(_, _) => ValType::Func,202			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,203		})204	}205206	pub fn into_string(self) -> Result<Rc<str>> {207		Ok(match self.unwrap_if_lazy()? {208			Val::Bool(true) => "true".into(),209			Val::Bool(false) => "false".into(),210			Val::Null => "null".into(),211			Val::Str(s) => s,212			v => manifest_json_ex(213				&v,214				&ManifestJsonOptions {215					padding: &"",216					mtype: ManifestType::ToString,217				},218			)?219			.into(),220		})221	}222223	/// For manifestification224	pub fn into_json(self, padding: usize) -> Result<Rc<str>> {225		manifest_json_ex(226			&self,227			&ManifestJsonOptions {228				padding: &" ".repeat(padding),229				mtype: ManifestType::Manifest,230			},231		)232		.map(|s| s.into())233	}234235	/// Calls std.manifestJson236	#[cfg(feature = "faster")]237	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {238		manifest_json_ex(239			&self,240			&ManifestJsonOptions {241				padding: &" ".repeat(padding),242				mtype: ManifestType::Std,243			},244		)245		.map(|s| s.into())246	}247248	/// Calls std.manifestJson249	#[cfg(not(feature = "faster"))]250	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {251		with_state(|s| {252			let ctx = s253				.create_default_context()?254				.with_var("__tmp__to_json__".into(), self)?;255			Ok(evaluate(256				ctx,257				&el!(Expr::Apply(258					el!(Expr::Index(259						el!(Expr::Var("std".into())),260						el!(Expr::Str("manifestJsonEx".into()))261					)),262					ArgsDesc(vec![263						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),264						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))265					]),266					false267				)),268			)?269			.try_cast_str("to json")?)270		})271	}272	pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {273		with_state(|s| {274			let ctx = s275				.create_default_context()?276				.with_var("__tmp__to_json__".into(), self);277			Ok(evaluate(278				ctx,279				&el!(Expr::Apply(280					el!(Expr::Index(281						el!(Expr::Var("std".into())),282						el!(Expr::Str("manifestYamlDoc".into()))283					)),284					ArgsDesc(vec![285						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),286						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))287					]),288					false289				)),290			)?291			.try_cast_str("to json")?)292		})293	}294}295296fn is_function_like(val: &Val) -> bool {297	matches!(val, Val::Func(_) | Val::Intristic(_, _))298}299300/// Implements std.primitiveEquals builtin301pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {302	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {303		(Val::Bool(a), Val::Bool(b)) => a == b,304		(Val::Null, Val::Null) => true,305		(Val::Str(a), Val::Str(b)) => a == b,306		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,307		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(308			"primitiveEquals operates on primitive types, got array".into(),309		)),310		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(311			"primitiveEquals operates on primitive types, got object".into(),312		)),313		(a, b) if is_function_like(&a) && is_function_like(&b) => {314			throw!(RuntimeError("cannot test equality of functions".into()))315		}316		(_, _) => false,317	})318}319320/// Native implementation of std.equals321pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {322	let val_a = val_a.unwrap_if_lazy()?;323	let val_b = val_b.unwrap_if_lazy()?;324325	if val_a.value_type()? != val_b.value_type()? {326		return Ok(false);327	}328	match (val_a, val_b) {329		// Cant test for ptr equality, because all fields needs to be evaluated330		(Val::Arr(a), Val::Arr(b)) => {331			if a.len() != b.len() {332				return Ok(false);333			}334			for (a, b) in a.iter().zip(b.iter()) {335				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {336					return Ok(false);337				}338			}339			Ok(true)340		}341		(Val::Obj(a), Val::Obj(b)) => {342			let fields = a.visible_fields();343			if fields != b.visible_fields() {344				return Ok(false);345			}346			for field in fields {347				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {348					return Ok(false);349				}350			}351			Ok(true)352		}353		(a, b) => Ok(primitive_equals(&a, &b)?),354	}355}356357#[derive(PartialEq)]358pub enum ManifestType {359	// Applied in manifestification360	Manifest,361	/// Used for std.manifestJson362	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest363	Std,364	// No line breaks, used in `obj+''`365	ToString,366}367368pub struct ManifestJsonOptions<'s> {369	pub padding: &'s str,370	pub mtype: ManifestType,371}372373pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {374	let mut out = String::new();375	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;376	Ok(out)377}378fn manifest_json_ex_buf(379	val: &Val,380	buf: &mut String,381	cur_padding: &mut String,382	options: &ManifestJsonOptions<'_>,383) -> Result<()> {384	use std::fmt::Write;385	match val.unwrap_if_lazy()? {386		Val::Bool(v) => {387			if v {388				buf.push_str("true");389			} else {390				buf.push_str("false");391			}392		}393		Val::Null => buf.push_str("null"),394		Val::Str(s) => buf.push_str(&escape_string_json(&s)),395		Val::Num(n) => write!(buf, "{}", n).unwrap(),396		Val::Arr(items) => {397			buf.push('[');398			if !items.is_empty() {399				if options.mtype != ManifestType::ToString {400					buf.push('\n');401				}402403				let old_len = cur_padding.len();404				cur_padding.push_str(options.padding);405				for (i, item) in items.iter().enumerate() {406					if i != 0 {407						buf.push(',');408						if options.mtype == ManifestType::ToString {409							buf.push(' ');410						} else {411							buf.push('\n');412						}413					}414					buf.push_str(cur_padding);415					manifest_json_ex_buf(item, buf, cur_padding, options)?;416				}417				cur_padding.truncate(old_len);418419				if options.mtype != ManifestType::ToString {420					buf.push('\n');421					buf.push_str(cur_padding);422				}423			} else if options.mtype == ManifestType::Std {424				buf.push_str("\n\n");425				buf.push_str(cur_padding);426			} else if options.mtype == ManifestType::ToString {427				buf.push(' ');428			}429			buf.push(']');430		}431		Val::Obj(obj) => {432			buf.push('{');433			let fields = obj.visible_fields();434			if !fields.is_empty() {435				if options.mtype != ManifestType::ToString {436					buf.push('\n');437				}438439				let old_len = cur_padding.len();440				cur_padding.push_str(options.padding);441				for (i, field) in fields.into_iter().enumerate() {442					if i != 0 {443						buf.push(',');444						if options.mtype == ManifestType::ToString {445							buf.push(' ');446						} else {447							buf.push('\n');448						}449					}450					buf.push_str(cur_padding);451					buf.push_str(&escape_string_json(&field));452					buf.push_str(": ");453					manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, cur_padding, options)?;454				}455				cur_padding.truncate(old_len);456457				if options.mtype != ManifestType::ToString {458					buf.push('\n');459					buf.push_str(cur_padding);460				}461			} else if options.mtype == ManifestType::Std {462				buf.push_str("\n\n");463				buf.push_str(cur_padding);464			} else if options.mtype == ManifestType::ToString {465				buf.push(' ');466			}467			buf.push('}');468		}469		Val::Func(_) | Val::Intristic(_, _) => {470			throw!(RuntimeError("tried to manifest function".into()))471		}472		Val::Lazy(_) => unreachable!(),473	};474	Ok(())475}476pub fn escape_string_json(s: &str) -> String {477	use std::fmt::Write;478	let mut out = String::new();479	out.push('"');480	for c in s.chars() {481		match c {482			'"' => out.push_str("\\\""),483			'\\' => out.push_str("\\\\"),484			'\u{0008}' => out.push_str("\\b"),485			'\u{000c}' => out.push_str("\\f"),486			'\n' => out.push_str("\\n"),487			'\r' => out.push_str("\\r"),488			'\t' => out.push_str("\\t"),489			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {490				write!(out, "\\u{:04x}", c as u32).unwrap()491			}492			c => out.push(c),493		}494	}495	out.push('"');496	out497}498499#[test]500fn json_test() {501	assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"")502}
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	throw, with_state, Context, ObjValue, Result,7};8use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};9use std::{10	cell::RefCell,11	collections::HashMap,12	fmt::{Debug, Display},13	rc::Rc,14};1516enum LazyValInternals {17	Computed(Val),18	Waiting(Box<dyn Fn() -> Result<Val>>),19}20#[derive(Clone)]21pub struct LazyVal(Rc<RefCell<LazyValInternals>>);22impl LazyVal {23	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {24		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))25	}26	pub fn new_resolved(val: Val) -> Self {27		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))28	}29	pub fn evaluate(&self) -> Result<Val> {30		let new_value = match &*self.0.borrow() {31			LazyValInternals::Computed(v) => return Ok(v.clone()),32			LazyValInternals::Waiting(f) => f()?,33		};34		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());35		Ok(new_value)36	}37}3839#[macro_export]40macro_rules! lazy_val {41	($f: expr) => {42		$crate::LazyVal::new(Box::new($f))43	};44}45#[macro_export]46macro_rules! resolved_lazy_val {47	($f: expr) => {48		$crate::LazyVal::new_resolved($f)49	};50}51impl Debug for LazyVal {52	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {53		write!(f, "Lazy")54	}55}56impl PartialEq for LazyVal {57	fn eq(&self, other: &Self) -> bool {58		Rc::ptr_eq(&self.0, &other.0)59	}60}6162#[derive(Debug, PartialEq)]63pub struct FuncDesc {64	pub name: Rc<str>,65	pub ctx: Context,66	pub params: ParamsDesc,67	pub body: LocExpr,68}69impl FuncDesc {70	/// This function is always inlined to make tailstrict work71	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {72		let ctx = parse_function_call(73			call_ctx,74			Some(self.ctx.clone()),75			&self.params,76			args,77			tailstrict,78		)?;79		evaluate(ctx, &self.body)80	}8182	pub fn evaluate_map(83		&self,84		call_ctx: Context,85		args: &HashMap<Rc<str>, Val>,86		tailstrict: bool,87	) -> Result<Val> {88		let ctx = parse_function_call_map(89			call_ctx,90			Some(self.ctx.clone()),91			&self.params,92			args,93			tailstrict,94		)?;95		evaluate(ctx, &self.body)96	}9798	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {99		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;100		evaluate(ctx, &self.body)101	}102}103104#[derive(Debug, Clone, Copy, PartialEq)]105pub enum ValType {106	Bool,107	Null,108	Str,109	Num,110	Arr,111	Obj,112	Func,113}114impl ValType {115	pub fn name(&self) -> &'static str {116		use ValType::*;117		match self {118			Bool => "boolean",119			Null => "null",120			Str => "string",121			Num => "number",122			Arr => "array",123			Obj => "object",124			Func => "function",125		}126	}127}128impl Display for ValType {129	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {130		write!(f, "{}", self.name())131	}132}133134#[derive(Debug, Clone)]135pub enum Val {136	Bool(bool),137	Null,138	Str(Rc<str>),139	Num(f64),140	Lazy(LazyVal),141	Arr(Rc<Vec<Val>>),142	Obj(ObjValue),143	Func(Rc<FuncDesc>),144145	// Library functions implemented in native146	Intristic(Rc<str>, Rc<str>),147}148macro_rules! matches_unwrap {149	($e: expr, $p: pat, $r: expr) => {150		match $e {151			$p => $r,152			_ => panic!("no match"),153			}154	};155}156impl Val {157	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity158	pub fn new_checked_num(num: f64) -> Result<Val> {159		if num.is_finite() {160			Ok(Val::Num(num))161		} else {162			throw!(RuntimeError("overflow".into()))163		}164	}165166	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {167		let this_type = self.value_type()?;168		if this_type != val_type {169			throw!(TypeMismatch(context, vec![val_type], this_type))170		} else {171			Ok(())172		}173	}174	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {175		self.assert_type(context, ValType::Bool)?;176		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))177	}178	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {179		self.assert_type(context, ValType::Str)?;180		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))181	}182	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {183		self.assert_type(context, ValType::Num)?;184		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))185	}186	pub fn unwrap_if_lazy(&self) -> Result<Self> {187		Ok(if let Val::Lazy(v) = self {188			v.evaluate()?.unwrap_if_lazy()?189		} else {190			self.clone()191		})192	}193	pub fn value_type(&self) -> Result<ValType> {194		Ok(match self {195			Val::Str(..) => ValType::Str,196			Val::Num(..) => ValType::Num,197			Val::Arr(..) => ValType::Arr,198			Val::Obj(..) => ValType::Obj,199			Val::Func(..) => ValType::Func,200			Val::Bool(_) => ValType::Bool,201			Val::Null => ValType::Null,202			Val::Intristic(_, _) => ValType::Func,203			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,204		})205	}206207	pub fn into_string(self) -> Result<Rc<str>> {208		Ok(match self.unwrap_if_lazy()? {209			Val::Bool(true) => "true".into(),210			Val::Bool(false) => "false".into(),211			Val::Null => "null".into(),212			Val::Str(s) => s,213			v => manifest_json_ex(214				&v,215				&ManifestJsonOptions {216					padding: &"",217					mtype: ManifestType::ToString,218				},219			)?220			.into(),221		})222	}223224	/// For manifestification225	pub fn into_json(self, padding: usize) -> Result<Rc<str>> {226		manifest_json_ex(227			&self,228			&ManifestJsonOptions {229				padding: &" ".repeat(padding),230				mtype: ManifestType::Manifest,231			},232		)233		.map(|s| s.into())234	}235236	/// Calls std.manifestJson237	#[cfg(feature = "faster")]238	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {239		manifest_json_ex(240			&self,241			&ManifestJsonOptions {242				padding: &" ".repeat(padding),243				mtype: ManifestType::Std,244			},245		)246		.map(|s| s.into())247	}248249	/// Calls std.manifestJson250	#[cfg(not(feature = "faster"))]251	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {252		with_state(|s| {253			let ctx = s254				.create_default_context()?255				.with_var("__tmp__to_json__".into(), self)?;256			Ok(evaluate(257				ctx,258				&el!(Expr::Apply(259					el!(Expr::Index(260						el!(Expr::Var("std".into())),261						el!(Expr::Str("manifestJsonEx".into()))262					)),263					ArgsDesc(vec![264						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),265						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))266					]),267					false268				)),269			)?270			.try_cast_str("to json")?)271		})272	}273	pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {274		with_state(|s| {275			let ctx = s276				.create_default_context()?277				.with_var("__tmp__to_json__".into(), self);278			Ok(evaluate(279				ctx,280				&el!(Expr::Apply(281					el!(Expr::Index(282						el!(Expr::Var("std".into())),283						el!(Expr::Str("manifestYamlDoc".into()))284					)),285					ArgsDesc(vec![286						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),287						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))288					]),289					false290				)),291			)?292			.try_cast_str("to json")?)293		})294	}295}296297fn is_function_like(val: &Val) -> bool {298	matches!(val, Val::Func(_) | Val::Intristic(_, _))299}300301/// Implements std.primitiveEquals builtin302pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {303	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {304		(Val::Bool(a), Val::Bool(b)) => a == b,305		(Val::Null, Val::Null) => true,306		(Val::Str(a), Val::Str(b)) => a == b,307		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,308		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(309			"primitiveEquals operates on primitive types, got array".into(),310		)),311		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(312			"primitiveEquals operates on primitive types, got object".into(),313		)),314		(a, b) if is_function_like(&a) && is_function_like(&b) => {315			throw!(RuntimeError("cannot test equality of functions".into()))316		}317		(_, _) => false,318	})319}320321/// Native implementation of std.equals322pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {323	let val_a = val_a.unwrap_if_lazy()?;324	let val_b = val_b.unwrap_if_lazy()?;325326	if val_a.value_type()? != val_b.value_type()? {327		return Ok(false);328	}329	match (val_a, val_b) {330		// Cant test for ptr equality, because all fields needs to be evaluated331		(Val::Arr(a), Val::Arr(b)) => {332			if a.len() != b.len() {333				return Ok(false);334			}335			for (a, b) in a.iter().zip(b.iter()) {336				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {337					return Ok(false);338				}339			}340			Ok(true)341		}342		(Val::Obj(a), Val::Obj(b)) => {343			let fields = a.visible_fields();344			if fields != b.visible_fields() {345				return Ok(false);346			}347			for field in fields {348				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {349					return Ok(false);350				}351			}352			Ok(true)353		}354		(a, b) => Ok(primitive_equals(&a, &b)?),355	}356}