git.delta.rocks / jrsonnet / refs/commits / 1450eba56177

difftreelog

feat(evaluator) stacktrace preparation

Лач2020-06-01parent: #b25a25b.patch.diff
in: master

3 files changed

modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -67,37 +67,25 @@
 
 pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {
 	match (op, b) {
-		(o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),
-		(UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),
-		(UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),
+		(o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()),
+		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),
 		(op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),
 	}
 }
 
 pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {
 	match (a, op, b) {
-		(Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),
-		(a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),
+		(Val::Lazy(a), o, b) => evaluate_binary_op(&a.evaluate(), o, b),
+		(a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.evaluate()),
 
 		(Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
-		(Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),
 		(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),
 
 		(Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
 		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
 
-		(Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
-			bool_val(false)
-		}
-		(Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
-			bool_val(false)
-		}
-		(Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
-			bool_val(false)
-		}
-		(Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
-			bool_val(true)
-		}
+		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
+		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
 
 		(Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
 
@@ -134,6 +122,8 @@
 		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {
 			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)
 		}
+		(a, BinaryOpType::Eq, b) => bool_val(a == b),
+		(a, BinaryOpType::Ne, b) => bool_val(a != b),
 		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
 	}
 }
@@ -238,7 +228,7 @@
 
 pub fn evaluate(context: Context, expr: &LocExpr) -> Val {
 	use Expr::*;
-	let LocExpr(expr, _location) = expr;
+	let LocExpr(expr, loc) = expr;
 	match &**expr {
 		Literal(LiteralType::This) => Val::Obj(
 			context
@@ -252,7 +242,9 @@
 				.clone()
 				.unwrap_or_else(|| panic!("super not found")),
 		),
-		Literal(t) => Val::Literal(t.clone()),
+		Literal(LiteralType::True) => Val::Bool(true),
+		Literal(LiteralType::False) => Val::Bool(false),
+		Literal(LiteralType::Null) => Val::Null,
 		Parened(e) => evaluate(context, e),
 		Str(v) => Val::Str(v.clone()),
 		Num(v) => Val::Num(*v),
@@ -383,13 +375,16 @@
 			cond_then,
 			cond_else,
 		} => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {
-			Val::Literal(LiteralType::True) => evaluate(context, cond_then),
-			Val::Literal(LiteralType::False) => match cond_else {
+			Val::Bool(true) => evaluate(context, cond_then),
+			Val::Bool(false) => match cond_else {
 				Some(v) => evaluate(context, v),
-				None => Val::Literal(LiteralType::False),
+				None => Val::Bool(false),
 			},
 			v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),
 		},
-		_ => panic!("evaluation not implemented: {:?}", expr),
+		_ => panic!(
+			"evaluation not implemented: {:?}",
+			LocExpr(expr.clone(), loc.clone())
+		),
 	}
 }
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
before · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19	Binding,20	binding,21	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(24	LazyBinding,25	lazy_binding,26	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);29rc_fn_helper!(30	FunctionDefault,31	function_default,32	dyn Fn(Context, LocExpr) -> Val33);3435#[cfg(test)]36pub mod tests {37	use super::{evaluate, Context, Val};38	use jsonnet_parser::*;3940	macro_rules! eval {41		($str: expr) => {42			evaluate(Context::new(), &parse($str, &ParserSettings {43				loc_data: false,44				file_name: "test.jsonnet".to_owned(),45			}).unwrap())46		};47	}4849	macro_rules! eval_stdlib {50		($str: expr) => {{51			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";52			evaluate(Context::new(), &parse(&(std + $str), &ParserSettings {53				loc_data: false,54				file_name: "test.jsonnet".to_owned(),55			}).unwrap())56			}};57	}5859	macro_rules! assert_eval {60		($str: expr) => {61			assert_eq!(62				evaluate(Context::new(), &parse($str, &ParserSettings {63					loc_data: false,64					file_name: "test.jsonnet".to_owned(),65				}).unwrap()),66				Val::Literal(LiteralType::True)67				)68		};69	}70	macro_rules! assert_json {71		($str: expr, $out: expr) => {72			assert_eq!(73				format!("{}", evaluate(Context::new(), &parse($str, &ParserSettings {74					loc_data: false,75					file_name: "test.jsonnet".to_owned(),76				}).unwrap())),77				$out78				)79		};80	}81	macro_rules! assert_json_stdlib {82		($str: expr, $out: expr) => {83			assert_eq!(format!("{}", eval_stdlib!($str)), $out)84		};85	}86	macro_rules! assert_eval_neg {87		($str: expr) => {88			assert_eq!(89				evaluate(Context::new(), &parse($str, &ParserSettings {90					loc_data: false,91					file_name: "test.jsonnet".to_owned(),92				}).unwrap()),93				Val::Literal(LiteralType::False)94				)95		};96	}9798	/// Sanity checking, before trusting to another tests99	#[test]100	fn equality_operator() {101		assert_eval!("2 == 2");102		assert_eval_neg!("2 != 2");103		assert_eval!("2 != 3");104		assert_eval_neg!("2 == 3");105		assert_eval!("'Hello' == 'Hello'");106		assert_eval_neg!("'Hello' != 'Hello'");107		assert_eval!("'Hello' != 'World'");108		assert_eval_neg!("'Hello' == 'World'");109	}110111	#[test]112	fn math_evaluation() {113		assert_eval!("2 + 2 * 2 == 6");114		assert_eval!("3 + (2 + 2 * 2) == 9");115	}116117	#[test]118	fn string_concat() {119		assert_eval!("'Hello' + 'World' == 'HelloWorld'");120		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");121		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");122	}123124	#[test]125	fn local() {126		assert_eval!("local a = 2; local b = 3; a + b == 5");127		assert_eval!("local a = 1, b = a + 1; a + b == 3");128		assert_eval!("local a = 1; local a = 2; a == 2");129	}130131	#[test]132	fn object_lazyness() {133		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);134	}135136	#[test]137	fn object_inheritance() {138		assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);139	}140141	#[test]142	fn test_object() {143		assert_json!("{a:2}", r#"{"a":2}"#);144		assert_json!("{a:2+2}", r#"{"a":4}"#);145		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);146		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);147		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);148		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);149		assert_json!(150			r#"151				{152					name: "Alice",153					welcome: "Hello " + self.name + "!",154				}155			"#,156			r#"{"name":"Alice","welcome":"Hello Alice!"}"#157		);158		assert_json!(159			r#"160				{161					name: "Alice",162					welcome: "Hello " + self.name + "!",163				} + {164					name: "Bob"165				}166			"#,167			r#"{"name":"Bob","welcome":"Hello Bob!"}"#168		);169	}170171	#[test]172	fn functions() {173		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");174		assert_json!(175			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,176			r#""HelloDearWorld""#177		);178	}179180	#[test]181	fn local_methods() {182		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");183		assert_json!(184			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,185			r#""HelloDearWorld""#186		);187	}188189	#[test]190	fn object_locals() {191		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);192		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);193		assert_json!(194			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,195			r#"{"test":{"test":4}}"#196		);197	}198199	#[test]200	fn direct_self() {201		println!(202			"{:#?}",203			eval!(204				r#"205					{206						local me = self,207						a: 3,208						b(): me.a,209					}210				"#211			)212		);213	}214215	#[test]216	fn indirect_self() {217		// `self` assigned to `me` was lost when being218		// referenced from field219		eval_stdlib!(220			r#"{221				local me = self,222				a: 3,223				b: me.a,224			}.b"#225		);226	}227228	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly229	#[test]230	fn std_assert_ok() {231		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");232	}233234	#[test]235	#[should_panic]236	fn std_assert_failure() {237		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");238	}239240	#[test]241	fn string_is_string() {242		assert_eq!(243			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),244			Val::Literal(LiteralType::False)245		);246	}247248	#[test]249	fn base64_works() {250		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);251	}252253	#[test]254	fn utf8_chars() {255		assert_json_stdlib!(256			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,257			r#"{"c":128526,"l":1}"#258		)259	}260}
after · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16use std::{cell::RefCell, collections::HashMap, rc::Rc};17pub use val::*;1819rc_fn_helper!(20	Binding,21	binding,22	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val23);24rc_fn_helper!(25	LazyBinding,26	lazy_binding,27	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal28);29rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);30rc_fn_helper!(31	FunctionDefault,32	function_default,33	dyn Fn(Context, LocExpr) -> Val34);3536pub struct ExitGuard<'s>(&'s EvaluationState);37impl<'s> Drop for ExitGuard<'s> {38	fn drop(&mut self) {39		self.0.stack.borrow_mut().pop();40	}41}4243pub struct EvaluationState {44	pub stack: Rc<RefCell<Vec<LocExpr>>>,45	pub files: Rc<RefCell<HashMap<String, String>>>,46}47impl EvaluationState {48	#[must_use = "should keep exit guard before exit from function"]49	pub fn push(&self, e: LocExpr) -> ExitGuard {50		self.stack.borrow_mut().push(e);51		ExitGuard(self)52	}53	pub fn print_stack_trace(&self) {54		for e in self55			.stack56			.borrow()57			.iter()58			.rev()59			.map(|e| e.1.clone())60			.flatten()61		{62			println!("{:?}", e)63		}64	}65}66impl Default for EvaluationState {67	fn default() -> Self {68		EvaluationState {69			stack: Rc::new(RefCell::new(Vec::new())),70			files: Rc::new(RefCell::new(HashMap::new())),71		}72	}73}7475#[cfg(test)]76pub mod tests {77	use super::{evaluate, Context, Val};78	use crate::EvaluationState;79	use jsonnet_parser::*;8081	#[test]82	fn eval_state_stacktrace() {83		let state = EvaluationState::default();84		let _v = state.push(loc_expr!(85			Expr::Num(0.0),86			true,87			("test.jsonnet".to_owned(), 10, 20)88		));8990		state.print_stack_trace()91	}9293	macro_rules! eval {94		($str: expr) => {95			evaluate(96				Context::new(),97				&parse(98					$str,99					&ParserSettings {100						loc_data: true,101						file_name: "test.jsonnet".to_owned(),102					},103					)104				.unwrap(),105				)106		};107	}108109	macro_rules! eval_stdlib {110		($str: expr) => {{111			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";112			evaluate(113				Context::new(),114				&parse(115					&(std + $str),116					&ParserSettings {117						loc_data: true,118						file_name: "test.jsonnet".to_owned(),119					},120					)121				.unwrap(),122				)123			}};124	}125126	macro_rules! assert_eval {127		($str: expr) => {128			assert_eq!(129				evaluate(130					Context::new(),131					&parse(132						$str,133						&ParserSettings {134							loc_data: true,135							file_name: "test.jsonnet".to_owned(),136						}137						)138					.unwrap()139					),140				Val::Bool(true)141				)142		};143	}144	macro_rules! assert_json {145		($str: expr, $out: expr) => {146			assert_eq!(147				format!(148					"{}",149					evaluate(150						Context::new(),151						&parse(152							$str,153							&ParserSettings {154								loc_data: true,155								file_name: "test.jsonnet".to_owned(),156							}157						)158						.unwrap()159						)160					),161				$out162				)163		};164	}165	macro_rules! assert_json_stdlib {166		($str: expr, $out: expr) => {167			assert_eq!(format!("{}", eval_stdlib!($str)), $out)168		};169	}170	macro_rules! assert_eval_neg {171		($str: expr) => {172			assert_eq!(173				evaluate(174					Context::new(),175					&parse(176						$str,177						&ParserSettings {178							loc_data: true,179							file_name: "test.jsonnet".to_owned(),180						}181						)182					.unwrap()183					),184				Val::Bool(false)185				)186		};187	}188189	/// Sanity checking, before trusting to another tests190	#[test]191	fn equality_operator() {192		assert_eval!("2 == 2");193		assert_eval_neg!("2 != 2");194		assert_eval!("2 != 3");195		assert_eval_neg!("2 == 3");196		assert_eval!("'Hello' == 'Hello'");197		assert_eval_neg!("'Hello' != 'Hello'");198		assert_eval!("'Hello' != 'World'");199		assert_eval_neg!("'Hello' == 'World'");200	}201202	#[test]203	fn math_evaluation() {204		assert_eval!("2 + 2 * 2 == 6");205		assert_eval!("3 + (2 + 2 * 2) == 9");206	}207208	#[test]209	fn string_concat() {210		assert_eval!("'Hello' + 'World' == 'HelloWorld'");211		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");212		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");213	}214215	#[test]216	fn local() {217		assert_eval!("local a = 2; local b = 3; a + b == 5");218		assert_eval!("local a = 1, b = a + 1; a + b == 3");219		assert_eval!("local a = 1; local a = 2; a == 2");220	}221222	#[test]223	fn object_lazyness() {224		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);225	}226227	#[test]228	fn object_inheritance() {229		assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);230	}231232	#[test]233	fn test_object() {234		assert_json!("{a:2}", r#"{"a":2}"#);235		assert_json!("{a:2+2}", r#"{"a":4}"#);236		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);237		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);238		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);239		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);240		assert_json!(241			r#"242				{243					name: "Alice",244					welcome: "Hello " + self.name + "!",245				}246			"#,247			r#"{"name":"Alice","welcome":"Hello Alice!"}"#248		);249		assert_json!(250			r#"251				{252					name: "Alice",253					welcome: "Hello " + self.name + "!",254				} + {255					name: "Bob"256				}257			"#,258			r#"{"name":"Bob","welcome":"Hello Bob!"}"#259		);260	}261262	#[test]263	fn functions() {264		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");265		assert_json!(266			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,267			r#""HelloDearWorld""#268		);269	}270271	#[test]272	fn local_methods() {273		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");274		assert_json!(275			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,276			r#""HelloDearWorld""#277		);278	}279280	#[test]281	fn object_locals() {282		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);283		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);284		assert_json!(285			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,286			r#"{"test":{"test":4}}"#287		);288	}289290	#[test]291	fn direct_self() {292		println!(293			"{:#?}",294			eval!(295				r#"296					{297						local me = self,298						a: 3,299						b(): me.a,300					}301				"#302			)303		);304	}305306	#[test]307	fn indirect_self() {308		// `self` assigned to `me` was lost when being309		// referenced from field310		eval_stdlib!(311			r#"{312				local me = self,313				a: 3,314				b: me.a,315			}.b"#316		);317	}318319	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly320	#[test]321	fn std_assert_ok() {322		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");323	}324325	#[test]326	#[should_panic]327	fn std_assert_failure() {328		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");329	}330331	#[test]332	fn string_is_string() {333		assert_eq!(334			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),335			Val::Bool(false)336		);337	}338339	#[test]340	fn base64_works() {341		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);342	}343344	#[test]345	fn utf8_chars() {346		assert_json_stdlib!(347			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,348			r#"{"c":128526,"l":1}"#349		)350	}351352	#[test]353	fn json() {354		println!("{:?}", eval_stdlib!(r#"std.manifestJson({a:3, b:4, c:6})"#));355	}356357	#[test]358	fn sjsonnet() {359		eval!(360			r#"361			local x0 = {k: 1};362			local x1 = {k: x0.k + x0.k};363			local x2 = {k: x1.k + x1.k};364			local x3 = {k: x2.k + x2.k};365			local x4 = {k: x3.k + x3.k};366			local x5 = {k: x4.k + x4.k};367			local x6 = {k: x5.k + x5.k};368			local x7 = {k: x6.k + x6.k};369			local x8 = {k: x7.k + x7.k};370			local x9 = {k: x8.k + x8.k};371			local x10 = {k: x9.k + x9.k};372			local x11 = {k: x10.k + x10.k};373			local x12 = {k: x11.k + x11.k};374			local x13 = {k: x12.k + x12.k};375			local x14 = {k: x13.k + x13.k};376			local x15 = {k: x14.k + x14.k};377			local x16 = {k: x15.k + x15.k};378			local x17 = {k: x16.k + x16.k};379			local x18 = {k: x17.k + x17.k};380			local x19 = {k: x18.k + x18.k};381			local x20 = {k: x19.k + x19.k};382			local x21 = {k: x20.k + x20.k};383			x21.k384		"#385		);386	}387}
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -1,7 +1,4 @@
-use std::{
-	fmt::{Debug, Display},
-	rc::Rc,
-};
+use std::{fmt::Debug, rc::Rc};
 
 #[derive(Debug, Clone, PartialEq)]
 pub enum FieldName {
@@ -135,18 +132,6 @@
 	True,
 	False,
 }
-impl Display for LiteralType {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-		use LiteralType::*;
-		match self {
-			This => write!(f, "this"),
-			Null => write!(f, "null"),
-			True => write!(f, "true"),
-			False => write!(f, "false"),
-			_ => panic!("non printable item"),
-		}
-	}
-}
 
 #[derive(Debug, Clone, PartialEq)]
 pub struct SliceDesc {
@@ -241,7 +226,7 @@
 pub struct ExprLocation(pub String, pub usize, pub usize);
 impl Debug for ExprLocation {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-		write!(f, "{}:{:?}", self.0, self.1)
+		write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)
 	}
 }
 
@@ -259,13 +244,13 @@
 macro_rules! loc_expr {
 	($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {
 		LocExpr(
-			Rc::new($expr),
+			std::rc::Rc::new($expr),
 			if $need_loc {
-				Some(Rc::new(ExprLocation($name.to_owned(), $start, $end)))
+				Some(std::rc::Rc::new(ExprLocation($name.to_owned(), $start, $end)))
 			} else {
 				None
-			},
-		)
+				},
+			)
 	};
 }