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

difftreelog

perf lesser and cheaper clones

Лач2020-06-26parent: #c1a6c6a.patch.diff
in: master

11 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -5,7 +5,7 @@
 use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};
 use location::{offset_to_location, CodeLocation};
 use std::env::current_dir;
-use std::{collections::HashMap, path::PathBuf, str::FromStr};
+use std::{collections::HashMap, path::PathBuf, str::FromStr, rc::Rc};
 
 enum Format {
 	None,
@@ -128,10 +128,10 @@
 		evaluator.with_stdlib();
 	}
 	for ExtStr { name, value } in opts.ext_str.iter().cloned() {
-		evaluator.add_ext_var(name, Val::Str(value));
+		evaluator.add_ext_var(name.into(), Val::Str(value.into()));
 	}
 	for ExtStr { name, value } in opts.ext_code.iter().cloned() {
-		evaluator.add_ext_var(name, evaluator.parse_evaluate_raw(&value).unwrap());
+		evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());
 	}
 	let mut input = current_dir().unwrap();
 	input.push(opts.input.clone());
@@ -147,7 +147,7 @@
 				Val::Func(f) => {
 					let mut desc_map = HashMap::new();
 					for ExtStr { name, value } in opts.tla_str.iter().cloned() {
-						desc_map.insert(name, el!(Expr::Str(value)));
+						desc_map.insert(name, el!(Expr::Str(value.into())));
 					}
 					for ExtStr { name, value } in opts.tla_code.iter().cloned() {
 						desc_map.insert(
@@ -155,17 +155,17 @@
 							jsonnet_parser::parse(
 								&value,
 								&ParserSettings {
-									file_name: PathBuf::new(),
+									file_name: Rc::new(PathBuf::new()),
 									loc_data: false,
 								},
 							)
 							.unwrap(),
 						);
 					}
-					evaluator.add_global("__tmp__tlf__".to_owned(), Val::Func(f));
+					evaluator.add_global("__tmp__tlf__".into(), Val::Func(f));
 					evaluator
 						.evaluate_raw(el!(Expr::Apply(
-							el!(Expr::Var("__tmp__tlf__".to_owned())),
+							el!(Expr::Var("__tmp__tlf__".into())),
 							ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),
 							false,
 						)))
@@ -178,7 +178,7 @@
 					if opts.no_stdlib {
 						evaluator.with_stdlib();
 					}
-					evaluator.add_global("__tmp__to_json__".to_owned(), v);
+					evaluator.add_global("__tmp__to_json__".into(), v);
 					let v = evaluator.parse_evaluate_raw(&format!(
 						"std.manifestJsonEx(__tmp__to_json__, \"{}\")",
 						" ".repeat(opts.line_padding),
@@ -195,7 +195,7 @@
 					if opts.no_stdlib {
 						evaluator.with_stdlib();
 					}
-					evaluator.add_global("__tmp__to_yaml__".to_owned(), v);
+					evaluator.add_global("__tmp__to_yaml__".into(), v);
 					let v = evaluator
 						.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")");
 					match v {
modifiedcrates/jsonnet-evaluator/build.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/build.rs
+++ b/crates/jsonnet-evaluator/build.rs
@@ -34,7 +34,7 @@
 								Member::Field(FieldMember {
 									name: FieldName::Fixed(name),
 									..
-								}) if name == "join"
+								}) if **name == *"join"
 							)
 						})
 						.collect(),
modifiedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -16,7 +16,7 @@
 	dollar: Option<ObjValue>,
 	this: Option<ObjValue>,
 	super_obj: Option<ObjValue>,
-	bindings: LayeredHashMap<String, LazyVal>,
+	bindings: LayeredHashMap<Rc<str>, LazyVal>,
 }
 pub struct Context(Rc<ContextInternals>);
 impl Debug for Context {
@@ -53,9 +53,9 @@
 		}))
 	}
 
