git.delta.rocks / jrsonnet / refs/commits / 9d883c6db8a3

difftreelog

source

crates/jrsonnet-evaluator/src/lib.rs20.9 KiBsourcehistory
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		self.run_in_state(|| {338			Ok(match self.manifest_format() {339				ManifestFormat::Yaml(padding) => val.into_yaml(padding)?,340				ManifestFormat::Json(padding) => val.into_json(padding)?,341				ManifestFormat::None => match val {342					Val::Str(s) => s,343					_ => return Err(create_error(Error::StringManifestOutputIsNotAString)),344				},345			})346		})347	}348349	/// If passed value is function - call with set TLA350	pub fn with_tla(&self, val: Val) -> Result<Val> {351		Ok(match val {352			Val::Func(func) => func.evaluate_map(353				self.create_default_context()?,354				&self.settings().tla_vars,355				true,356			)?,357			v => v,358		})359	}360}361362/// Internals363impl EvaluationState {364	fn data(&self) -> Ref<EvaluationData> {365		self.0.data.borrow()366	}367	fn data_mut(&self) -> RefMut<EvaluationData> {368		self.0.data.borrow_mut()369	}370	pub fn settings(&self) -> Ref<EvaluationSettings> {371		self.0.settings.borrow()372	}373	pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {374		self.0.settings.borrow_mut()375	}376}377378/// Raw methods evaluates passed values, but not performs TLA execution379impl EvaluationState {380	pub fn evaluate_file_raw(&self, name: &PathBuf) -> Result<Val> {381		self.run_in_state(|| self.import_file(&std::env::current_dir().expect("cwd"), &name))382	}383	pub fn evaluate_file_raw_nocwd(&self, name: &PathBuf) -> Result<Val> {384		self.run_in_state(|| self.import_file(&PathBuf::from("."), &name))385	}386	/// Parses and evaluates snippet387	pub fn evaluate_snippet_raw(&self, source: Rc<PathBuf>, code: Rc<str>) -> Result<Val> {388		let parsed = parse(389			&code,390			&ParserSettings {391				file_name: source.clone(),392				loc_data: true,393			},394		)395		.unwrap();396		self.add_parsed_file(source, code, parsed.clone())?;397		self.evaluate_expr_raw(parsed)398	}399	/// Evaluates parsed expression400	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {401		self.run_in_state(|| evaluate(self.create_default_context()?, &code))402	}403}404405/// Settings utilities406impl EvaluationState {407	pub fn add_ext_var(&self, name: Rc<str>, value: Val) {408		self.settings_mut().ext_vars.insert(name, value);409	}410	pub fn add_ext_str(&self, name: Rc<str>, value: Rc<str>) {411		self.add_ext_var(name, Val::Str(value));412	}413	pub fn add_ext_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {414		let value =415			self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("ext_code {}", name))), code)?;416		self.add_ext_var(name, value);417		Ok(())418	}419420	pub fn add_tla(&self, name: Rc<str>, value: Val) {421		self.settings_mut().tla_vars.insert(name, value);422	}423	pub fn add_tla_str(&self, name: Rc<str>, value: Rc<str>) {424		self.add_tla(name, Val::Str(value));425	}426	pub fn add_tla_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {427		let value =428			self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("tla_code {}", name))), code)?;429		self.add_ext_var(name, value);430		Ok(())431	}432433	pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {434		Ok(self.settings().import_resolver.resolve_file(from, path)?)435	}436	pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {437		Ok(self.settings().import_resolver.load_file_contents(path)?)438	}439440	pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {441		Ref::map(self.settings(), |s| &*s.import_resolver)442	}443	pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {444		self.settings_mut().import_resolver = resolver;445	}446447	pub fn manifest_format(&self) -> ManifestFormat {448		self.settings().manifest_format.clone()449	}450	pub fn set_manifest_format(&self, format: ManifestFormat) {451		self.settings_mut().manifest_format = format;452	}453454	pub fn trace_format(&self) -> Ref<dyn TraceFormat> {455		Ref::map(self.settings(), |s| &*s.trace_format)456	}457	pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {458		self.settings_mut().trace_format = format;459	}460461	pub fn max_trace(&self) -> usize {462		self.settings().max_trace463	}464	pub fn set_max_trace(&self, trace: usize) {465		self.settings_mut().max_trace = trace;466	}467468	pub fn max_stack(&self) -> usize {469		self.settings().max_stack470	}471	pub fn set_max_stack(&self, trace: usize) {472		self.settings_mut().max_stack = trace;473	}474}475476#[cfg(test)]477pub mod tests {478	use super::Val;479	use crate::{create_error, primitive_equals, EvaluationState};480	use jrsonnet_parser::*;481	use std::{path::PathBuf, rc::Rc};482483	#[test]484	#[should_panic]485	fn eval_state_stacktrace() {486		let state = EvaluationState::default();487		state.run_in_state(|| {488			state489				.push(490					&ExprLocation(Rc::new(PathBuf::from("test1.jsonnet")), 10, 20),491					|| "outer".to_owned(),492					|| {493						state.push(494							&ExprLocation(Rc::new(PathBuf::from("test2.jsonnet")), 30, 40),495							|| "inner".to_owned(),496							|| Err(create_error(crate::error::Error::RuntimeError("".into()))),497						)?;498						Ok(())499					},500				)501				.unwrap();502		});503	}504505	#[test]506	fn eval_state_standard() {507		let state = EvaluationState::default();508		state.with_stdlib();509		assert!(primitive_equals(510			&state511				.evaluate_snippet_raw(512					Rc::new(PathBuf::from("raw.jsonnet")),513					r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#.into()514				)515				.unwrap(),516			&Val::Bool(true),517		)518		.unwrap());519	}520521	macro_rules! eval {522		($str: expr) => {523			EvaluationState::default()524				.with_stdlib()525				.evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())526				.unwrap()527		};528	}529	macro_rules! eval_json {530		($str: expr) => {{531			let evaluator = EvaluationState::default();532			evaluator.with_stdlib();533			evaluator.run_in_state(|| {534				evaluator535					.evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())536					.unwrap()537					.into_json(0)538					.unwrap()539					.replace("\n", "")540				})541			}};542	}543544	/// Asserts given code returns `true`545	macro_rules! assert_eval {546		($str: expr) => {547			assert!(primitive_equals(&eval!($str), &Val::Bool(true)).unwrap())548		};549	}550551	/// Asserts given code returns `false`552	macro_rules! assert_eval_neg {553		($str: expr) => {554			assert!(primitive_equals(&eval!($str), &Val::Bool(false)).unwrap())555		};556	}557	macro_rules! assert_json {558		($str: expr, $out: expr) => {559			assert_eq!(eval_json!($str), $out.replace("\t", ""))560		};561	}562563	/// Sanity checking, before trusting to another tests564	#[test]565	fn equality_operator() {566		assert_eval!("2 == 2");567		assert_eval_neg!("2 != 2");568		assert_eval!("2 != 3");569		assert_eval_neg!("2 == 3");570		assert_eval!("'Hello' == 'Hello'");571		assert_eval_neg!("'Hello' != 'Hello'");572		assert_eval!("'Hello' != 'World'");573		assert_eval_neg!("'Hello' == 'World'");574	}575576	#[test]577	fn math_evaluation() {578		assert_eval!("2 + 2 * 2 == 6");579		assert_eval!("3 + (2 + 2 * 2) == 9");580	}581582	#[test]583	fn string_concat() {584		assert_eval!("'Hello' + 'World' == 'HelloWorld'");585		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");586		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");587	}588589	#[test]590	fn faster_join() {591		assert_eval!("std.join([0,0], [[1,2],[3,4],[5,6]]) == [1,2,0,0,3,4,0,0,5,6]");592		assert_eval!("std.join(',', ['1','2','3','4']) == '1,2,3,4'");593	}594595	#[test]596	fn function_contexts() {597		assert_eval!(598			r#"599				local k = {600					t(name = self.h): [self.h, name],601					h: 3,602				};603				local f = {604					t: k.t(),605					h: 4,606				};607				f.t[0] == f.t[1]608			"#609		);610	}611612	#[test]613	fn local() {614		assert_eval!("local a = 2; local b = 3; a + b == 5");615		assert_eval!("local a = 1, b = a + 1; a + b == 3");616		assert_eval!("local a = 1; local a = 2; a == 2");617	}618619	#[test]620	fn object_lazyness() {621		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);622	}623624	#[test]625	fn object_inheritance() {626		assert_json!("{a: self.b} + {b:3}", r#"{"a": 3,"b": 3}"#);627	}628629	#[test]630	fn object_assertion_success() {631		eval!("{assert \"a\" in self} + {a:2}");632	}633634	#[test]635	fn object_assertion_error() {636		eval!("{assert \"a\" in self}");637	}638639	#[test]640	fn lazy_args() {641		eval!("local test(a) = 2; test(error '3')");642	}643644	#[test]645	#[should_panic]646	fn tailstrict_args() {647		eval!("local test(a) = 2; test(error '3') tailstrict");648	}649650	#[test]651	#[should_panic]652	fn no_binding_error() {653		eval!("a");654	}655656	#[test]657	fn test_object() {658		assert_json!("{a:2}", r#"{"a": 2}"#);659		assert_json!("{a:2+2}", r#"{"a": 4}"#);660		assert_json!("{a:2}+{b:2}", r#"{"a": 2,"b": 2}"#);661		assert_json!("{b:3}+{b:2}", r#"{"b": 2}"#);662		assert_json!("{b:3}+{b+:2}", r#"{"b": 5}"#);663		assert_json!("local test='a'; {[test]:2}", r#"{"a": 2}"#);664		assert_json!(665			r#"666				{667					name: "Alice",668					welcome: "Hello " + self.name + "!",669				}670			"#,671			r#"{"name": "Alice","welcome": "Hello Alice!"}"#672		);673		assert_json!(674			r#"675				{676					name: "Alice",677					welcome: "Hello " + self.name + "!",678				} + {679					name: "Bob"680				}681			"#,682			r#"{"name": "Bob","welcome": "Hello Bob!"}"#683		);684	}685686	#[test]687	fn functions() {688		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");689		assert_json!(690			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,691			r#""HelloDearWorld""#692		);693	}694695	#[test]696	fn local_methods() {697		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");698		assert_json!(699			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,700			r#""HelloDearWorld""#701		);702	}703704	#[test]705	fn object_locals() {706		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b": 3}"#);707		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b": 3}"#);708		assert_json!(709			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,710			r#"{"test": {"test": 4}}"#711		);712	}713714	#[test]715	fn object_comp() {716		assert_json!(717			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}"#,718			"{\"h1_2\": \"0a\",\"h1_3\": \"0a\",\"h1_4\": \"0a\",\"h2_3\": \"a1\",\"h2_4\": \"a1\",\"h3_2\": \"0a\",\"h3_4\": \"a1\"}"719		)720	}721722	#[test]723	fn direct_self() {724		println!(725			"{:#?}",726			eval!(727				r#"728					{729						local me = self,730						a: 3,731						b(): me.a,732					}733				"#734			)735		);736	}737738	#[test]739	fn indirect_self() {740		// `self` assigned to `me` was lost when being741		// referenced from field742		eval!(743			r#"{744				local me = self,745				a: 3,746				b: me.a,747			}.b"#748		);749	}750751	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly752	#[test]753	fn std_assert_ok() {754		eval!("std.assertEqual(4.5 << 2, 16)");755	}756757	#[test]758	#[should_panic]759	fn std_assert_failure() {760		eval!("std.assertEqual(4.5 << 2, 15)");761	}762763	#[test]764	fn string_is_string() {765		assert!(primitive_equals(766			&eval!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),767			&Val::Bool(false),768		)769		.unwrap());770	}771772	#[test]773	fn base64_works() {774		assert_json!(r#"std.base64("test")"#, r#""dGVzdA==""#);775	}776777	#[test]778	fn utf8_chars() {779		assert_json!(780			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,781			r#"{"c": 128526,"l": 1}"#782		)783	}784785	#[test]786	fn json() {787		assert_json!(788			r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,789			r#""{\n\"a\": 3,\n\"b\": 4,\n\"c\": 6\n}""#790		);791	}792793	#[test]794	fn test() {795		assert_json!(796			r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,797			"[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"798		);799	}800801	#[test]802	fn sjsonnet() {803		eval!(804			r#"805			local x0 = {k: 1};806			local x1 = {k: x0.k + x0.k};807			local x2 = {k: x1.k + x1.k};808			local x3 = {k: x2.k + x2.k};809			local x4 = {k: x3.k + x3.k};810			local x5 = {k: x4.k + x4.k};811			local x6 = {k: x5.k + x5.k};812			local x7 = {k: x6.k + x6.k};813			local x8 = {k: x7.k + x7.k};814			local x9 = {k: x8.k + x8.k};815			local x10 = {k: x9.k + x9.k};816			local x11 = {k: x10.k + x10.k};817			local x12 = {k: x11.k + x11.k};818			local x13 = {k: x12.k + x12.k};819			local x14 = {k: x13.k + x13.k};820			local x15 = {k: x14.k + x14.k};821			local x16 = {k: x15.k + x15.k};822			local x17 = {k: x16.k + x16.k};823			local x18 = {k: x17.k + x17.k};824			local x19 = {k: x18.k + x18.k};825			local x20 = {k: x19.k + x19.k};826			local x21 = {k: x20.k + x20.k};827			x21.k828		"#829		);830	}831832	use test::Bencher;833834	// This test is commented out by default, because of huge compilation slowdown835	// #[bench]836	// fn bench_codegen(b: &mut Bencher) {837	// 	b.iter(|| {838	// 		#[allow(clippy::all)]839	// 		let stdlib = {840	// 			use jrsonnet_parser::*;841	// 			include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))842	// 		};843	// 		stdlib844	// 	})845	// }846847	#[bench]848	fn bench_serialize(b: &mut Bencher) {849		b.iter(|| {850			bincode::deserialize::<jrsonnet_parser::LocExpr>(include_bytes!(concat!(851				env!("OUT_DIR"),852				"/stdlib.bincode"853			)))854			.expect("deserialize stdlib")855		})856	}857858	#[bench]859	fn bench_parse(b: &mut Bencher) {860		b.iter(|| {861			jrsonnet_parser::parse(862				jrsonnet_stdlib::STDLIB_STR,863				&jrsonnet_parser::ParserSettings {864					loc_data: true,865					file_name: Rc::new(PathBuf::from("std.jsonnet")),866				},867			)868		})869	}870871	#[test]872	fn equality() {873		println!(874			"{:?}",875			jrsonnet_parser::parse(876				"{ x: 1, y: 2 } == { x: 1, y: 2 }",877				&ParserSettings::default()878			)879		);880		assert_eval!("{ x: 1, y: 2 } == { x: 1, y: 2 }")881	}882}