git.delta.rocks / jrsonnet / refs/commits / 3100b67da51b

difftreelog

feat add standard like api to evaluator

Лач2020-07-19parent: #b14023b.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![feature(test)]5#![feature(stmt_expr_attributes)]6#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]78extern crate test;910mod builtin;11mod ctx;12mod dynamic;13mod error;14mod evaluate;15mod function;16mod import;17mod map;18mod obj;19pub mod trace;20mod val;2122pub use ctx::*;23pub use dynamic::*;24pub use error::*;25pub use evaluate::*;26pub use function::parse_function_call;27pub use import::*;28use jrsonnet_parser::*;29pub use obj::*;30use std::{31	cell::{Ref, RefCell, RefMut},32	collections::HashMap,33	fmt::Debug,34	path::PathBuf,35	rc::Rc,36};37use trace::{offset_to_location, CodeLocation};38pub use val::*;3940type BindableFn = dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>;41#[derive(Clone)]42pub enum LazyBinding {43	Bindable(Rc<BindableFn>),44	Bound(LazyVal),45}4647impl Debug for LazyBinding {48	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {49		write!(f, "LazyBinding")50	}51}52impl LazyBinding {53	pub fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {54		match self {55			LazyBinding::Bindable(v) => v(this, super_obj),56			LazyBinding::Bound(v) => Ok(v.clone()),57		}58	}59}6061pub struct EvaluationSettings {62	/// Limits recursion by limiting stack frames63	pub max_stack: usize,64	/// Limit amount of stack trace items preserved65	pub max_trace: usize,66	/// Used for std.extVar67	pub ext_vars: HashMap<Rc<str>, Val>,68	/// Global variables are inserted in default context69	pub globals: HashMap<Rc<str>, Val>,70	/// Used to resolve file locations/contents71	pub import_resolver: Box<dyn ImportResolver>,72}73impl Default for EvaluationSettings {74	fn default() -> Self {75		EvaluationSettings {76			max_stack: 200,77			max_trace: 20,78			globals: Default::default(),79			ext_vars: Default::default(),80			import_resolver: Box::new(DummyImportResolver),81		}82	}83}8485#[derive(Default)]86struct EvaluationData {87	/// Used for stack overflow detection, stacktrace is now populated on unwind88	stack_depth: usize,89	/// Contains file source codes and evaluated results for imports and pretty90	/// printing stacktraces91	files: HashMap<Rc<PathBuf>, FileData>,92	str_files: HashMap<Rc<PathBuf>, Rc<str>>,93}9495pub struct FileData {96	source_code: Rc<str>,97	parsed: LocExpr,98	evaluated: Option<Val>,99}100#[derive(Default)]101pub struct EvaluationStateInternals {102	/// Internal state103	data: RefCell<EvaluationData>,104	/// Settings, safe to change at runtime105	settings: RefCell<EvaluationSettings>,106}107108thread_local! {109	/// Contains state for currently executing file110	/// Global state is fine there111	pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)112}113pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {114	EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))115}116pub fn create_error(err: Error) -> LocError {117	with_state(|s| s.error(err))118}119pub fn create_error_result<T>(err: Error) -> Result<T> {120	Err(with_state(|s| s.error(err)))121}122pub(crate) fn push<T>(123	e: &Option<ExprLocation>,124	frame_desc: impl FnOnce() -> String,125	f: impl FnOnce() -> Result<T>,126) -> Result<T> {127	if let Some(v) = e {128		with_state(|s| s.push(&v, frame_desc, f))129	} else {130		f()131	}132}133134/// Maintains stack trace and import resolution135#[derive(Default, Clone)]136pub struct EvaluationState(Rc<EvaluationStateInternals>);137impl EvaluationState {138	fn data(&self) -> Ref<EvaluationData> {139		self.0.data.borrow()140	}141	fn data_mut(&self) -> RefMut<EvaluationData> {142		self.0.data.borrow_mut()143	}144	pub fn settings(&self) -> Ref<EvaluationSettings> {145		self.0.settings.borrow()146	}147	pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {148		self.0.settings.borrow_mut()149	}150151	pub fn evaluate_file_to_json(&self, path: &PathBuf) -> std::result::Result<Rc<str>, LocError> {152		self.import_file(&PathBuf::new(), &path)153			.and_then(|v| v.into_json(4))154	}155	pub fn evaluate_snippet_to_json(156		&self,157		path: &PathBuf,158		snippet: &str,159	) -> std::result::Result<Rc<str>, LocError> {160		self.parse_evaluate_raw(Rc::new(path.clone()), snippet)161			.and_then(|v| v.into_json(4))162	}163164	/// Parses and adds file to loaded165	pub fn add_file(&self, path: Rc<PathBuf>, source_code: Rc<str>) -> Result<()> {166		self.add_parsed_file(167			path.clone(),168			source_code.clone(),169			parse(170				&source_code,171				&ParserSettings {172					file_name: path.clone(),173					loc_data: true,174				},175			)176			.map_err(|error| {177				create_error(Error::ImportSyntaxError {178					error,179					path,180					source_code,181				})182			})?,183		)?;184185		Ok(())186	}187188	/// Adds file by source code and parsed expr189	pub fn add_parsed_file(190		&self,191		name: Rc<PathBuf>,192		source_code: Rc<str>,193		parsed: LocExpr,194	) -> Result<()> {195		self.data_mut().files.insert(196			name,197			FileData {198				source_code,199				parsed,200				evaluated: None,201			},202		);203204		Ok(())205	}206	pub fn get_source(&self, name: &PathBuf) -> Option<Rc<str>> {207		let ro_map = &self.data().files;208		ro_map.get(name).map(|value| value.source_code.clone())209	}210	pub fn map_source_locations(&self, file: &PathBuf, locs: &[usize]) -> Vec<CodeLocation> {211		offset_to_location(&self.get_source(file).unwrap(), locs)212	}213214	pub fn evaluate_file(&self, name: &PathBuf) -> Result<Val> {215		self.run_in_state(|| {216			let expr: LocExpr = {217				let ro_map = &self.data().files;218				let value = ro_map219					.get(name)220					.unwrap_or_else(|| panic!("file not added: {:?}", name));221				if value.evaluated.is_some() {222					return Ok(value.evaluated.clone().unwrap());223				}224				value.parsed.clone()225			};226			let value = evaluate(self.create_default_context()?, &expr)?;227			{228				self.0229					.data230					.borrow_mut()231					.files232					.get_mut(name)233					.unwrap()234					.evaluated235					.replace(value.clone());236			}237			Ok(value)238		})239	}240	pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {241		Ok(self.settings().import_resolver.resolve_file(from, path)?)242	}243	pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {244		Ok(self.settings().import_resolver.load_file_contents(path)?)245	}246	pub(crate) fn import_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Val> {247		let file_path = self.resolve_file(from, path)?;248		{249			let files = &self.data().files;250			if files.contains_key(&file_path) {251				return self.evaluate_file(&file_path);252			}253		}254		let contents = self.load_file_contents(&file_path)?;255		self.add_file(file_path.clone(), contents)?;256		self.evaluate_file(&file_path)257	}258	pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {259		let path = self.resolve_file(from, path)?;260		if !self.data().str_files.contains_key(&path) {261			let file_str = self.load_file_contents(&path)?;262			self.data_mut().str_files.insert(path.clone(), file_str);263		}264		Ok(self.data().str_files.get(&path).cloned().unwrap())265	}266267	/// Parses and evaluates snippet268	pub fn parse_evaluate_raw(&self, source: Rc<PathBuf>, code: &str) -> Result<Val> {269		let parsed = parse(270			&code,271			&ParserSettings {272				file_name: source,273				loc_data: true,274			},275		)276		.unwrap();277		self.evaluate_raw(parsed)278	}279	/// Evaluates parsed expression280	pub fn evaluate_raw(&self, code: LocExpr) -> Result<Val> {281		self.run_in_state(|| evaluate(self.create_default_context()?, &code))282	}283284	/// Adds standard library global variable (std) to this evaluator285	pub fn with_stdlib(&self) -> &Self {286		use jrsonnet_stdlib::STDLIB_STR;287		let std_path = Rc::new(PathBuf::from("std.jsonnet"));288		self.run_in_state(|| {289			self.add_parsed_file(290				std_path.clone(),291				STDLIB_STR.to_owned().into(),292				builtin::get_parsed_stdlib(),293			)294			.unwrap();295			let val = self.evaluate_file(&std_path).unwrap();296			self.settings_mut().globals.insert("std".into(), val);297		});298		self299	}300301	/// Creates context with all passed global variables302	pub fn create_default_context(&self) -> Result<Context> {303		let globals = &self.settings().globals;304		let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();305		for (name, value) in globals.iter() {306			new_bindings.insert(307				name.clone(),308				LazyBinding::Bound(resolved_lazy_val!(value.clone())),309			);310		}311		Context::new().extend_unbound(new_bindings, None, None, None)312	}313314	/// Executes code, creating new stack frame315	pub fn push<T>(316		&self,317		e: &ExprLocation,318		frame_desc: impl FnOnce() -> String,319		f: impl FnOnce() -> Result<T>,320	) -> Result<T> {321		{322			let mut data = self.data_mut();323			let stack_depth = &mut data.stack_depth;324			if *stack_depth > self.settings().max_stack {325				// Error creation uses data, so i drop guard here326				drop(data);327				return Err(self.error(Error::StackOverflow));328			} else {329				*stack_depth += 1;330			}331		}332		let result = f();333		self.data_mut().stack_depth -= 1;334		if let Err(mut err) = result {335			(err.1).0.push(StackTraceElement(e.clone(), frame_desc()));336			return Err(err);337		}338		result339	}340341	/// Creates error with stack trace342	pub fn error(&self, err: Error) -> LocError {343		LocError(err, StackTrace(vec![]))344	}345346	/// Runs passed function in state (required, if function needs to modify stack trace)347	pub fn run_in_state<T>(&self, f: impl FnOnce() -> T) -> T {348		EVAL_STATE.with(|v| {349			let has_state = v.borrow().is_some();350			if !has_state {351				v.borrow_mut().replace(self.clone());352			}353			let result = f();354			if !has_state {355				v.borrow_mut().take();356			}357			result358		})359	}360}361362#[cfg(test)]363pub mod tests {364	use super::Val;365	use crate::{create_error, primitive_equals, EvaluationState};366	use jrsonnet_parser::*;367	use std::{path::PathBuf, rc::Rc};368369	#[test]370	fn eval_state_stacktrace() {371		let state = EvaluationState::default();372		state.run_in_state(|| {373			state374				.push(375					&ExprLocation(Rc::new(PathBuf::from("test1.jsonnet")), 10, 20),376					|| "outer".to_owned(),377					|| {378						state.push(379							&ExprLocation(Rc::new(PathBuf::from("test2.jsonnet")), 30, 40),380							|| "inner".to_owned(),381							|| Err(create_error(crate::error::Error::RuntimeError("".into()))),382						)?;383						Ok(())384					},385				)386				.unwrap();387		});388	}389390	#[test]391	fn eval_state_standard() {392		let state = EvaluationState::default();393		state.with_stdlib();394		assert!(primitive_equals(395			&state396				.parse_evaluate_raw(397					Rc::new(PathBuf::from("raw.jsonnet")),398					r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#399				)400				.unwrap(),401			&Val::Bool(true),402		)403		.unwrap());404	}405406	macro_rules! eval {407		($str: expr) => {408			EvaluationState::default()409				.with_stdlib()410				.parse_evaluate_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str)411				.unwrap()412		};413	}414	macro_rules! eval_json {415		($str: expr) => {{416			let evaluator = EvaluationState::default();417			evaluator.with_stdlib();418			evaluator.run_in_state(|| {419				evaluator420					.parse_evaluate_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str)421					.unwrap()422					.into_json(0)423					.unwrap()424					.replace("\n", "")425				})426			}};427	}428429	/// Asserts given code returns `true`430	macro_rules! assert_eval {431		($str: expr) => {432			assert!(primitive_equals(&eval!($str), &Val::Bool(true)).unwrap())433		};434	}435436	/// Asserts given code returns `false`437	macro_rules! assert_eval_neg {438		($str: expr) => {439			assert!(primitive_equals(&eval!($str), &Val::Bool(false)).unwrap())440		};441	}442	macro_rules! assert_json {443		($str: expr, $out: expr) => {444			assert_eq!(eval_json!($str), $out.replace("\t", ""))445		};446	}447448	/// Sanity checking, before trusting to another tests449	#[test]450	fn equality_operator() {451		assert_eval!("2 == 2");452		assert_eval_neg!("2 != 2");453		assert_eval!("2 != 3");454		assert_eval_neg!("2 == 3");455		assert_eval!("'Hello' == 'Hello'");456		assert_eval_neg!("'Hello' != 'Hello'");457		assert_eval!("'Hello' != 'World'");458		assert_eval_neg!("'Hello' == 'World'");459	}460461	#[test]462	fn math_evaluation() {463		assert_eval!("2 + 2 * 2 == 6");464		assert_eval!("3 + (2 + 2 * 2) == 9");465	}466467	#[test]468	fn string_concat() {469		assert_eval!("'Hello' + 'World' == 'HelloWorld'");470		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");471		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");472	}473474	#[test]475	fn faster_join() {476		assert_eval!("std.join([0,0], [[1,2],[3,4],[5,6]]) == [1,2,0,0,3,4,0,0,5,6]");477		assert_eval!("std.join(',', ['1','2','3','4']) == '1,2,3,4'");478	}479480	#[test]481	fn function_contexts() {482		assert_eval!(483			r#"484				local k = {485					t(name = self.h): [self.h, name],486					h: 3,487				};488				local f = {489					t: k.t(),490					h: 4,491				};492				f.t[0] == f.t[1]493			"#494		);495	}496497	#[test]498	fn local() {499		assert_eval!("local a = 2; local b = 3; a + b == 5");500		assert_eval!("local a = 1, b = a + 1; a + b == 3");501		assert_eval!("local a = 1; local a = 2; a == 2");502	}503504	#[test]505	fn object_lazyness() {506		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);507	}508509	#[test]510	fn object_inheritance() {511		assert_json!("{a: self.b} + {b:3}", r#"{"a": 3,"b": 3}"#);512	}513514	#[test]515	fn object_assertion_success() {516		eval!("{assert \"a\" in self} + {a:2}");517	}518519	#[test]520	fn object_assertion_error() {521		eval!("{assert \"a\" in self}");522	}523524	#[test]525	fn lazy_args() {526		eval!("local test(a) = 2; test(error '3')");527	}528529	#[test]530	#[should_panic]531	fn tailstrict_args() {532		eval!("local test(a) = 2; test(error '3') tailstrict");533	}534535	#[test]536	#[should_panic]537	fn no_binding_error() {538		eval!("a");539	}540541	#[test]542	fn test_object() {543		assert_json!("{a:2}", r#"{"a": 2}"#);544		assert_json!("{a:2+2}", r#"{"a": 4}"#);545		assert_json!("{a:2}+{b:2}", r#"{"a": 2,"b": 2}"#);546		assert_json!("{b:3}+{b:2}", r#"{"b": 2}"#);547		assert_json!("{b:3}+{b+:2}", r#"{"b": 5}"#);548		assert_json!("local test='a'; {[test]:2}", r#"{"a": 2}"#);549		assert_json!(550			r#"551				{552					name: "Alice",553					welcome: "Hello " + self.name + "!",554				}555			"#,556			r#"{"name": "Alice","welcome": "Hello Alice!"}"#557		);558		assert_json!(559			r#"560				{561					name: "Alice",562					welcome: "Hello " + self.name + "!",563				} + {564					name: "Bob"565				}566			"#,567			r#"{"name": "Bob","welcome": "Hello Bob!"}"#568		);569	}570571	#[test]572	fn functions() {573		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");574		assert_json!(575			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,576			r#""HelloDearWorld""#577		);578	}579580	#[test]581	fn local_methods() {582		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");583		assert_json!(584			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,585			r#""HelloDearWorld""#586		);587	}588589	#[test]590	fn object_locals() {591		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b": 3}"#);592		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b": 3}"#);593		assert_json!(594			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,595			r#"{"test": {"test": 4}}"#596		);597	}598599	#[test]600	fn object_comp() {601		assert_json!(602			r#"{local t = "a", ["h"+i+"_"+z]: if "h"+(i-1)+"_"+z in self then t+1 else 0+t for i in [1,2,3] for z in [2,3,4] if z != i}"#,603			"{\"h1_2\": \"0a\",\"h1_3\": \"0a\",\"h1_4\": \"0a\",\"h2_3\": \"a1\",\"h2_4\": \"a1\",\"h3_2\": \"0a\",\"h3_4\": \"a1\"}"604		)605	}606607	#[test]608	fn direct_self() {609		println!(610			"{:#?}",611			eval!(612				r#"613					{614						local me = self,615						a: 3,616						b(): me.a,617					}618				"#619			)620		);621	}622623	#[test]624	fn indirect_self() {625		// `self` assigned to `me` was lost when being626		// referenced from field627		eval!(628			r#"{629				local me = self,630				a: 3,631				b: me.a,632			}.b"#633		);634	}635636	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly637	#[test]638	fn std_assert_ok() {639		eval!("std.assertEqual(4.5 << 2, 16)");640	}641642	#[test]643	#[should_panic]644	fn std_assert_failure() {645		eval!("std.assertEqual(4.5 << 2, 15)");646	}647648	#[test]649	fn string_is_string() {650		assert!(primitive_equals(651			&eval!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),652			&Val::Bool(false),653		)654		.unwrap());655	}656657	#[test]658	fn base64_works() {659		assert_json!(r#"std.base64("test")"#, r#""dGVzdA==""#);660	}661662	#[test]663	fn utf8_chars() {664		assert_json!(665			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,666			r#"{"c": 128526,"l": 1}"#667		)668	}669670	#[test]671	fn json() {672		assert_json!(673			r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,674			r#""{\n\"a\": 3,\n\"b\": 4,\n\"c\": 6\n}""#675		);676	}677678	#[test]679	fn test() {680		assert_json!(681			r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,682			"[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"683		);684	}685686	#[test]687	fn sjsonnet() {688		eval!(689			r#"690			local x0 = {k: 1};691			local x1 = {k: x0.k + x0.k};692			local x2 = {k: x1.k + x1.k};693			local x3 = {k: x2.k + x2.k};694			local x4 = {k: x3.k + x3.k};695			local x5 = {k: x4.k + x4.k};696			local x6 = {k: x5.k + x5.k};697			local x7 = {k: x6.k + x6.k};698			local x8 = {k: x7.k + x7.k};699			local x9 = {k: x8.k + x8.k};700			local x10 = {k: x9.k + x9.k};701			local x11 = {k: x10.k + x10.k};702			local x12 = {k: x11.k + x11.k};703			local x13 = {k: x12.k + x12.k};704			local x14 = {k: x13.k + x13.k};705			local x15 = {k: x14.k + x14.k};706			local x16 = {k: x15.k + x15.k};707			local x17 = {k: x16.k + x16.k};708			local x18 = {k: x17.k + x17.k};709			local x19 = {k: x18.k + x18.k};710			local x20 = {k: x19.k + x19.k};711			local x21 = {k: x20.k + x20.k};712			x21.k713		"#714		);715	}716717	use test::Bencher;718719	// This test is commented out by default, because of huge compilation slowdown720	// #[bench]721	// fn bench_codegen(b: &mut Bencher) {722	// 	b.iter(|| {723	// 		#[allow(clippy::all)]724	// 		let stdlib = {725	// 			use jrsonnet_parser::*;726	// 			include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))727	// 		};728	// 		stdlib729	// 	})730	// }731732	#[bench]733	fn bench_serialize(b: &mut Bencher) {734		b.iter(|| {735			bincode::deserialize::<jrsonnet_parser::LocExpr>(include_bytes!(concat!(736				env!("OUT_DIR"),737				"/stdlib.bincode"738			)))739			.expect("deserialize stdlib")740		})741	}742743	#[bench]744	fn bench_parse(b: &mut Bencher) {745		b.iter(|| {746			jrsonnet_parser::parse(747				jrsonnet_stdlib::STDLIB_STR,748				&jrsonnet_parser::ParserSettings {749					loc_data: true,750					file_name: Rc::new(PathBuf::from("std.jsonnet")),751				},752			)753		})754	}755756	#[test]757	fn equality() {758		println!(759			"{:?}",760			jrsonnet_parser::parse(761				"{ x: 1, y: 2 } == { x: 1, y: 2 }",762				&ParserSettings::default()763			)764		);765		assert_eval!("{ x: 1, y: 2 } == { x: 1, y: 2 }")766	}767}
after · crates/jrsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![feature(test)]5#![feature(stmt_expr_attributes)]6#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]78extern crate test;910mod builtin;11mod ctx;12mod dynamic;13mod error;14mod evaluate;15mod function;16mod import;17mod map;18mod obj;19pub mod trace;20mod val;2122pub use ctx::*;23pub use dynamic::*;24pub use error::*;25pub use evaluate::*;26pub use function::parse_function_call;27pub use import::*;28use jrsonnet_parser::*;29pub use obj::*;30use std::{31	cell::{Ref, RefCell, RefMut},32	collections::HashMap,33	fmt::Debug,34	path::PathBuf,35	rc::Rc,36};37use trace::{offset_to_location, CodeLocation, CompactFormat, TraceFormat};38pub use val::*;3940type BindableFn = dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>;41#[derive(Clone)]42pub enum LazyBinding {43	Bindable(Rc<BindableFn>),44	Bound(LazyVal),45}4647impl Debug for LazyBinding {48	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {49		write!(f, "LazyBinding")50	}51}52impl LazyBinding {53	pub fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {54		match self {55			LazyBinding::Bindable(v) => v(this, super_obj),56			LazyBinding::Bound(v) => Ok(v.clone()),57		}58	}59}6061#[derive(Clone)]62pub enum ManifestFormat {63	Yaml(usize),64	Json(usize),65	None,66}6768pub struct EvaluationSettings {69	/// Limits recursion by limiting stack frames70	pub max_stack: usize,71	/// Limit amount of stack trace items preserved72	pub max_trace: usize,73	/// Used for std.extVar74	pub ext_vars: HashMap<Rc<str>, Val>,75	/// TLA vars76	pub tla_vars: HashMap<Rc<str>, Val>,77	/// Global variables are inserted in default context78	pub globals: HashMap<Rc<str>, Val>,79	/// Used to resolve file locations/contents80	pub import_resolver: Box<dyn ImportResolver>,81	/// Used in manifestification functions82	pub manifest_format: ManifestFormat,83	/// Used for bindings84	pub trace_format: Box<dyn TraceFormat>,85}86impl Default for EvaluationSettings {87	fn default() -> Self {88		EvaluationSettings {89			max_stack: 200,90			max_trace: 20,91			globals: Default::default(),92			ext_vars: Default::default(),93			tla_vars: Default::default(),94			import_resolver: Box::new(DummyImportResolver),95			manifest_format: ManifestFormat::Json(4),96			trace_format: Box::new(CompactFormat {97				padding: 4,98				resolver: trace::PathResolver::Absolute,99			}),100		}101	}102}103104#[derive(Default)]105struct EvaluationData {106	/// Used for stack overflow detection, stacktrace is now populated on unwind107	stack_depth: usize,108	/// Contains file source codes and evaluated results for imports and pretty109	/// printing stacktraces110	files: HashMap<Rc<PathBuf>, FileData>,111	str_files: HashMap<Rc<PathBuf>, Rc<str>>,112}113114pub struct FileData {115	source_code: Rc<str>,116	parsed: LocExpr,117	evaluated: Option<Val>,118}119#[derive(Default)]120pub struct EvaluationStateInternals {121	/// Internal state122	data: RefCell<EvaluationData>,123	/// Settings, safe to change at runtime124	settings: RefCell<EvaluationSettings>,125}126127thread_local! {128	/// Contains state for currently executing file129	/// Global state is fine there130	pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)131}132pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {133	EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))134}135pub fn create_error(err: Error) -> LocError {136	LocError(err, StackTrace(vec![]))137}138pub fn create_error_result<T>(err: Error) -> Result<T> {139	Err(LocError(err, StackTrace(vec![])))140}141pub(crate) fn push<T>(142	e: &Option<ExprLocation>,143	frame_desc: impl FnOnce() -> String,144	f: impl FnOnce() -> Result<T>,145) -> Result<T> {146	if let Some(v) = e {147		with_state(|s| s.push(&v, frame_desc, f))148	} else {149		f()150	}151}152153/// Maintains stack trace and import resolution154#[derive(Default, Clone)]155pub struct EvaluationState(Rc<EvaluationStateInternals>);156157impl EvaluationState {158	/// Parses and adds file to loaded159	pub fn add_file(&self, path: Rc<PathBuf>, source_code: Rc<str>) -> Result<()> {160		self.add_parsed_file(161			path.clone(),162			source_code.clone(),163			parse(164				&source_code,165				&ParserSettings {166					file_name: path.clone(),167					loc_data: true,168				},169			)170			.map_err(|error| {171				create_error(Error::ImportSyntaxError {172					error,173					path,174					source_code,175				})176			})?,177		)?;178179		Ok(())180	}181182	/// Adds file by source code and parsed expr183	pub fn add_parsed_file(184		&self,185		name: Rc<PathBuf>,186		source_code: Rc<str>,187		parsed: LocExpr,188	) -> Result<()> {189		self.data_mut().files.insert(190			name,191			FileData {192				source_code,193				parsed,194				evaluated: None,195			},196		);197198		Ok(())199	}200	pub fn get_source(&self, name: &PathBuf) -> Option<Rc<str>> {201		let ro_map = &self.data().files;202		ro_map.get(name).map(|value| value.source_code.clone())203	}204	pub fn map_source_locations(&self, file: &PathBuf, locs: &[usize]) -> Vec<CodeLocation> {205		offset_to_location(&self.get_source(file).unwrap(), locs)206	}207208	pub(crate) fn import_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Val> {209		let file_path = self.resolve_file(from, path)?;210		{211			let files = &self.data().files;212			if files.contains_key(&file_path) {213				return self.evaluate_loaded_file_raw(&file_path);214			}215		}216		let contents = self.load_file_contents(&file_path)?;217		self.add_file(file_path.clone(), contents)?;218		self.evaluate_loaded_file_raw(&file_path)219	}220	pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {221		let path = self.resolve_file(from, path)?;222		if !self.data().str_files.contains_key(&path) {223			let file_str = self.load_file_contents(&path)?;224			self.data_mut().str_files.insert(path.clone(), file_str);225		}226		Ok(self.data().str_files.get(&path).cloned().unwrap())227	}228229	fn evaluate_loaded_file_raw(&self, name: &PathBuf) -> Result<Val> {230		let expr: LocExpr = {231			let ro_map = &self.data().files;232			let value = ro_map233				.get(name)234				.unwrap_or_else(|| panic!("file not added: {:?}", name));235			if let Some(ref evaluated) = value.evaluated {236				return Ok(evaluated.clone());237			}238			value.parsed.clone()239		};240		let value = evaluate(self.create_default_context()?, &expr)?;241		{242			self.data_mut()243				.files244				.get_mut(name)245				.unwrap()246				.evaluated247				.replace(value.clone());248		}249		Ok(value)250	}251252	/// Adds standard library global variable (std) to this evaluator253	pub fn with_stdlib(&self) -> &Self {254		use jrsonnet_stdlib::STDLIB_STR;255		let std_path = Rc::new(PathBuf::from("std.jsonnet"));256		self.run_in_state(|| {257			self.add_parsed_file(258				std_path.clone(),259				STDLIB_STR.to_owned().into(),260				builtin::get_parsed_stdlib(),261			)262			.unwrap();263			let val = self.evaluate_loaded_file_raw(&std_path).unwrap();264			self.settings_mut().globals.insert("std".into(), val);265		});266		self267	}268269	/// Creates context with all passed global variables270	pub fn create_default_context(&self) -> Result<Context> {271		let globals = &self.settings().globals;272		let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();273		for (name, value) in globals.iter() {274			new_bindings.insert(275				name.clone(),276				LazyBinding::Bound(resolved_lazy_val!(value.clone())),277			);278		}279		Context::new().extend_unbound(new_bindings, None, None, None)280	}281282	/// Executes code, creating new stack frame283	pub fn push<T>(284		&self,285		e: &ExprLocation,286		frame_desc: impl FnOnce() -> String,287		f: impl FnOnce() -> Result<T>,288	) -> Result<T> {289		{290			let mut data = self.data_mut();291			let stack_depth = &mut data.stack_depth;292			if *stack_depth > self.max_stack() {293				// Error creation uses data, so i drop guard here294				drop(data);295				return Err(create_error(Error::StackOverflow));296			} else {297				*stack_depth += 1;298			}299		}300		let result = f();301		self.data_mut().stack_depth -= 1;302		if let Err(mut err) = result {303			(err.1).0.push(StackTraceElement {304				location: e.clone(),305				desc: frame_desc(),306			});307			return Err(err);308		}309		result310	}311312	/// Runs passed function in state (required, if function needs to modify stack trace)313	pub fn run_in_state<T>(&self, f: impl FnOnce() -> T) -> T {314		EVAL_STATE.with(|v| {315			let has_state = v.borrow().is_some();316			if !has_state {317				v.borrow_mut().replace(self.clone());318			}319			let result = f();320			if !has_state {321				v.borrow_mut().take();322			}323			result324		})325	}326327	pub fn stringify_err(&self, e: &LocError) -> String {328		let mut out = String::new();329		self.settings()330			.trace_format331			.write_trace(&mut out, self, e)332			.unwrap();333		out334	}335336	pub fn manifest(&self, val: Val) -> Result<Rc<str>> {337		Ok(match self.manifest_format() {338			ManifestFormat::Yaml(padding) => val.into_yaml(padding)?,339			ManifestFormat::Json(padding) => val.into_json(padding)?,340			ManifestFormat::None => match val {341				Val::Str(s) => s,342				_ => return Err(create_error(Error::StringManifestOutputIsNotAString)),343			},344		})345	}346347	/// If passed value is function - call with set TLA348	pub fn with_tla(&self, val: Val) -> Result<Val> {349		Ok(match val {350			Val::Func(func) => func.evaluate_map(351				self.create_default_context()?,352				&self.settings().tla_vars,353				true,354			)?,355			v => v,356		})357	}358}359360/// Internals361impl EvaluationState {362	fn data(&self) -> Ref<EvaluationData> {363		self.0.data.borrow()364	}365	fn data_mut(&self) -> RefMut<EvaluationData> {366		self.0.data.borrow_mut()367	}368	pub fn settings(&self) -> Ref<EvaluationSettings> {369		self.0.settings.borrow()370	}371	pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {372		self.0.settings.borrow_mut()373	}374}375376/// Raw methods evaluates passed values, but not performs TLA execution377impl EvaluationState {378	pub fn evaluate_file_raw(&self, name: &PathBuf) -> Result<Val> {379		self.import_file(&std::env::current_dir().expect("cwd"), &name)380	}381	pub fn evaluate_file_raw_nocwd(&self, name: &PathBuf) -> Result<Val> {382		self.import_file(&PathBuf::from("."), &name)383	}384	/// Parses and evaluates snippet385	pub fn evaluate_snippet_raw(&self, source: Rc<PathBuf>, code: Rc<str>) -> Result<Val> {386		let parsed = parse(387			&code,388			&ParserSettings {389				file_name: source.clone(),390				loc_data: true,391			},392		)393		.unwrap();394		self.add_parsed_file(source, code, parsed.clone())?;395		self.evaluate_expr_raw(parsed)396	}397	/// Evaluates parsed expression398	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {399		evaluate(self.create_default_context()?, &code)400	}401}402403/// Settings utilities404impl EvaluationState {405	pub fn add_ext_var(&self, name: Rc<str>, value: Val) {406		self.settings_mut().ext_vars.insert(name, value);407	}408	pub fn add_ext_str(&self, name: Rc<str>, value: Rc<str>) {409		self.add_ext_var(name, Val::Str(value));410	}411	pub fn add_ext_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {412		let value =413			self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("ext_code {}", name))), code)?;414		self.add_ext_var(name, value);415		Ok(())416	}417418	pub fn add_tla(&self, name: Rc<str>, value: Val) {419		self.settings_mut().tla_vars.insert(name, value);420	}421	pub fn add_tla_str(&self, name: Rc<str>, value: Rc<str>) {422		self.add_tla(name, Val::Str(value));423	}424	pub fn add_tla_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {425		let value =426			self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("tla_code {}", name))), code)?;427		self.add_ext_var(name, value);428		Ok(())429	}430431	pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {432		Ok(self.settings().import_resolver.resolve_file(from, path)?)433	}434	pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {435		Ok(self.settings().import_resolver.load_file_contents(path)?)436	}437438	pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {439		Ref::map(self.settings(), |s| &*s.import_resolver)440	}441	pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {442		self.settings_mut().import_resolver = resolver;443	}444445	pub fn manifest_format(&self) -> ManifestFormat {446		self.settings().manifest_format.clone()447	}448	pub fn set_manifest_format(&self, format: ManifestFormat) {449		self.settings_mut().manifest_format = format;450	}451452	pub fn trace_format(&self) -> Ref<dyn TraceFormat> {453		Ref::map(self.settings(), |s| &*s.trace_format)454	}455	pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {456		self.settings_mut().trace_format = format;457	}458459	pub fn max_trace(&self) -> usize {460		self.settings().max_trace461	}462	pub fn set_max_trace(&self, trace: usize) {463		self.settings_mut().max_trace = trace;464	}465466	pub fn max_stack(&self) -> usize {467		self.settings().max_stack468	}469	pub fn set_max_stack(&self, trace: usize) {470		self.settings_mut().max_stack = trace;471	}472}473474#[cfg(test)]475pub mod tests {476	use super::Val;477	use crate::{create_error, primitive_equals, EvaluationState};478	use jrsonnet_parser::*;479	use std::{path::PathBuf, rc::Rc};480481	#[test]482	fn eval_state_stacktrace() {483		let state = EvaluationState::default();484		state.run_in_state(|| {485			state486				.push(487					&ExprLocation(Rc::new(PathBuf::from("test1.jsonnet")), 10, 20),488					|| "outer".to_owned(),489					|| {490						state.push(491							&ExprLocation(Rc::new(PathBuf::from("test2.jsonnet")), 30, 40),492							|| "inner".to_owned(),493							|| Err(create_error(crate::error::Error::RuntimeError("".into()))),494						)?;495						Ok(())496					},497				)498				.unwrap();499		});500	}501502	#[test]503	fn eval_state_standard() {504		let state = EvaluationState::default();505		state.with_stdlib();506		assert!(primitive_equals(507			&state508				.evaluate_snippet_raw(509					Rc::new(PathBuf::from("raw.jsonnet")),510					r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#.into()511				)512				.unwrap(),513			&Val::Bool(true),514		)515		.unwrap());516	}517518	macro_rules! eval {519		($str: expr) => {520			EvaluationState::default()521				.with_stdlib()522				.evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())523				.unwrap()524		};525	}526	macro_rules! eval_json {527		($str: expr) => {{528			let evaluator = EvaluationState::default();529			evaluator.with_stdlib();530			evaluator.run_in_state(|| {531				evaluator532					.evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())533					.unwrap()534					.into_json(0)535					.unwrap()536					.replace("\n", "")537				})538			}};539	}540541	/// Asserts given code returns `true`542	macro_rules! assert_eval {543		($str: expr) => {544			assert!(primitive_equals(&eval!($str), &Val::Bool(true)).unwrap())545		};546	}547548	/// Asserts given code returns `false`549	macro_rules! assert_eval_neg {550		($str: expr) => {551			assert!(primitive_equals(&eval!($str), &Val::Bool(false)).unwrap())552		};553	}554	macro_rules! assert_json {555		($str: expr, $out: expr) => {556			assert_eq!(eval_json!($str), $out.replace("\t", ""))557		};558	}559560	/// Sanity checking, before trusting to another tests561	#[test]562	fn equality_operator() {563		assert_eval!("2 == 2");564		assert_eval_neg!("2 != 2");565		assert_eval!("2 != 3");566		assert_eval_neg!("2 == 3");567		assert_eval!("'Hello' == 'Hello'");568		assert_eval_neg!("'Hello' != 'Hello'");569		assert_eval!("'Hello' != 'World'");570		assert_eval_neg!("'Hello' == 'World'");571	}572573	#[test]574	fn math_evaluation() {575		assert_eval!("2 + 2 * 2 == 6");576		assert_eval!("3 + (2 + 2 * 2) == 9");577	}578579	#[test]580	fn string_concat() {581		assert_eval!("'Hello' + 'World' == 'HelloWorld'");582		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");583		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");584	}585586	#[test]587	fn faster_join() {588		assert_eval!("std.join([0,0], [[1,2],[3,4],[5,6]]) == [1,2,0,0,3,4,0,0,5,6]");589		assert_eval!("std.join(',', ['1','2','3','4']) == '1,2,3,4'");590	}591592	#[test]593	fn function_contexts() {594		assert_eval!(595			r#"596				local k = {597					t(name = self.h): [self.h, name],598					h: 3,599				};600				local f = {601					t: k.t(),602					h: 4,603				};604				f.t[0] == f.t[1]605			"#606		);607	}608609	#[test]610	fn local() {611		assert_eval!("local a = 2; local b = 3; a + b == 5");612		assert_eval!("local a = 1, b = a + 1; a + b == 3");613		assert_eval!("local a = 1; local a = 2; a == 2");614	}615616	#[test]617	fn object_lazyness() {618		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);619	}620621	#[test]622	fn object_inheritance() {623		assert_json!("{a: self.b} + {b:3}", r#"{"a": 3,"b": 3}"#);624	}625626	#[test]627	fn object_assertion_success() {628		eval!("{assert \"a\" in self} + {a:2}");629	}630631	#[test]632	fn object_assertion_error() {633		eval!("{assert \"a\" in self}");634	}635636	#[test]637	fn lazy_args() {638		eval!("local test(a) = 2; test(error '3')");639	}640641	#[test]642	#[should_panic]643	fn tailstrict_args() {644		eval!("local test(a) = 2; test(error '3') tailstrict");645	}646647	#[test]648	#[should_panic]649	fn no_binding_error() {650		eval!("a");651	}652653	#[test]654	fn test_object() {655		assert_json!("{a:2}", r#"{"a": 2}"#);656		assert_json!("{a:2+2}", r#"{"a": 4}"#);657		assert_json!("{a:2}+{b:2}", r#"{"a": 2,"b": 2}"#);658		assert_json!("{b:3}+{b:2}", r#"{"b": 2}"#);659		assert_json!("{b:3}+{b+:2}", r#"{"b": 5}"#);660		assert_json!("local test='a'; {[test]:2}", r#"{"a": 2}"#);661		assert_json!(662			r#"663				{664					name: "Alice",665					welcome: "Hello " + self.name + "!",666				}667			"#,668			r#"{"name": "Alice","welcome": "Hello Alice!"}"#669		);670		assert_json!(671			r#"672				{673					name: "Alice",674					welcome: "Hello " + self.name + "!",675				} + {676					name: "Bob"677				}678			"#,679			r#"{"name": "Bob","welcome": "Hello Bob!"}"#680		);681	}682683	#[test]684	fn functions() {685		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");686		assert_json!(687			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,688			r#""HelloDearWorld""#689		);690	}691692	#[test]693	fn local_methods() {694		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");695		assert_json!(696			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,697			r#""HelloDearWorld""#698		);699	}700701	#[test]702	fn object_locals() {703		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b": 3}"#);704		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b": 3}"#);705		assert_json!(706			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,707			r#"{"test": {"test": 4}}"#708		);709	}710711	#[test]712	fn object_comp() {713		assert_json!(714			r#"{local t = "a", ["h"+i+"_"+z]: if "h"+(i-1)+"_"+z in self then t+1 else 0+t for i in [1,2,3] for z in [2,3,4] if z != i}"#,715			"{\"h1_2\": \"0a\",\"h1_3\": \"0a\",\"h1_4\": \"0a\",\"h2_3\": \"a1\",\"h2_4\": \"a1\",\"h3_2\": \"0a\",\"h3_4\": \"a1\"}"716		)717	}718719	#[test]720	fn direct_self() {721		println!(722			"{:#?}",723			eval!(724				r#"725					{726						local me = self,727						a: 3,728						b(): me.a,729					}730				"#731			)732		);733	}734735	#[test]736	fn indirect_self() {737		// `self` assigned to `me` was lost when being738		// referenced from field739		eval!(740			r#"{741				local me = self,742				a: 3,743				b: me.a,744			}.b"#745		);746	}747748	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly749	#[test]750	fn std_assert_ok() {751		eval!("std.assertEqual(4.5 << 2, 16)");752	}753754	#[test]755	#[should_panic]756	fn std_assert_failure() {757		eval!("std.assertEqual(4.5 << 2, 15)");758	}759760	#[test]761	fn string_is_string() {762		assert!(primitive_equals(763			&eval!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),764			&Val::Bool(false),765		)766		.unwrap());767	}768769	#[test]770	fn base64_works() {771		assert_json!(r#"std.base64("test")"#, r#""dGVzdA==""#);772	}773774	#[test]775	fn utf8_chars() {776		assert_json!(777			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,778			r#"{"c": 128526,"l": 1}"#779		)780	}781782	#[test]783	fn json() {784		assert_json!(785			r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,786			r#""{\n\"a\": 3,\n\"b\": 4,\n\"c\": 6\n}""#787		);788	}789790	#[test]791	fn test() {792		assert_json!(793			r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,794			"[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"795		);796	}797798	#[test]799	fn sjsonnet() {800		eval!(801			r#"802			local x0 = {k: 1};803			local x1 = {k: x0.k + x0.k};804			local x2 = {k: x1.k + x1.k};805			local x3 = {k: x2.k + x2.k};806			local x4 = {k: x3.k + x3.k};807			local x5 = {k: x4.k + x4.k};808			local x6 = {k: x5.k + x5.k};809			local x7 = {k: x6.k + x6.k};810			local x8 = {k: x7.k + x7.k};811			local x9 = {k: x8.k + x8.k};812			local x10 = {k: x9.k + x9.k};813			local x11 = {k: x10.k + x10.k};814			local x12 = {k: x11.k + x11.k};815			local x13 = {k: x12.k + x12.k};816			local x14 = {k: x13.k + x13.k};817			local x15 = {k: x14.k + x14.k};818			local x16 = {k: x15.k + x15.k};819			local x17 = {k: x16.k + x16.k};820			local x18 = {k: x17.k + x17.k};821			local x19 = {k: x18.k + x18.k};822			local x20 = {k: x19.k + x19.k};823			local x21 = {k: x20.k + x20.k};824			x21.k825		"#826		);827	}828829	use test::Bencher;830831	// This test is commented out by default, because of huge compilation slowdown832	// #[bench]833	// fn bench_codegen(b: &mut Bencher) {834	// 	b.iter(|| {835	// 		#[allow(clippy::all)]836	// 		let stdlib = {837	// 			use jrsonnet_parser::*;838	// 			include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))839	// 		};840	// 		stdlib841	// 	})842	// }843844	#[bench]845	fn bench_serialize(b: &mut Bencher) {846		b.iter(|| {847			bincode::deserialize::<jrsonnet_parser::LocExpr>(include_bytes!(concat!(848				env!("OUT_DIR"),849				"/stdlib.bincode"850			)))851			.expect("deserialize stdlib")852		})853	}854855	#[bench]856	fn bench_parse(b: &mut Bencher) {857		b.iter(|| {858			jrsonnet_parser::parse(859				jrsonnet_stdlib::STDLIB_STR,860				&jrsonnet_parser::ParserSettings {861					loc_data: true,862					file_name: Rc::new(PathBuf::from("std.jsonnet")),863				},864			)865		})866	}867868	#[test]869	fn equality() {870		println!(871			"{:?}",872			jrsonnet_parser::parse(873				"{ x: 1, y: 2 } == { x: 1, y: 2 }",874				&ParserSettings::default()875			)876		);877		assert_eval!("{ x: 1, y: 2 } == { x: 1, y: 2 }")878	}879}