-	pub fn binding(&self, name: &str) -> Result<LazyVal> {
-		self.0.bindings.get(name).cloned().ok_or_else(|| {
-			create_error::<()>(Error::UnknownVariable(name.to_owned()))
+	pub fn binding(&self, name: Rc<str>) -> Result<LazyVal> {
+		self.0.bindings.get(&name).cloned().ok_or_else(|| {
+			create_error::<()>(Error::UnknownVariable(name))
 				.err()
 				.unwrap()
 		})
@@ -67,7 +67,7 @@
 		ctx.unwrap()
 	}
 
-	pub fn with_var(&self, name: String, value: Val) -> Result<Context> {
+	pub fn with_var(&self, name: Rc<str>, value: Val) -> Result<Context> {
 		let mut new_bindings = HashMap::with_capacity(1);
 		new_bindings.insert(name, resolved_lazy_val!(value));
 		self.extend(new_bindings, None, None, None)
@@ -75,7 +75,7 @@
 
 	pub fn extend(
 		&self,
-		new_bindings: HashMap<String, LazyVal>,
+		new_bindings: HashMap<Rc<str>, LazyVal>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
@@ -97,7 +97,7 @@
 	}
 	pub fn extend_unbound(
 		&self,
-		new_bindings: HashMap<String, LazyBinding>,
+		new_bindings: HashMap<Rc<str>, LazyBinding>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
modifiedcrates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -6,20 +6,20 @@
 pub enum Error {
 	VariableIsNotDefined(String),
 	TypeMismatch(&'static str, Vec<ValType>, ValType),
-	NoSuchField(String),
+	NoSuchField(Rc<str>),
 
-	UnknownVariable(String),
+	UnknownVariable(Rc<str>),
 
 	UnknownFunctionParameter(String),
-	BindingParameterASecondTime(String),
+	BindingParameterASecondTime(Rc<str>),
 	TooManyArgsFunctionHas(usize),
-	FunctionParameterNotBoundInCall(String),
+	FunctionParameterNotBoundInCall(Rc<str>),
 
-	UndefinedExternalVariable(String),
+	UndefinedExternalVariable(Rc<str>),
 
 	FieldMustBeStringGot(ValType),
 
-	AttemptedIndexAnArrayWithString(String),
+	AttemptedIndexAnArrayWithString(Rc<str>),
 	ValueIndexMustBeTypeGot(ValType, ValType, ValType),
 	CantIndexInto(ValType),
 
@@ -31,7 +31,7 @@
 	ImportNotSupported(PathBuf, PathBuf),
 	ImportSyntaxError(jsonnet_parser::ParseError),
 
-	RuntimeError(String),
+	RuntimeError(Rc<str>),
 	StackOverflow,
 	FractionalIndex,
 	DivisionByZero,
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
before · crates/jsonnet-evaluator/src/evaluate.rs
1use crate::{2	context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context,3	ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4	ValType,5};6use closure::closure;7use jsonnet_parser::{8	el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,9	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10	Visibility,11};12use std::{13	collections::{BTreeMap, HashMap},14	rc::Rc,15};1617pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {18	let b = b.clone();19	if let Some(params) = &b.params {20		let params = params.clone();21		(22			b.name.clone(),23			LazyBinding::Bindable(Rc::new(move |this, super_obj| {24				Ok(lazy_val!(25					closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(26						context_creator.0(this.clone(), super_obj.clone())?,27						params.clone(),28						b.value.clone(),29					)))30				))31			})),32		)33	} else {34		(35			b.name.clone(),36			LazyBinding::Bindable(Rc::new(move |this, super_obj| {37				Ok(lazy_val!(closure!(clone context_creator, clone b, ||38					push(&b.value.1, "thunk", ||{39						evaluate(40							context_creator.0(this.clone(), super_obj.clone())?,41							&b.value42						)43					})44				)))45			})),46		)47	}48}4950pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val {51	Val::Func(FuncDesc { ctx, params, body })52}5354pub fn evaluate_field_name(55	context: Context,56	field_name: &jsonnet_parser::FieldName,57) -> Result<Option<String>> {58	Ok(match field_name {59		jsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),60		jsonnet_parser::FieldName::Dyn(expr) => {61			let value = evaluate(context, expr)?.unwrap_if_lazy()?;62			if matches!(value, Val::Null) {63				None64			} else {65				Some(value.try_cast_str("dynamic field name")?)66			}67		}68	})69}7071pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {72	Ok(match (op, b) {73		(o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,74		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),75		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),76		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),77		(op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),78	})79}8081pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {82	Ok(match (a, b) {83		(Val::Str(v1), Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),8485		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)86		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o)),87		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n)),8889		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_json(0)?)),90		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_json(0)?, s)),9192		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),93		(Val::Arr(a), Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),94		(Val::Num(v1), Val::Num(v2)) => Val::Num(v1 + v2),95		_ => panic!("can't add: {:?} and {:?}", a, b),96	})97}9899pub fn evaluate_binary_op_special(100	context: Context,101	a: &LocExpr,102	op: BinaryOpType,103	b: &LocExpr,104) -> Result<Val> {105	Ok(106		match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {107			(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),108			(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),109			(a, op, eb) => {110				evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?111			}112		},113	)114}115116pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {117	Ok(match (a, op, b) {118		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,119120		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),121122		// Bool X Bool123		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),124		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),125126		// Str X Str127		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),128		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),129		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),130		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),131132		// Num X Num133		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),134		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {135			if *v2 <= f64::EPSILON {136				create_error(crate::Error::DivisionByZero)?137			}138			Val::Num(v1 / v2)139		}140141		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),142143		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),144		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),145		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),146		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),147148		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {149			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)150		}151		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {152			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)153		}154		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {155			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)156		}157		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {158			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)159		}160		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {161			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)162		}163164		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),165	})166}167168future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);169future_wrapper!(ObjValue, FutureObjValue);170171pub fn evaluate_comp<T>(172	context: Context,173	value: &impl Fn(Context) -> Result<T>,174	specs: &[CompSpec],175) -> Result<Option<Vec<T>>> {176	Ok(match specs.get(0) {177		None => Some(vec![value(context)?]),178		Some(CompSpec::IfSpec(IfSpecData(cond))) => {179			if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {180				evaluate_comp(context, value, &specs[1..])?181			} else {182				None183			}184		}185		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {186			match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {187				Val::Arr(list) => {188					let mut out = Vec::new();189					for item in list {190						let item = item.clone().unwrap_if_lazy()?;191						out.push(evaluate_comp(192							context.with_var(var.clone(), item)?,193							value,194							&specs[1..],195						)?);196					}197					Some(out.into_iter().flatten().flatten().collect())198				}199				_ => panic!("for expression evaluated to non-iterable value"),200			}201		}202	})203}204205// TODO: Asserts206pub fn evaluate_object(context: Context, object: ObjBody) -> Result<ObjValue> {207	Ok(match object {208		ObjBody::MemberList(members) => {209			let new_bindings = FutureNewBindings::new();210			let future_this = FutureObjValue::new();211			let context_creator = context_creator!(212				closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {213					Ok(context.extend_unbound(214						new_bindings.clone().unwrap(),215						context.dollar().clone().or_else(||this.clone()),216						Some(this.unwrap()),217						super_obj218					)?)219				})220			);221			{222				let mut bindings: HashMap<String, LazyBinding> = HashMap::new();223				for (n, b) in members224					.iter()225					.filter_map(|m| match m {226						Member::BindStmt(b) => Some(b.clone()),227						_ => None,228					})229					.map(|b| evaluate_binding(&b, context_creator.clone()))230				{231					bindings.insert(n, b);232				}233				new_bindings.fill(bindings);234			}235236			let mut new_members = BTreeMap::new();237			for member in members.into_iter() {238				match member {239					Member::Field(FieldMember {240						name,241						plus,242						params: None,243						visibility,244						value,245					}) => {246						let name = evaluate_field_name(context.clone(), &name)?;247						if name.is_none() {248							continue;249						}250						let name = name.unwrap();251						new_members.insert(252							name.clone(),253							ObjMember {254								add: plus,255								visibility: visibility.clone(),256								invoke: LazyBinding::Bindable(Rc::new(257									closure!(clone name, clone value, clone context_creator, |this, super_obj| {258										Ok(LazyVal::new_resolved(push(&value.1, "object field", ||{259											let context = context_creator.0(this, super_obj)?;260											evaluate(261												context,262												&value,263											)?.unwrap_if_lazy()264										})?))265									}),266								)),267							},268						);269					}270					Member::Field(FieldMember {271						name,272						params: Some(params),273						value,274						..275					}) => {276						let name = evaluate_field_name(context.clone(), &name)?;277						if name.is_none() {278							continue;279						}280						let name = name.unwrap();281						new_members.insert(282							name,283							ObjMember {284								add: false,285								visibility: Visibility::Hidden,286								invoke: LazyBinding::Bindable(Rc::new(287									closure!(clone value, clone context_creator, |this, super_obj| {288										// TODO: Assert289										Ok(LazyVal::new_resolved(evaluate_method(290											context_creator.0(this, super_obj)?,291											params.clone(),292											value.clone(),293										)))294									}),295								)),296							},297						);298					}299					Member::BindStmt(_) => {}300					Member::AssertStmt(_) => {}301				}302			}303			future_this.fill(ObjValue::new(None, Rc::new(new_members)))304		}305		ObjBody::ObjComp {306			pre_locals,307			key,308			value,309			post_locals,310			compspecs,311		} => {312			let future_this = FutureObjValue::new();313			let mut new_members = BTreeMap::new();314			for (k, v) in evaluate_comp(315				context.clone(),316				&|ctx| {317					let new_bindings = FutureNewBindings::new();318					let context_creator = context_creator!(319						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {320							Ok(context.extend_unbound(321								new_bindings.clone().unwrap(),322								context.dollar().clone().or_else(||this.clone()),323								None,324								super_obj325							)?)326						})327					);328					let mut bindings: HashMap<String, LazyBinding> = HashMap::new();329					for (n, b) in pre_locals330						.iter()331						.chain(post_locals.iter())332						.map(|b| evaluate_binding(b, context_creator.clone()))333					{334						bindings.insert(n, b);335					}336					let bindings = new_bindings.fill(bindings);337					let ctx = ctx.extend_unbound(bindings, None, None, None)?;338					let key = evaluate(ctx.clone(), &key)?;339					let value = LazyBinding::Bindable(Rc::new(340						closure!(clone ctx, clone value, |this, _super_obj| {341							Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))342						}),343					));344345					Ok((key, value))346				},347				&compspecs,348			)?349			.unwrap()350			{351				match k {352					Val::Null => {}353					Val::Str(n) => {354						new_members.insert(355							n,356							ObjMember {357								add: false,358								visibility: Visibility::Normal,359								invoke: v,360							},361						);362					}363					v => create_error(Error::FieldMustBeStringGot(v.value_type()?))?,364				}365			}366367			future_this.fill(ObjValue::new(None, Rc::new(new_members)))368		}369	})370}371372pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {373	use Expr::*;374	let LocExpr(expr, loc) = expr;375	Ok(match &**expr {376		Literal(LiteralType::This) => Val::Obj(377			context378				.this()379				.clone()380				.unwrap_or_else(|| panic!("this not found")),381		),382		Literal(LiteralType::Dollar) => Val::Obj(383			context384				.dollar()385				.clone()386				.unwrap_or_else(|| panic!("dollar not found")),387		),388		Literal(LiteralType::True) => Val::Bool(true),389		Literal(LiteralType::False) => Val::Bool(false),390		Literal(LiteralType::Null) => Val::Null,391		Parened(e) => evaluate(context, e)?,392		Str(v) => Val::Str(v.clone()),393		Num(v) => Val::Num(*v),394		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,395		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,396		Var(name) => push(loc, "var", || {397			Val::Lazy(context.binding(&name)?).unwrap_if_lazy()398		})?,399		Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {400			let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;401			context402				.super_obj()403				.clone()404				.expect("no super found")405				.get_raw(&name, &context.this().clone().expect("no this found"))?406				.expect("value not found")407		}408		Index(value, index) => {409			match (410				evaluate(context.clone(), value)?.unwrap_if_lazy()?,411				evaluate(context, index)?,412			) {413				(Val::Obj(v), Val::Str(s)) => {414					if let Some(v) = v.get(&s)? {415						v.unwrap_if_lazy()?416					} else if let Some(Val::Str(n)) = v.get("__intristic_namespace__")? {417						Val::Intristic(n, s)418					} else {419						create_error(crate::Error::NoSuchField(s))?420					}421				}422				(Val::Obj(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(423					ValType::Obj,424					ValType::Str,425					n.value_type()?,426				))?,427428				(Val::Arr(v), Val::Num(n)) => {429					if n.fract() > f64::EPSILON {430						create_error(crate::Error::FractionalIndex)?431					}432					v.get(n as usize)433						.unwrap_or_else(|| panic!("out of bounds"))434						.clone()435						.unwrap_if_lazy()?436				}437				(Val::Arr(_), Val::Str(n)) => {438					create_error(crate::Error::AttemptedIndexAnArrayWithString(n))?439				}440				(Val::Arr(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(441					ValType::Arr,442					ValType::Num,443					n.value_type()?,444				))?,445446				(Val::Str(s), Val::Num(n)) => {447					Val::Str(s.chars().skip(n as usize).take(1).collect())448				}449				(Val::Str(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(450					ValType::Str,451					ValType::Num,452					n.value_type()?,453				))?,454455				(v, _) => create_error(crate::Error::CantIndexInto(v.value_type()?))?,456			}457		}458		LocalExpr(bindings, returned) => {459			let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();460			let future_context = Context::new_future();461462			let context_creator = context_creator!(463				closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))464			);465466			for (k, v) in bindings467				.iter()468				.map(|b| evaluate_binding(b, context_creator.clone()))469			{470				new_bindings.insert(k, v);471			}472473			let context = context474				.extend_unbound(new_bindings, None, None, None)?475				.into_future(future_context);476			evaluate(context, &returned.clone())?477		}478		Arr(items) => {479			let mut out = Vec::with_capacity(items.len());480			for item in items {481				out.push(Val::Lazy(lazy_val!(482					closure!(clone context, clone item, || {483						evaluate(context.clone(), &item)484					})485				)));486			}487			Val::Arr(out)488		}489		ArrComp(expr, compspecs) => Val::Arr(490			// First compspec should be forspec, so no "None" possible here491			evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap(),492		),493		Obj(body) => Val::Obj(evaluate_object(context, body.clone())?),494		ObjExtend(s, t) => evaluate_add_op(495			&evaluate(context.clone(), s)?,496			&Val::Obj(evaluate_object(context, t.clone())?),497		)?,498		Apply(value, args, tailstrict) => {499			let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?;500			match value {501				Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {502					// arr/string/function503					("std", "length") => {504						assert_eq!(args.len(), 1);505						let expr = &args.get(0).unwrap().1;506						match evaluate(context, expr)? {507							Val::Str(n) => Val::Num(n.chars().count() as f64),508							Val::Arr(i) => Val::Num(i.len() as f64),509							Val::Obj(o) => Val::Num(510								o.fields_visibility()511									.into_iter()512									.filter(|(_k, v)| *v)513									.count() as f64,514							),515							v => panic!("can't get length of {:?}", v),516						}517					}518					// any519					("std", "type") => {520						assert_eq!(args.len(), 1);521						let expr = &args.get(0).unwrap().1;522						Val::Str(evaluate(context, expr)?.value_type()?.name().to_owned())523					}524					// length, idx=>any525					("std", "makeArray") => {526						assert_eq!(args.len(), 2);527						if let (Val::Num(v), Val::Func(d)) = (528							evaluate(context.clone(), &args[0].1)?,529							evaluate(context, &args[1].1)?,530						) {531							assert!(v >= 0.0);532							let mut out = Vec::with_capacity(v as usize);533							for i in 0..v as usize {534								let call_ctx =535									Context::new().with_var("v".to_owned(), Val::Num(i as f64))?;536								out.push(d.evaluate(537									call_ctx,538									&ArgsDesc(vec![Arg(None, el!(Expr::Var("v".to_owned())))]),539									true,540								)?)541							}542							Val::Arr(out)543						} else {544							panic!("bad makeArray call");545						}546					}547					// string548					("std", "codepoint") => {549						assert_eq!(args.len(), 1);550						if let Val::Str(s) = evaluate(context, &args[0].1)? {551							assert!(552								s.chars().count() == 1,553								"std.codepoint should receive single char string"554							);555							Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)556						} else {557							panic!("bad codepoint call");558						}559					}560					// object, includeHidden561					("std", "objectFieldsEx") => {562						assert_eq!(args.len(), 2);563						if let (Val::Obj(body), Val::Bool(include_hidden)) = (564							evaluate(context.clone(), &args[0].1)?,565							evaluate(context, &args[1].1)?,566						) {567							Val::Arr(568								body.fields_visibility()569									.into_iter()570									.filter(|(_k, v)| *v || include_hidden)571									.map(|(k, _v)| Val::Str(k))572									.collect(),573							)574						} else {575							panic!("bad objectFieldsEx call");576						}577					}578					// object, field, includeHidden579					("std", "objectHasEx") => {580						assert_eq!(args.len(), 3);581						if let (Val::Obj(body), Val::Str(name), Val::Bool(include_hidden)) = (582							evaluate(context.clone(), &args[0].1)?,583							evaluate(context.clone(), &args[1].1)?,584							evaluate(context, &args[2].1)?,585						) {586							Val::Bool(587								body.fields_visibility()588									.into_iter()589									.filter(|(_k, v)| *v || include_hidden)590									.any(|(k, _v)| k == name),591							)592						} else {593							panic!("bad objectHasEx call");594						}595					}596					("std", "primitiveEquals") => {597						assert_eq!(args.len(), 2);598						let (a, b) = (599							evaluate(context.clone(), &args[0].1)?,600							evaluate(context, &args[1].1)?,601						);602						Val::Bool(a == b)603					}604					("std", "modulo") => {605						assert_eq!(args.len(), 2);606						if let (Val::Num(a), Val::Num(b)) = (607							evaluate(context.clone(), &args[0].1)?,608							evaluate(context, &args[1].1)?,609						) {610							Val::Num(a % b)611						} else {612							panic!("bad modulo call");613						}614					}615					("std", "floor") => {616						assert_eq!(args.len(), 1);617						if let Val::Num(a) = evaluate(context, &args[0].1)? {618							Val::Num(a.floor())619						} else {620							panic!("bad floor call");621						}622					}623					("std", "trace") => {624						assert_eq!(args.len(), 2);625						if let (Val::Str(a), b) = (626							evaluate(context.clone(), &args[0].1)?,627							evaluate(context, &args[1].1)?,628						) {629							// TODO: Line numbers as in original jsonnet630							println!("TRACE: {}", a);631							b632						} else {633							panic!("bad trace call");634						}635					}636					("std", "pow") => {637						assert_eq!(args.len(), 2);638						if let (Val::Num(a), Val::Num(b)) = (639							evaluate(context.clone(), &args[0].1)?,640							evaluate(context, &args[1].1)?,641						) {642							Val::Num(a.powf(b))643						} else {644							panic!("bad pow call");645						}646					}647					("std", "extVar") => {648						assert_eq!(args.len(), 1);649						if let Val::Str(a) = evaluate(context, &args[0].1)? {650							with_state(|s| s.0.ext_vars.borrow().get(&a).cloned()).ok_or_else(651								|| {652									create_error::<()>(crate::Error::UndefinedExternalVariable(a))653										.err()654										.unwrap()655								},656							)?657						} else {658							panic!("bad extVar call");659						}660					}661					("std", "filter") => {662						assert_eq!(args.len(), 2);663						if let (Val::Func(predicate), Val::Arr(arr)) = (664							evaluate(context.clone(), &args[0].1)?,665							evaluate(context.clone(), &args[1].1)?,666						) {667							Val::Arr(668								arr.into_iter()669									.filter(|e| {670										predicate671											.evaluate_values(context.clone(), &[e.clone()])672											.unwrap()673											.try_cast_bool("filter predicate")674											.unwrap()675									})676									.collect(),677							)678						} else {679							panic!("bad filter call");680						}681					}682					// faster683					("std", "join") => {684						assert_eq!(args.len(), 2);685						let joiner = evaluate(context.clone(), &args[0].1)?.unwrap_if_lazy()?;686						let items = evaluate(context, &args[1].1)?.unwrap_if_lazy()?;687						match (joiner, items) {688							(Val::Arr(joiner_items), Val::Arr(items)) => {689								// TODO: Minimal size should be known690								let mut out = Vec::new();691692								let mut first = true;693								for item in items {694									if let Val::Arr(items) = item.unwrap_if_lazy()? {695										if !first {696											out.extend(joiner_items.iter().cloned());697										}698										first = false;699										out.extend(items);700									} else {701										panic!("all array items should be arrays")702									}703								}704705								Val::Arr(out)706							}707							(Val::Str(joiner), Val::Arr(items)) => {708								let mut out = String::new();709710								let mut first = true;711								for item in items {712									if let Val::Str(item) = item.unwrap_if_lazy()? {713										if !first {714											out += &joiner;715										}716										first = false;717										out += &item;718									} else {719										panic!("all array items should be strings")720									}721								}722723								Val::Str(out)724							}725							(joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items),726						}727					}728					(ns, name) => panic!("Intristic not found: {}.{}", ns, name),729				},730				Val::Func(f) => {731					let body = || f.evaluate(context, args, *tailstrict);732					if *tailstrict {733						body()?734					} else {735						push(loc, "function call", body)?736					}737				}738				_ => panic!("{:?} is not a function", value),739			}740		}741		Function(params, body) => evaluate_method(context, params.clone(), body.clone()),742		AssertExpr(AssertStmt(value, msg), returned) => {743			let assertion_result = push(&value.1, "assertion condition", || {744				evaluate(context.clone(), &value)?745					.try_cast_bool("assertion condition should be boolean")746			})?;747			if assertion_result {748				evaluate(context, returned)?749			} else if let Some(msg) = msg {750				panic!(751					"assertion failed ({:?}): {}",752					value,753					evaluate(context, msg)?.try_cast_str("assertion message should be string")?754				);755			} else {756				panic!("assertion failed ({:?}): no message", value);757			}758		}759		Error(e) => create_error(crate::Error::RuntimeError(760			evaluate(context, e)?.try_cast_str("error text should be string")?,761		))?,762		IfElse {763			cond,764			cond_then,765			cond_else,766		} => {767			if evaluate(context.clone(), &cond.0)?768				.try_cast_bool("if condition should be boolean")?769			{770				evaluate(context, cond_then)?771			} else {772				match cond_else {773					Some(v) => evaluate(context, v)?,774					None => Val::Null,775				}776			}777		}778		Import(path) => {779			let mut import_location = loc780				.clone()781				.expect("imports can't be used without loc_data")782				.0783				.clone();784			import_location.pop();785			with_state(|s| s.import_file(&import_location, path))?786		}787		ImportStr(path) => {788			let mut import_location = loc789				.clone()790				.expect("imports can't be used without loc_data")791				.0792				.clone();793			import_location.pop();794			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)795		}796		Literal(LiteralType::Super) => return create_error(crate::error::Error::StandaloneSuper),797	})798}
after · crates/jsonnet-evaluator/src/evaluate.rs
1use crate::{2	context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context,3	ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4	ValType,5};6use closure::closure;7use jsonnet_parser::{8	el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,9	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10	Visibility,11};12use std::{13	collections::{BTreeMap, HashMap},14	rc::Rc,15};1617pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {18	let b = b.clone();19	if let Some(params) = &b.params {20		let params = params.clone();21		(22			b.name.clone(),23			LazyBinding::Bindable(Rc::new(move |this, super_obj| {24				Ok(lazy_val!(25					closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(26						context_creator.0(this.clone(), super_obj.clone())?,27						params.clone(),28						b.value.clone(),29					)))30				))31			})),32		)33	} else {34		(35			b.name.clone(),36			LazyBinding::Bindable(Rc::new(move |this, super_obj| {37				Ok(lazy_val!(closure!(clone context_creator, clone b, ||38					push(&b.value.1, "thunk", ||{39						evaluate(40							context_creator.0(this.clone(), super_obj.clone())?,41							&b.value42						)43					})44				)))45			})),46		)47	}48}4950pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val {51	Val::Func(FuncDesc { ctx, params, body })52}5354pub fn evaluate_field_name(55	context: Context,56	field_name: &jsonnet_parser::FieldName,57) -> Result<Option<Rc<str>>> {58	Ok(match field_name {59		jsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),60		jsonnet_parser::FieldName::Dyn(expr) => {61			let lazy = evaluate(context, expr)?;62			let value = lazy.unwrap_if_lazy()?;63			if matches!(value, Val::Null) {64				None65			} else {66				Some(value.try_cast_str("dynamic field name")?)67			}68		}69	})70}7172pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {73	Ok(match (op, b) {74		(o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,75		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),76		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),77		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),78		(op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),79	})80}8182pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {83	Ok(match (a, b) {84		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + &v2).into()),8586		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)87		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),88		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),8990		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_json(0)?).into()),91		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_json(0)?, s).into()),9293		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),94		(Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),95		(Val::Num(v1), Val::Num(v2)) => Val::Num(v1 + v2),96		_ => panic!("can't add: {:?} and {:?}", a, b),97	})98}99100pub fn evaluate_binary_op_special(101	context: Context,102	a: &LocExpr,103	op: BinaryOpType,104	b: &LocExpr,105) -> Result<Val> {106	Ok(107		match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {108			(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),109			(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),110			(a, op, eb) => {111				evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?112			}113		},114	)115}116117pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {118	Ok(match (a, op, b) {119		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,120121		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),122123		// Bool X Bool124		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),125		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),126127		// Str X Str128		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),129		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),130		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),131		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),132133		// Num X Num134		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),135		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {136			if *v2 <= f64::EPSILON {137				create_error(crate::Error::DivisionByZero)?138			}139			Val::Num(v1 / v2)140		}141142		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),143144		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),145		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),146		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),147		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),148149		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {150			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)151		}152		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {153			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)154		}155		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {156			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)157		}158		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {159			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)160		}161		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {162			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)163		}164165		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),166	})167}168169future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);170future_wrapper!(ObjValue, FutureObjValue);171172pub fn evaluate_comp<T>(173	context: Context,174	value: &impl Fn(Context) -> Result<T>,175	specs: &[CompSpec],176) -> Result<Option<Vec<T>>> {177	Ok(match specs.get(0) {178		None => Some(vec![value(context)?]),179		Some(CompSpec::IfSpec(IfSpecData(cond))) => {180			if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {181				evaluate_comp(context, value, &specs[1..])?182			} else {183				None184			}185		}186		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {187			match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {188				Val::Arr(list) => {189					let mut out = Vec::new();190					for item in list.iter() {191						let item = item.unwrap_if_lazy()?;192						out.push(evaluate_comp(193							context.with_var(var.clone(), item.clone())?,194							value,195							&specs[1..],196						)?);197					}198					Some(out.into_iter().flatten().flatten().collect())199				}200				_ => panic!("for expression evaluated to non-iterable value"),201			}202		}203	})204}205206pub fn evaluate_member_list_object(context: Context, members: &Vec<Member>) -> Result<ObjValue> {207	let new_bindings = FutureNewBindings::new();208	let future_this = FutureObjValue::new();209	let context_creator = context_creator!(210		closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {211			Ok(context.extend_unbound(212				new_bindings.clone().unwrap(),213				context.dollar().clone().or_else(||this.clone()),214				Some(this.unwrap()),215				super_obj216			)?)217		})218	);219	{220		let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();221		for (n, b) in members222			.iter()223			.filter_map(|m| match m {224				Member::BindStmt(b) => Some(b.clone()),225				_ => None,226			})227			.map(|b| evaluate_binding(&b, context_creator.clone()))228		{229			bindings.insert(n, b);230		}231		new_bindings.fill(bindings);232	}233234	let mut new_members = BTreeMap::new();235	for member in members.iter() {236		match member {237			Member::Field(FieldMember {238				name,239				plus,240				params: None,241				visibility,242				value,243			}) => {244				let name = evaluate_field_name(context.clone(), &name)?;245				if name.is_none() {246					continue;247				}248				let name = name.unwrap();249				new_members.insert(250					name.clone(),251					ObjMember {252						add: *plus,253						visibility: *visibility,254						invoke: LazyBinding::Bindable(Rc::new(255							closure!(clone name, clone value, clone context_creator, |this, super_obj| {256								Ok(LazyVal::new_resolved(push(&value.1, "object field", ||{257									let context = context_creator.0(this, super_obj)?;258									evaluate(259										context,260										&value,261									)262								})?))263							}),264						)),265					},266				);267			}268			Member::Field(FieldMember {269				name,270				params: Some(params),271				value,272				..273			}) => {274				let name = evaluate_field_name(context.clone(), &name)?;275				if name.is_none() {276					continue;277				}278				let name = name.unwrap();279				new_members.insert(280					name,281					ObjMember {282						add: false,283						visibility: Visibility::Hidden,284						invoke: LazyBinding::Bindable(Rc::new(285							closure!(clone value, clone context_creator, clone params, |this, super_obj| {286								// TODO: Assert287								Ok(LazyVal::new_resolved(evaluate_method(288									context_creator.0(this, super_obj)?,289									params.clone(),290									value.clone(),291								)))292							}),293						)),294					},295				);296			}297			Member::BindStmt(_) => {}298			Member::AssertStmt(_) => {}299		}300	}301	Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))302}303304pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {305	Ok(match object {306		ObjBody::MemberList(members) => evaluate_member_list_object(context, &members)?,307		ObjBody::ObjComp(obj) => {308			let future_this = FutureObjValue::new();309			let mut new_members = BTreeMap::new();310			for (k, v) in evaluate_comp(311				context.clone(),312				&|ctx| {313					let new_bindings = FutureNewBindings::new();314					let context_creator = context_creator!(315						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {316							Ok(context.extend_unbound(317								new_bindings.clone().unwrap(),318								context.dollar().clone().or_else(||this.clone()),319								None,320								super_obj321							)?)322						})323					);324					let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();325					for (n, b) in obj326						.pre_locals327						.iter()328						.chain(obj.post_locals.iter())329						.map(|b| evaluate_binding(b, context_creator.clone()))330					{331						bindings.insert(n, b);332					}333					let bindings = new_bindings.fill(bindings);334					let ctx = ctx.extend_unbound(bindings, None, None, None)?;335					let key = evaluate(ctx.clone(), &obj.key)?;336					let value = LazyBinding::Bindable(Rc::new(337						closure!(clone ctx, clone obj.value, |this, _super_obj| {338							Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))339						}),340					));341342					Ok((key, value))343				},344				&obj.compspecs,345			)?346			.unwrap()347			{348				match k {349					Val::Null => {}350					Val::Str(n) => {351						new_members.insert(352							n,353							ObjMember {354								add: false,355								visibility: Visibility::Normal,356								invoke: v,357							},358						);359					}360					v => create_error(Error::FieldMustBeStringGot(v.value_type()?))?,361				}362			}363364			future_this.fill(ObjValue::new(None, Rc::new(new_members)))365		}366	})367}368369pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {370	use Expr::*;371	let LocExpr(expr, loc) = expr;372	Ok(match &**expr {373		Literal(LiteralType::This) => Val::Obj(374			context375				.this()376				.clone()377				.unwrap_or_else(|| panic!("this not found")),378		),379		Literal(LiteralType::Dollar) => Val::Obj(380			context381				.dollar()382				.clone()383				.unwrap_or_else(|| panic!("dollar not found")),384		),385		Literal(LiteralType::True) => Val::Bool(true),386		Literal(LiteralType::False) => Val::Bool(false),387		Literal(LiteralType::Null) => Val::Null,388		Parened(e) => evaluate(context, e)?,389		Str(v) => Val::Str(v.clone()),390		Num(v) => Val::Num(*v),391		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,392		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,393		Var(name) => push(loc, "var", || {394			Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?)395		})?,396		Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {397			let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;398			context399				.super_obj()400				.clone()401				.expect("no super found")402				.get_raw(&name, &context.this().clone().expect("no this found"))?403				.expect("value not found")404		}405		Index(value, index) => {406			match (407				evaluate(context.clone(), value)?.unwrap_if_lazy()?,408				evaluate(context, index)?,409			) {410				(Val::Obj(v), Val::Str(s)) => {411					if let Some(v) = v.get(s.clone())? {412						v.unwrap_if_lazy()?413					} else if let Some(Val::Str(n)) = v.get("__intristic_namespace__".into())? {414						Val::Intristic(n, s)415					} else {416						create_error(crate::Error::NoSuchField(s))?417					}418				}419				(Val::Obj(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(420					ValType::Obj,421					ValType::Str,422					n.value_type()?,423				))?,424425				(Val::Arr(v), Val::Num(n)) => {426					if n.fract() > f64::EPSILON {427						create_error(crate::Error::FractionalIndex)?428					}429					v.get(n as usize)430						.unwrap_or_else(|| panic!("out of bounds"))431						.clone()432						.unwrap_if_lazy()?433				}434				(Val::Arr(_), Val::Str(n)) => {435					create_error(crate::Error::AttemptedIndexAnArrayWithString(n))?436				}437				(Val::Arr(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(438					ValType::Arr,439					ValType::Num,440					n.value_type()?,441				))?,442443				(Val::Str(s), Val::Num(n)) => Val::Str(444					s.chars()445						.skip(n as usize)446						.take(1)447						.collect::<String>()448						.into(),449				),450				(Val::Str(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(451					ValType::Str,452					ValType::Num,453					n.value_type()?,454				))?,455456				(v, _) => create_error(crate::Error::CantIndexInto(v.value_type()?))?,457			}458		}459		LocalExpr(bindings, returned) => {460			let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();461			let future_context = Context::new_future();462463			let context_creator = context_creator!(464				closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))465			);466467			for (k, v) in bindings468				.iter()469				.map(|b| evaluate_binding(b, context_creator.clone()))470			{471				new_bindings.insert(k, v);472			}473474			let context = context475				.extend_unbound(new_bindings, None, None, None)?476				.into_future(future_context);477			evaluate(context, &returned.clone())?478		}479		Arr(items) => {480			let mut out = Vec::with_capacity(items.len());481			for item in items {482				out.push(Val::Lazy(lazy_val!(483					closure!(clone context, clone item, || {484						evaluate(context.clone(), &item)485					})486				)));487			}488			Val::Arr(Rc::new(out))489		}490		ArrComp(expr, compspecs) => Val::Arr(491			// First compspec should be forspec, so no "None" possible here492			Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap()),493		),494		Obj(body) => Val::Obj(evaluate_object(context, body)?),495		ObjExtend(s, t) => evaluate_add_op(496			&evaluate(context.clone(), s)?,497			&Val::Obj(evaluate_object(context, t)?),498		)?,499		Apply(value, args, tailstrict) => {500			let lazy = evaluate(context.clone(), value)?;501			let value = lazy.unwrap_if_lazy()?;502			match value {503				Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {504					// arr/string/function505					("std", "length") => {506						assert_eq!(args.len(), 1);507						let expr = &args.get(0).unwrap().1;508						match evaluate(context, expr)? {509							Val::Str(n) => Val::Num(n.chars().count() as f64),510							Val::Arr(i) => Val::Num(i.len() as f64),511							Val::Obj(o) => Val::Num(512								o.fields_visibility()513									.into_iter()514									.filter(|(_k, v)| *v)515									.count() as f64,516							),517							v => panic!("can't get length of {:?}", v),518						}519					}520					// any521					("std", "type") => {522						assert_eq!(args.len(), 1);523						let expr = &args.get(0).unwrap().1;524						Val::Str(evaluate(context, expr)?.value_type()?.name().into())525					}526					// length, idx=>any527					("std", "makeArray") => {528						assert_eq!(args.len(), 2);529						if let (Val::Num(v), Val::Func(d)) = (530							evaluate(context.clone(), &args[0].1)?,531							evaluate(context, &args[1].1)?,532						) {533							assert!(v >= 0.0);534							let mut out = Vec::with_capacity(v as usize);535							for i in 0..v as usize {536								let call_ctx =537									Context::new().with_var("v".into(), Val::Num(i as f64))?;538								out.push(d.evaluate(539									call_ctx,540									&ArgsDesc(vec![Arg(None, el!(Expr::Var("v".into())))]),541									true,542								)?)543							}544							Val::Arr(Rc::new(out))545						} else {546							panic!("bad makeArray call");547						}548					}549					// string550					("std", "codepoint") => {551						assert_eq!(args.len(), 1);552						if let Val::Str(s) = evaluate(context, &args[0].1)? {553							assert!(554								s.chars().count() == 1,555								"std.codepoint should receive single char string"556							);557							Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)558						} else {559							panic!("bad codepoint call");560						}561					}562					// object, includeHidden563					("std", "objectFieldsEx") => {564						assert_eq!(args.len(), 2);565						if let (Val::Obj(body), Val::Bool(include_hidden)) = (566							evaluate(context.clone(), &args[0].1)?,567							evaluate(context, &args[1].1)?,568						) {569							Val::Arr(Rc::new(570								body.fields_visibility()571									.into_iter()572									.filter(|(_k, v)| *v || include_hidden)573									.map(|(k, _v)| Val::Str(k))574									.collect(),575							))576						} else {577							panic!("bad objectFieldsEx call");578						}579					}580					// object, field, includeHidden581					("std", "objectHasEx") => {582						assert_eq!(args.len(), 3);583						if let (Val::Obj(body), Val::Str(name), Val::Bool(include_hidden)) = (584							evaluate(context.clone(), &args[0].1)?,585							evaluate(context.clone(), &args[1].1)?,586							evaluate(context, &args[2].1)?,587						) {588							Val::Bool(589								body.fields_visibility()590									.into_iter()591									.filter(|(_k, v)| *v || include_hidden)592									.any(|(k, _v)| *k == *name),593							)594						} else {595							panic!("bad objectHasEx call");596						}597					}598					("std", "primitiveEquals") => {599						assert_eq!(args.len(), 2);600						let (a, b) = (601							evaluate(context.clone(), &args[0].1)?,602							evaluate(context, &args[1].1)?,603						);604						Val::Bool(a == b)605					}606					("std", "modulo") => {607						assert_eq!(args.len(), 2);608						if let (Val::Num(a), Val::Num(b)) = (609							evaluate(context.clone(), &args[0].1)?,610							evaluate(context, &args[1].1)?,611						) {612							Val::Num(a % b)613						} else {614							panic!("bad modulo call");615						}616					}617					("std", "floor") => {618						assert_eq!(args.len(), 1);619						if let Val::Num(a) = evaluate(context, &args[0].1)? {620							Val::Num(a.floor())621						} else {622							panic!("bad floor call");623						}624					}625					("std", "trace") => {626						assert_eq!(args.len(), 2);627						if let (Val::Str(a), b) = (628							evaluate(context.clone(), &args[0].1)?,629							evaluate(context, &args[1].1)?,630						) {631							// TODO: Line numbers as in original jsonnet632							println!("TRACE: {}", a);633							b634						} else {635							panic!("bad trace call");636						}637					}638					("std", "pow") => {639						assert_eq!(args.len(), 2);640						if let (Val::Num(a), Val::Num(b)) = (641							evaluate(context.clone(), &args[0].1)?,642							evaluate(context, &args[1].1)?,643						) {644							Val::Num(a.powf(b))645						} else {646							panic!("bad pow call");647						}648					}649					("std", "extVar") => {650						assert_eq!(args.len(), 1);651						if let Val::Str(a) = evaluate(context, &args[0].1)? {652							with_state(|s| s.0.ext_vars.borrow().get(&a).cloned()).ok_or_else(653								|| {654									create_error::<()>(crate::Error::UndefinedExternalVariable(a))655										.err()656										.unwrap()657								},658							)?659						} else {660							panic!("bad extVar call");661						}662					}663					("std", "filter") => {664						assert_eq!(args.len(), 2);665						if let (Val::Func(predicate), Val::Arr(arr)) = (666							evaluate(context.clone(), &args[0].1)?,667							evaluate(context.clone(), &args[1].1)?,668						) {669							Val::Arr(Rc::new(670								arr.iter()671									.filter(|e| {672										predicate673											.evaluate_values(context.clone(), &[e.clone()])674											.unwrap()675											.try_cast_bool("filter predicate")676											.unwrap()677									})678									.cloned()679									.collect(),680							))681						} else {682							panic!("bad filter call");683						}684					}685					// faster686					("std", "join") => {687						assert_eq!(args.len(), 2);688						let joiner = evaluate(context.clone(), &args[0].1)?;689						let items = evaluate(context, &args[1].1)?;690						match (joiner.unwrap_if_lazy()?, items.unwrap_if_lazy()?) {691							(Val::Arr(joiner_items), Val::Arr(items)) => {692								// TODO: Minimal size should be known693								let mut out = Vec::new();694695								let mut first = true;696								for item in items.iter().cloned() {697									if let Val::Arr(items) = item.unwrap_if_lazy()? {698										if !first {699											out.reserve(joiner_items.len());700											out.extend(joiner_items.iter().cloned());701										}702										first = false;703										out.reserve(items.len());704										out.extend(items.iter().cloned());705									} else {706										panic!("all array items should be arrays")707									}708								}709710								Val::Arr(Rc::new(out))711							}712							(Val::Str(joiner), Val::Arr(items)) => {713								let mut out = String::new();714715								let mut first = true;716								for item in items.iter().cloned() {717									if let Val::Str(item) = item.unwrap_if_lazy()? {718										if !first {719											out += &joiner;720										}721										first = false;722										out += &item;723									} else {724										panic!("all array items should be strings")725									}726								}727728								Val::Str(out.into())729							}730							(joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items),731						}732					}733					(ns, name) => panic!("Intristic not found: {}.{}", ns, name),734				},735				Val::Func(f) => {736					let body = || f.evaluate(context, args, *tailstrict);737					if *tailstrict {738						body()?739					} else {740						push(loc, "function call", body)?741					}742				}743				_ => panic!("{:?} is not a function", value),744			}745		}746		Function(params, body) => evaluate_method(context, params.clone(), body.clone()),747		AssertExpr(AssertStmt(value, msg), returned) => {748			let assertion_result = push(&value.1, "assertion condition", || {749				evaluate(context.clone(), &value)?750					.try_cast_bool("assertion condition should be boolean")751			})?;752			if assertion_result {753				evaluate(context, returned)?754			} else if let Some(msg) = msg {755				panic!(756					"assertion failed ({:?}): {}",757					value,758					evaluate(context, msg)?.try_cast_str("assertion message should be string")?759				);760			} else {761				panic!("assertion failed ({:?}): no message", value);762			}763		}764		Error(e) => create_error(crate::Error::RuntimeError(765			evaluate(context, e)?.try_cast_str("error text should be string")?,766		))?,767		IfElse {768			cond,769			cond_then,770			cond_else,771		} => {772			if evaluate(context.clone(), &cond.0)?773				.try_cast_bool("if condition should be boolean")?774			{775				evaluate(context, cond_then)?776			} else {777				match cond_else {778					Some(v) => evaluate(context, v)?,779					None => Val::Null,780				}781			}782		}783		Import(path) => {784			let mut tmp = loc785				.clone()786				.expect("imports can't be used without loc_data")787				.0;788			let import_location = Rc::make_mut(&mut tmp);789			import_location.pop();790			with_state(|s| s.import_file(&import_location, path))?791		}792		ImportStr(path) => {793			let mut tmp = loc794				.clone()795				.expect("imports can't be used without loc_data")796				.0;797			let import_location = Rc::make_mut(&mut tmp);798			import_location.pop();799			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)800		}801		Literal(LiteralType::Super) => return create_error(crate::error::Error::StandaloneSuper),802	})803}
modifiedcrates/jsonnet-evaluator/src/function.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/function.rs
+++ b/crates/jsonnet-evaluator/src/function.rs
@@ -35,7 +35,7 @@
 	let mut positioned_args = vec![None; params.0.len()];
 	for (id, arg) in args.iter().enumerate() {
 		let idx = if let Some(name) = &arg.0 {
-			params.iter().position(|p| &p.0 == name).ok_or_else(|| {
+			params.iter().position(|p| *p.0 == *name).ok_or_else(|| {
 				create_error::<()>(Error::UnknownFunctionParameter(name.clone()))
 					.err()
 					.unwrap()
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -65,11 +65,11 @@
 	/// Contains file source codes and evaluated results for imports and pretty
 	/// printing stacktraces
 	files: RefCell<HashMap<PathBuf, FileData>>,
-	str_files: RefCell<HashMap<PathBuf, String>>,
-	globals: RefCell<HashMap<String, Val>>,
+	str_files: RefCell<HashMap<PathBuf, Rc<str>>>,
+	globals: RefCell<HashMap<Rc<str>, Val>>,
 
 	/// Values to use with std.extVar
-	ext_vars: RefCell<HashMap<String, Val>>,
+	ext_vars: RefCell<HashMap<Rc<str>, Val>>,
 
 	settings: EvaluationSettings,
 	import_resolver: Box<dyn ImportResolver>,
@@ -87,7 +87,7 @@
 	with_state(|s| s.error(err))
 }
 pub(crate) fn push<T>(
-	e: &Option<Rc<ExprLocation>>,
+	e: &Option<ExprLocation>,
 	comment: &str,
 	f: impl FnOnce() -> Result<T>,
 ) -> Result<T> {
@@ -185,11 +185,14 @@
 		})?;
 		self.evaluate_file(&file_path)
 	}
-	pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<String> {
+	pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {
 		let path = self.0.import_resolver.resolve_file(from, path)?;
 		if !self.0.str_files.borrow().contains_key(&path) {
 			let file_str = self.0.import_resolver.load_file_contents(&path)?;
-			self.0.str_files.borrow_mut().insert(path.clone(), file_str);
+			self.0
+				.str_files
+				.borrow_mut()
+				.insert(path.clone(), file_str.into());
 		}
 		Ok(self.0.str_files.borrow().get(&path).cloned().unwrap())
 	}
@@ -210,10 +213,10 @@
 		self.run_in_state(|| evaluate(self.create_default_context()?, &code))
 	}
 
-	pub fn add_global(&self, name: String, value: Val) {
+	pub fn add_global(&self, name: Rc<str>, value: Val) {
 		self.0.globals.borrow_mut().insert(name, value);
 	}
-	pub fn add_ext_var(&self, name: String, value: Val) {
+	pub fn add_ext_var(&self, name: Rc<str>, value: Val) {
 		self.0.ext_vars.borrow_mut().insert(name, value);
 	}
 
@@ -236,14 +239,14 @@
 					.unwrap();
 			}
 			let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();
-			self.add_global("std".to_owned(), val);
+			self.add_global("std".into(), val);
 		});
 		self
 	}
 
 	pub fn create_default_context(&self) -> Result<Context> {
 		let globals = self.0.globals.borrow();
-		let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
+		let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
 		for (name, value) in globals.iter() {
 			new_bindings.insert(
 				name.clone(),
@@ -255,7 +258,7 @@
 
 	pub fn push<T>(
 		&self,
-		e: Rc<ExprLocation>,
+		e: ExprLocation,
 		comment: String,
 		f: impl FnOnce() -> Result<T>,
 	) -> Result<T> {
@@ -362,7 +365,7 @@
 			let evaluator = EvaluationState::default();
 			evaluator.with_stdlib();
 			let val = evaluator.parse_evaluate_raw($str).unwrap();
-			evaluator.add_global("__tmp__to_yaml__".to_owned(), val);
+			evaluator.add_global("__tmp__to_yaml__".into(), val);
 			evaluator
 				.parse_evaluate_raw("std.manifestJsonEx(__tmp__to_yaml__, \"\")")
 				.unwrap()
modifiedcrates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -18,9 +18,10 @@
 #[derive(Debug)]
 pub struct ObjValueInternals {
 	super_obj: Option<ObjValue>,
-	this_entries: Rc<BTreeMap<String, ObjMember>>,
-	value_cache: RefCell<HashMap<String, Val>>,
+	this_entries: Rc<BTreeMap<Rc<str>, ObjMember>>,
+	value_cache: RefCell<HashMap<Rc<str>, Val>>,
 }
+#[derive(Clone)]
 pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
 impl Debug for ObjValue {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -43,7 +44,7 @@
 impl ObjValue {
 	pub fn new(
 		super_obj: Option<ObjValue>,
-		this_entries: Rc<BTreeMap<String, ObjMember>>,
+		this_entries: Rc<BTreeMap<Rc<str>, ObjMember>>,
 	) -> ObjValue {
 		ObjValue(Rc::new(ObjValueInternals {
 			super_obj,
@@ -57,7 +58,7 @@
 			Some(v) => ObjValue::new(Some(v.with_super(super_obj)), self.0.this_entries.clone()),
 		}
 	}
-	pub fn enum_fields(&self, handler: &impl Fn(&str, &Visibility)) {
+	pub fn enum_fields(&self, handler: &impl Fn(&Rc<str>, &Visibility)) {
 		if let Some(s) = &self.0.super_obj {
 			s.enum_fields(handler);
 		}
@@ -65,7 +66,7 @@
 			handler(&name, &member.visibility);
 		}
 	}
-	pub fn fields_visibility(&self) -> IndexMap<String, bool> {
+	pub fn fields_visibility(&self) -> IndexMap<Rc<str>, bool> {
 		let out = Rc::new(RefCell::new(IndexMap::new()));
 		self.enum_fields(&|name, visibility| {
 			let mut out = out.borrow_mut();
@@ -85,16 +86,20 @@
 		});
 		Rc::try_unwrap(out).unwrap().into_inner()
 	}
-	pub fn get(&self, key: &str) -> Result<Option<Val>> {
-		if let Some(v) = self.0.value_cache.borrow().get(key) {
+	pub fn visible_fields(&self) -> Vec<Rc<str>> {
+		self.fields_visibility()
+			.into_iter()
+			.filter(|(_k, v)| *v)
+			.map(|(k, _)| k)
+			.collect()
+	}
+	pub fn get(&self, key: Rc<str>) -> Result<Option<Val>> {
+		if let Some(v) = self.0.value_cache.borrow().get(&key) {
 			return Ok(Some(v.clone()));
 		}
-		if let Some(v) = self.get_raw(key, self)? {
+		if let Some(v) = self.get_raw(&key, self)? {
 			let v = v.unwrap_if_lazy()?;
-			self.0
-				.value_cache
-				.borrow_mut()
-				.insert(key.to_owned(), v.clone());
+			self.0.value_cache.borrow_mut().insert(key, v.clone());
 			Ok(Some(v))
 		} else {
 			Ok(None)
@@ -108,10 +113,10 @@
 					.evaluate()?,
 			)),
 			(Some(k), Some(s)) => {
-				let our = k
+				let lazy = k
 					.invoke
-					.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?
-					.evaluate()?;
+					.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?;
+				let our = lazy.evaluate()?;
 				if k.add {
 					s.get_raw(key, real_this)?
 						.map_or(Ok(Some(our.clone())), |v| {
@@ -124,11 +129,6 @@
 			(None, Some(s)) => s.get_raw(key, real_this),
 			(None, None) => Ok(None),
 		}
-	}
-}
-impl Clone for ObjValue {
-	fn clone(&self) -> Self {
-		ObjValue(self.0.clone())
 	}
 }
 impl PartialEq for ObjValue {
modifiedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -81,7 +81,7 @@
 	}
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Copy, PartialEq)]
 pub enum ValType {
 	Bool,
 	Null,
@@ -115,42 +115,46 @@
 pub enum Val {
 	Bool(bool),
 	Null,
-	Str(String),
+	Str(Rc<str>),
 	Num(f64),
 	Lazy(LazyVal),
-	Arr(Vec<Val>),
+	Arr(Rc<Vec<Val>>),
 	Obj(ObjValue),
 	Func(FuncDesc),
 
 	// Library functions implemented in native
-	Intristic(String, String),
+	Intristic(Rc<str>, Rc<str>),
+}
+macro_rules! matches_unwrap {
+	($e: expr, $p: pat, $r: expr) => {
+		match $e {
+			$p => $r,
+			_ => panic!("no match"),
+			}
+	};
 }
 impl Val {
+	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {
+		let this_type = self.value_type()?;
+		if this_type != val_type {
+			create_error(Error::TypeMismatch(context, vec![val_type], this_type))
+		} else {
+			Ok(())
+		}
+	}
 	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
-		match self.unwrap_if_lazy()? {
-			Val::Bool(v) => Ok(v),
-			v => create_error(Error::TypeMismatch(
-				context,
-				vec![ValType::Bool],
-				v.value_type()?,
-			)),
-		}
+		self.assert_type(context, ValType::Bool)?;
+		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))
 	}
-	pub fn try_cast_str(self, context: &'static str) -> Result<String> {
-		match self.unwrap_if_lazy()? {
-			Val::Str(v) => Ok(v),
-			v => create_error(Error::TypeMismatch(
-				context,
-				vec![ValType::Str],
-				v.value_type()?,
-			)),
-		}
+	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {
+		self.assert_type(context, ValType::Str)?;
+		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))
 	}
-	pub fn unwrap_if_lazy(self) -> Result<Self> {
+	pub fn unwrap_if_lazy(&self) -> Result<Self> {
 		Ok(if let Val::Lazy(v) = self {
 			v.evaluate()?.unwrap_if_lazy()?
 		} else {
-			self
+			self.clone()
 		})
 	}
 	pub fn value_type(&self) -> Result<ValType> {
@@ -166,26 +170,30 @@
 			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,
 		})
 	}
-	pub fn into_json(self, padding: usize) -> Result<String> {
+	pub fn into_json(self, padding: usize) -> Result<Rc<str>> {
 		with_state(|s| {
 			let ctx = s
 				.create_default_context()?
-				.with_var("__tmp__to_json__".to_owned(), self)?;
-			if let Val::Str(result) = evaluate(
+				.with_var("__tmp__to_json__".into(), self)?;
+			Ok(evaluate(
 				ctx,
 				&el!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("manifestJsonEx".to_owned()))
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("manifestJsonEx".into()))
 					)),
 					ArgsDesc(vec![
-						Arg(None, el!(Expr::Var("__tmp__to_json__".to_owned()))),
-						Arg(None, el!(Expr::Str(" ".repeat(padding))))
+						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),
+						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))
 					]),
 					false
 				)),
-			)? {
-				Ok(result)
+			)?
+			.try_cast_str("to json")?)
+		})
+	}
+}
+
 			} else {
 				unreachable!()
 			}
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -1,15 +1,15 @@
 use serde::{Deserialize, Serialize};
 use std::{fmt::Debug, ops::Deref, path::PathBuf, rc::Rc};
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub enum FieldName {
 	/// {fixed: 2}
-	Fixed(String),
+	Fixed(Rc<str>),
 	/// {["dyn"+"amic"]: 3}
 	Dyn(LocExpr),
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
 pub enum Visibility {
 	/// :
 	Normal,
@@ -19,10 +19,10 @@
 	Unhide,
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct FieldMember {
 	pub name: FieldName,
 	pub plus: bool,
@@ -31,7 +31,7 @@
 	pub value: LocExpr,
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub enum Member {
 	Field(FieldMember),
 	BindStmt(BindSpec),
@@ -71,11 +71,11 @@
 }
 
 /// name, default value
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct Param(pub String, pub Option<LocExpr>);
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct Param(pub Rc<str>, pub Option<LocExpr>);
 /// Defined function parameters
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct ParamsDesc(pub Vec<Param>);
+pub struct ParamsDesc(pub Rc<Vec<Param>>);
 impl Deref for ParamsDesc {
 	type Target = Vec<Param>;
 	fn deref(&self) -> &Self::Target {
@@ -83,9 +83,9 @@
 	}
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct Arg(pub Option<String>, pub LocExpr);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct ArgsDesc(pub Vec<Arg>);
 impl Deref for ArgsDesc {
 	type Target = Vec<Arg>;
@@ -96,35 +96,38 @@
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct BindSpec {
-	pub name: String,
+	pub name: Rc<str>,
 	pub params: Option<ParamsDesc>,
 	pub value: LocExpr,
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct IfSpecData(pub LocExpr);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct ForSpecData(pub String, pub LocExpr);
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ForSpecData(pub Rc<str>, pub LocExpr);
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub enum CompSpec {
 	IfSpec(IfSpecData),
 	ForSpec(ForSpecData),
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ObjComp {
+	pub pre_locals: Vec<BindSpec>,
+	pub key: LocExpr,
+	pub value: LocExpr,
+	pub post_locals: Vec<BindSpec>,
+	pub compspecs: Vec<CompSpec>,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub enum ObjBody {
 	MemberList(Vec<Member>),
-	ObjComp {
-		pre_locals: Vec<BindSpec>,
-		key: LocExpr,
-		value: LocExpr,
-		post_locals: Vec<BindSpec>,
-		compspecs: Vec<CompSpec>,
-	},
+	ObjComp(ObjComp),
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
 pub enum LiteralType {
 	This,
 	Super,
@@ -134,7 +137,7 @@
 	False,
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct SliceDesc {
 	pub start: Option<LocExpr>,
 	pub end: Option<LocExpr>,
@@ -142,16 +145,16 @@
 }
 
 /// Syntax base
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub enum Expr {
 	Literal(LiteralType),
 
 	/// String value: "hello"
-	Str(String),
+	Str(Rc<str>),
 	/// Number: 1, 2.0, 2e+20
 	Num(f64),
 	/// Variable name: test
-	Var(String),
+	Var(Rc<str>),
 
 	/// Array of expressions: [1, 2, "Hello"]
 	Arr(Vec<LocExpr>),
modifiedcrates/jsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -39,7 +39,7 @@
 
 		/// Reserved word followed by any non-alphanumberic
 		rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
-		rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")
+		rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")
 
 		rule keyword(id: &'static str)
 			= ##parse_string_literal(id) end_of_ident()
@@ -47,7 +47,7 @@
 		rule l(s: &ParserSettings, x: rule<Expr>) -> LocExpr
 			= start:position!() v:x() end:position!() {loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end))}
 
-		pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
+		pub rule param(s: &ParserSettings) -> expr::Param = name:$(id()) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name.into(), expr) }
 		pub rule params(s: &ParserSettings) -> expr::ParamsDesc
 			= params:(param(s) ** comma()) {
 				let mut defaults_started = false;
@@ -55,12 +55,12 @@
 					defaults_started = defaults_started || param.1.is_some();
 					assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");
 				}
-				expr::ParamsDesc(params)
+				expr::ParamsDesc(Rc::new(params))
 			}
-			/ { expr::ParamsDesc(Vec::new()) }
+			/ { expr::ParamsDesc(Rc::new(Vec::new())) }
 
 		pub rule arg(s: &ParserSettings) -> expr::Arg
-			= name:id() _ "=" _ expr:expr(s) {expr::Arg(Some(name), expr)}
+			= name:$(id()) _ "=" _ expr:expr(s) {expr::Arg(Some(name.into()), expr)}
 			/ expr:expr(s) {expr::Arg(None, expr)}
 		pub rule args(s: &ParserSettings) -> expr::ArgsDesc
 			= args:arg(s) ** comma() comma()? {
@@ -74,8 +74,8 @@
 			/ { expr::ArgsDesc(Vec::new()) }
 
 		pub rule bind(s: &ParserSettings) -> expr::BindSpec
-			= name:id() _ "=" _ expr:expr(s) {expr::BindSpec{name, params: None, value: expr}}
-			/ name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name, params: Some(params), value: expr}}
+			= name:$(id()) _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: None, value: expr}}
+			/ name:$(id()) _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: Some(params), value: expr}}
 		pub rule assertion(s: &ParserSettings) -> expr::AssertStmt
 			= keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }
 
@@ -95,8 +95,8 @@
 			/ string_block()
 
 		pub rule field_name(s: &ParserSettings) -> expr::FieldName
-			= name:id() {expr::FieldName::Fixed(name)}
-			/ name:string() {expr::FieldName::Fixed(name)}
+			= name:$(id()) {expr::FieldName::Fixed(name.into())}
+			/ name:string() {expr::FieldName::Fixed(name.into())}
 			/ "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}
 		pub rule visibility() -> expr::Visibility
 			= ":::" {expr::Visibility::Unhide}
@@ -125,35 +125,41 @@
 			/ field:field(s) {expr::Member::Field(field)}
 		pub rule objinside(s: &ParserSettings) -> expr::ObjBody
 			= pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {
-				expr::ObjBody::ObjComp {
+				let mut compspecs = vec![CompSpec::ForSpec(forspec)];
+				compspecs.extend(others.unwrap_or_default());
+				expr::ObjBody::ObjComp(expr::ObjComp{
 					pre_locals,
 					key,
 					value,
 					post_locals,
-					compspecs: [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat(),
-				}
+					compspecs,
+				})
 			}
 			/ members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}
 		pub rule ifspec(s: &ParserSettings) -> IfSpecData
 			= keyword("if") _ expr:expr(s) {IfSpecData(expr)}
 		pub rule forspec(s: &ParserSettings) -> ForSpecData
-			= keyword("for") _ id:id() _ keyword("in") _ cond:expr(s) {ForSpecData(id, cond)}
+			= keyword("for") _ id:$(id()) _ keyword("in") _ cond:expr(s) {ForSpecData(id.into(), cond)}
 		pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>
 			= s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}
 		pub rule local_expr(s: &ParserSettings) -> LocExpr
 			= l(s,<keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }>)
 		pub rule string_expr(s: &ParserSettings) -> LocExpr
-			= l(s, <s:string() {Expr::Str(s)}>)
+			= l(s, <s:string() {Expr::Str(s.into())}>)
 		pub rule obj_expr(s: &ParserSettings) -> LocExpr
 			= l(s,<"{" _ body:objinside(s) _ "}" {Expr::Obj(body)}>)
 		pub rule array_expr(s: &ParserSettings) -> LocExpr
 			= l(s,<"[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}>)
 		pub rule array_comp_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {Expr::ArrComp(expr, [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat())}>)
+			= l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {
+				let mut specs = vec![CompSpec::ForSpec(forspec)];
+				specs.extend(others.unwrap_or_default());
+				Expr::ArrComp(expr, specs)
+			}>)
 		pub rule number_expr(s: &ParserSettings) -> LocExpr
 			= l(s,<n:number() { expr::Expr::Num(n) }>)
 		pub rule var_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<n:id() { expr::Expr::Var(n) }>)
+			= l(s,<n:$(id()) { expr::Expr::Var(n.into()) }>)
 		pub rule if_then_else_expr(s: &ParserSettings) -> LocExpr
 			= l(s,<cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
 				cond,
@@ -219,16 +225,16 @@
 				--
 				a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("equals".to_owned()))
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("equals".into()))
 					)),
 					ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					true
 				))}
 				a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("equals".to_owned()))
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("equals".into()))
 					)),
 					ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					true
@@ -240,8 +246,8 @@
 				a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}
 				a:(@) _ keyword("in") _ b:@ {loc_expr_todo!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("objectHasEx".to_owned()))
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("objectHasEx".into()))
 					)), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),
 					true
 				))}
@@ -256,8 +262,8 @@
 				a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}
 				a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("mod".to_owned()))
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("mod".into()))
 					)), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					true
 				))}
@@ -268,8 +274,8 @@
 				--
 				a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(
 					el!(Expr::Index(
-						el!(Expr::Var("std".to_owned())),
-						el!(Expr::Str("slice".to_owned())),
+						el!(Expr::Var("std".into())),
+						el!(Expr::Str("slice".into())),
 					)),
 					ArgsDesc(vec![
 						Arg(None, a),
@@ -279,7 +285,7 @@
 					]),
 					true,
 				))}
-				a:(@) _ "." _ s:id() {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s))))}
+				a:(@) _ "." _ s:$(id()) {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s.into()))))}
 				a:(@) _ "[" _ s:expr(s) _ "]" {loc_expr_todo!(Expr::Index(a, s))}
 				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {loc_expr_todo!(Expr::Apply(a, args, ts.is_some()))}
 				a:(@) _ "{" _ body:objinside(s) _ "}" {loc_expr_todo!(Expr::ObjExtend(a, body))}
@@ -352,7 +358,7 @@
 	fn multiline_string() {
 		assert_eq!(
 			parse!("|||\n    Hello world!\n     a\n|||"),
-			el!(Expr::Str("Hello world!\n a\n".to_owned())),
+			el!(Expr::Str("Hello world!\n a\n".into())),
 		)
 	}
 
@@ -369,20 +375,20 @@
 	fn string_escaping() {
 		assert_eq!(
 			parse!(r#""Hello, \"world\"!""#),
-			el!(Expr::Str(r#"Hello, "world"!"#.to_owned())),
+			el!(Expr::Str(r#"Hello, "world"!"#.into())),
 		);
 		assert_eq!(
 			parse!(r#"'Hello \'world\'!'"#),
-			el!(Expr::Str("Hello 'world'!".to_owned())),
+			el!(Expr::Str("Hello 'world'!".into())),
 		);
-		assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".to_owned())),);
+		assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);
 	}
 
 	#[test]
 	fn string_unescaping() {
 		assert_eq!(
 			parse!(r#""Hello\nWorld""#),
-			el!(Expr::Str("Hello\nWorld".to_owned())),
+			el!(Expr::Str("Hello\nWorld".into())),
 		);
 	}
 
@@ -390,7 +396,7 @@
 	fn string_verbantim() {
 		assert_eq!(
 			parse!(r#"@"Hello\n""World""""#),
-			el!(Expr::Str("Hello\\n\"World\"".to_owned())),
+			el!(Expr::Str("Hello\\n\"World\"".into())),
 		);
 	}
 
@@ -489,16 +495,13 @@
 			parse!("[std.deepJoin(x) for x in arr]"),
 			el!(ArrComp(
 				el!(Apply(
-					el!(Index(
-						el!(Var("std".to_owned())),
-						el!(Str("deepJoin".to_owned()))
-					)),
-					ArgsDesc(vec![Arg(None, el!(Var("x".to_owned())))]),
+					el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),
+					ArgsDesc(vec![Arg(None, el!(Var("x".into())))]),
 					false,
 				)),
 				vec![CompSpec::ForSpec(ForSpecData(
-					"x".to_owned(),
-					el!(Var("arr".to_owned()))
+					"x".into(),
+					el!(Var("arr".into()))
 				))]
 			)),
 		)
@@ -508,7 +511,7 @@
 	fn reserved() {
 		use Expr::*;
 		assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));
-		assert_eq!(parse!("nulla"), el!(Var("nulla".to_owned())));
+		assert_eq!(parse!("nulla"), el!(Var("nulla".into())));
 	}
 
 	#[test]
@@ -522,9 +525,9 @@
 		assert_eq!(
 			parse!("!a && !b"),
 			el!(BinaryOp(
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),
+				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
 				BinaryOpType::And,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))
+				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
 			))
 		);
 	}
@@ -535,9 +538,9 @@
 		assert_eq!(
 			parse!("!a / !b"),
 			el!(BinaryOp(
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),
+				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
 				BinaryOpType::Div,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))
+				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
 			))
 		);
 	}
@@ -549,7 +552,7 @@
 			parse!("!!a"),
 			el!(UnaryOp(
 				UnaryOpType::Not,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned()))))
+				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))
 			))
 		)
 	}