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
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -13,6 +13,7 @@
 pub use evaluate::*;
 use jsonnet_parser::*;
 pub use obj::*;
+use std::{cell::RefCell, collections::HashMap, rc::Rc};
 pub use val::*;
 
 rc_fn_helper!(
@@ -32,48 +33,131 @@
 	dyn Fn(Context, LocExpr) -> Val
 );
 
+pub struct ExitGuard<'s>(&'s EvaluationState);
+impl<'s> Drop for ExitGuard<'s> {
+	fn drop(&mut self) {
+		self.0.stack.borrow_mut().pop();
+	}
+}
+
+pub struct EvaluationState {
+	pub stack: Rc<RefCell<Vec<LocExpr>>>,
+	pub files: Rc<RefCell<HashMap<String, String>>>,
+}
+impl EvaluationState {
+	#[must_use = "should keep exit guard before exit from function"]
+	pub fn push(&self, e: LocExpr) -> ExitGuard {
+		self.stack.borrow_mut().push(e);
+		ExitGuard(self)
+	}
+	pub fn print_stack_trace(&self) {
+		for e in self
+			.stack
+			.borrow()
+			.iter()
+			.rev()
+			.map(|e| e.1.clone())
+			.flatten()
+		{
+			println!("{:?}", e)
+		}
+	}
+}
+impl Default for EvaluationState {
+	fn default() -> Self {
+		EvaluationState {
+			stack: Rc::new(RefCell::new(Vec::new())),
+			files: Rc::new(RefCell::new(HashMap::new())),
+		}
+	}
+}
+
 #[cfg(test)]
 pub mod tests {
 	use super::{evaluate, Context, Val};
+	use crate::EvaluationState;
 	use jsonnet_parser::*;
 
+	#[test]
+	fn eval_state_stacktrace() {
+		let state = EvaluationState::default();
+		let _v = state.push(loc_expr!(
+			Expr::Num(0.0),
+			true,
+			("test.jsonnet".to_owned(), 10, 20)
+		));
+
+		state.print_stack_trace()
+	}
+
 	macro_rules! eval {
 		($str: expr) => {
-			evaluate(Context::new(), &parse($str, &ParserSettings {
-				loc_data: false,
-				file_name: "test.jsonnet".to_owned(),
-			}).unwrap())
+			evaluate(
+				Context::new(),
+				&parse(
+					$str,
+					&ParserSettings {
+						loc_data: true,
+						file_name: "test.jsonnet".to_owned(),
+					},
+					)
+				.unwrap(),
+				)
 		};
 	}
 
 	macro_rules! eval_stdlib {
 		($str: expr) => {{
 			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
-			evaluate(Context::new(), &parse(&(std + $str), &ParserSettings {
-				loc_data: false,
-				file_name: "test.jsonnet".to_owned(),
-			}).unwrap())
+			evaluate(
+				Context::new(),
+				&parse(
+					&(std + $str),
+					&ParserSettings {
+						loc_data: true,
+						file_name: "test.jsonnet".to_owned(),
+					},
+					)
+				.unwrap(),
+				)
 			}};
 	}
 
 	macro_rules! assert_eval {
 		($str: expr) => {
 			assert_eq!(
-				evaluate(Context::new(), &parse($str, &ParserSettings {
-					loc_data: false,
-					file_name: "test.jsonnet".to_owned(),
-				}).unwrap()),
-				Val::Literal(LiteralType::True)
+				evaluate(
+					Context::new(),
+					&parse(
+						$str,
+						&ParserSettings {
+							loc_data: true,
+							file_name: "test.jsonnet".to_owned(),
+						}
+						)
+					.unwrap()
+					),
+				Val::Bool(true)
 				)
 		};
 	}
 	macro_rules! assert_json {
 		($str: expr, $out: expr) => {
 			assert_eq!(
-				format!("{}", evaluate(Context::new(), &parse($str, &ParserSettings {
-					loc_data: false,
-					file_name: "test.jsonnet".to_owned(),
-				}).unwrap())),
+				format!(
+					"{}",
+					evaluate(
+						Context::new(),
+						&parse(
+							$str,
+							&ParserSettings {
+								loc_data: true,
+								file_name: "test.jsonnet".to_owned(),
+							}
+						)
+						.unwrap()
+						)
+					),
 				$out
 				)
 		};
@@ -86,11 +170,18 @@
 	macro_rules! assert_eval_neg {
 		($str: expr) => {
 			assert_eq!(
-				evaluate(Context::new(), &parse($str, &ParserSettings {
-					loc_data: false,
-					file_name: "test.jsonnet".to_owned(),
-				}).unwrap()),
-				Val::Literal(LiteralType::False)
+				evaluate(
+					Context::new(),
+					&parse(
+						$str,
+						&ParserSettings {
+							loc_data: true,
+							file_name: "test.jsonnet".to_owned(),
+						}
+						)
+					.unwrap()
+					),
+				Val::Bool(false)
 				)
 		};
 	}
@@ -241,7 +332,7 @@
 	fn string_is_string() {
 		assert_eq!(
 			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),
-			Val::Literal(LiteralType::False)
+			Val::Bool(false)
 		);
 	}
 
@@ -257,4 +348,40 @@
 			r#"{"c":128526,"l":1}"#
 		)
 	}
+
+	#[test]
+	fn json() {
+		println!("{:?}", eval_stdlib!(r#"std.manifestJson({a:3, b:4, c:6})"#));
+	}
+
+	#[test]
+	fn sjsonnet() {
+		eval!(
+			r#"
+			local x0 = {k: 1};
+			local x1 = {k: x0.k + x0.k};
+			local x2 = {k: x1.k + x1.k};
+			local x3 = {k: x2.k + x2.k};
+			local x4 = {k: x3.k + x3.k};
+			local x5 = {k: x4.k + x4.k};
+			local x6 = {k: x5.k + x5.k};
+			local x7 = {k: x6.k + x6.k};
+			local x8 = {k: x7.k + x7.k};
+			local x9 = {k: x8.k + x8.k};
+			local x10 = {k: x9.k + x9.k};
+			local x11 = {k: x10.k + x10.k};
+			local x12 = {k: x11.k + x11.k};
+			local x13 = {k: x12.k + x12.k};
+			local x14 = {k: x13.k + x13.k};
+			local x15 = {k: x14.k + x14.k};
+			local x16 = {k: x15.k + x15.k};
+			local x17 = {k: x16.k + x16.k};
+			local x18 = {k: x17.k + x17.k};
+			local x19 = {k: x18.k + x18.k};
+			local x20 = {k: x19.k + x19.k};
+			local x21 = {k: x20.k + x20.k};
+			x21.k
+		"#
+		);
+	}
 }
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jsonnet-parser/src/expr.rs
1use std::{2	fmt::{Debug, Display},3	rc::Rc,4};56#[derive(Debug, Clone, PartialEq)]7pub enum FieldName {8	/// {fixed: 2}9	Fixed(String),10	/// {["dyn"+"amic"]: 3}11	Dyn(LocExpr),12}1314#[derive(Debug, Clone, PartialEq)]15pub enum Visibility {16	/// :17	Normal,18	/// ::19	Hidden,20	/// :::21	Unhide,22}2324#[derive(Debug, Clone, PartialEq)]25pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2627#[derive(Debug, Clone, PartialEq)]28pub struct FieldMember {29	pub name: FieldName,30	pub plus: bool,31	pub params: Option<ParamsDesc>,32	pub visibility: Visibility,33	pub value: LocExpr,34}3536#[derive(Debug, Clone, PartialEq)]37pub enum Member {38	Field(FieldMember),39	BindStmt(BindSpec),40	AssertStmt(AssertStmt),41}4243#[derive(Debug, Clone, Copy, PartialEq)]44pub enum UnaryOpType {45	Plus,46	Minus,47	BitNot,48	Not,49}5051#[derive(Debug, Clone, Copy, PartialEq)]52pub enum BinaryOpType {53	Mul,54	Div,55	Mod,5657	Add,58	Sub,5960	Lhs,61	Rhs,6263	Lt,64	Gt,65	Lte,66	Gte,6768	In,6970	Eq,71	Ne,7273	BitAnd,74	BitOr,75	BitXor,7677	And,78	Or,79}8081/// name, default value82#[derive(Debug, Clone, PartialEq)]83pub struct Param(pub String, pub Option<LocExpr>);84/// Defined function parameters85#[derive(Debug, Clone, PartialEq)]86pub struct ParamsDesc(pub Vec<Param>);87impl ParamsDesc {88	pub fn with_defaults(&self) -> Vec<Param> {89		self.0.iter().filter(|e| e.1.is_some()).cloned().collect()90	}91}9293#[derive(Debug, Clone, PartialEq)]94pub struct Arg(pub Option<String>, pub LocExpr);95#[derive(Debug, Clone, PartialEq)]96pub struct ArgsDesc(pub Vec<Arg>);9798#[derive(Debug, Clone, PartialEq)]99pub struct BindSpec {100	pub name: String,101	pub params: Option<ParamsDesc>,102	pub value: LocExpr,103}104105#[derive(Debug, Clone, PartialEq)]106pub struct IfSpecData(pub LocExpr);107#[derive(Debug, Clone, PartialEq)]108pub struct ForSpecData(pub String, pub LocExpr);109110#[derive(Debug, Clone, PartialEq)]111pub enum CompSpec {112	IfSpec(IfSpecData),113	ForSpec(ForSpecData),114}115116#[derive(Debug, Clone, PartialEq)]117pub enum ObjBody {118	MemberList(Vec<Member>),119	ObjComp {120		pre_locals: Vec<BindSpec>,121		key: LocExpr,122		value: LocExpr,123		post_locals: Vec<BindSpec>,124		first: ForSpecData,125		rest: Vec<CompSpec>,126	},127}128129#[derive(Debug, Clone, PartialEq)]130pub enum LiteralType {131	This,132	Super,133	Dollar,134	Null,135	True,136	False,137}138impl Display for LiteralType {139	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {140		use LiteralType::*;141		match self {142			This => write!(f, "this"),143			Null => write!(f, "null"),144			True => write!(f, "true"),145			False => write!(f, "false"),146			_ => panic!("non printable item"),147		}148	}149}150151#[derive(Debug, Clone, PartialEq)]152pub struct SliceDesc {153	pub start: Option<LocExpr>,154	pub end: Option<LocExpr>,155	pub step: Option<LocExpr>,156}157158/// Syntax base159#[derive(Debug, Clone, PartialEq)]160pub enum Expr {161	Literal(LiteralType),162163	/// String value: "hello"164	Str(String),165	/// Number: 1, 2.0, 2e+20166	Num(f64),167	/// Variable name: test168	Var(String),169170	/// Array of expressions: [1, 2, "Hello"]171	Arr(Vec<LocExpr>),172	/// Array comprehension:173	/// ```jsonnet174	///  ingredients: [175	///    { kind: kind, qty: 4 / 3 }176	///    for kind in [177	///      'Honey Syrup',178	///      'Lemon Juice',179	///      'Farmers Gin',180	///    ]181	///  ],182	/// ```183	ArrComp(LocExpr, ForSpecData, Vec<CompSpec>),184185	/// Object: {a: 2}186	Obj(ObjBody),187	/// Object extension: var1 {b: 2}188	ObjExtend(LocExpr, ObjBody),189190	/// (obj)191	Parened(LocExpr),192193	/// Params in function definition194	/// hello, world, test = 2195	Params(ParamsDesc),196	/// Args in function call197	/// 2 + 2, 3, named = 6198	Args(ArgsDesc),199200	/// -2201	UnaryOp(UnaryOpType, LocExpr),202	/// 2 - 2203	BinaryOp(LocExpr, BinaryOpType, LocExpr),204	/// assert 2 == 2 : "Math is broken"205	AssertExpr(AssertStmt, LocExpr),206	/// local a = 2; { b: a }207	LocalExpr(Vec<BindSpec>, LocExpr),208209	/// a = 3210	Bind(BindSpec),211	/// import "hello"212	Import(String),213	/// importStr "file.txt"214	ImportStr(String),215	/// error "I'm broken"216	Error(LocExpr),217	/// a(b, c)218	Apply(LocExpr, ArgsDesc),219	///220	Select(LocExpr, String),221	/// a[b]222	Index(LocExpr, LocExpr),223	/// a[1::2]224	Slice(LocExpr, SliceDesc),225	/// function(x) x226	Function(ParamsDesc, LocExpr),227	/// if true == false then 1 else 2228	IfElse {229		cond: IfSpecData,230		cond_then: LocExpr,231		cond_else: Option<LocExpr>,232	},233	/// if 2 = 3234	IfSpec(IfSpecData),235	/// for elem in array236	ForSpec(ForSpecData),237}238239/// file, begin offset, end offset240#[derive(Clone, PartialEq)]241pub struct ExprLocation(pub String, pub usize, pub usize);242impl Debug for ExprLocation {243	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {244		write!(f, "{}:{:?}", self.0, self.1)245	}246}247248/// Holds AST expression and its location in source file+249#[derive(Clone, PartialEq)]250pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);251impl Debug for LocExpr {252	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {253		write!(f, "{:?} from {:?}", self.0, self.1)254	}255}256257/// Creates LocExpr from Expr and ExprLocation components258#[macro_export]259macro_rules! loc_expr {260	($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {261		LocExpr(262			Rc::new($expr),263			if $need_loc {264				Some(Rc::new(ExprLocation($name.to_owned(), $start, $end)))265			} else {266				None267			},268		)269	};270}271272/// Creates LocExpr without location info273#[macro_export]274macro_rules! loc_expr_todo {275	($expr:expr) => {276		LocExpr(Rc::new($expr), None)277	};278}
after · crates/jsonnet-parser/src/expr.rs
1use std::{fmt::Debug, rc::Rc};23#[derive(Debug, Clone, PartialEq)]4pub enum FieldName {5	/// {fixed: 2}6	Fixed(String),7	/// {["dyn"+"amic"]: 3}8	Dyn(LocExpr),9}1011#[derive(Debug, Clone, PartialEq)]12pub enum Visibility {13	/// :14	Normal,15	/// ::16	Hidden,17	/// :::18	Unhide,19}2021#[derive(Debug, Clone, PartialEq)]22pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2324#[derive(Debug, Clone, PartialEq)]25pub struct FieldMember {26	pub name: FieldName,27	pub plus: bool,28	pub params: Option<ParamsDesc>,29	pub visibility: Visibility,30	pub value: LocExpr,31}3233#[derive(Debug, Clone, PartialEq)]34pub enum Member {35	Field(FieldMember),36	BindStmt(BindSpec),37	AssertStmt(AssertStmt),38}3940#[derive(Debug, Clone, Copy, PartialEq)]41pub enum UnaryOpType {42	Plus,43	Minus,44	BitNot,45	Not,46}4748#[derive(Debug, Clone, Copy, PartialEq)]49pub enum BinaryOpType {50	Mul,51	Div,52	Mod,5354	Add,55	Sub,5657	Lhs,58	Rhs,5960	Lt,61	Gt,62	Lte,63	Gte,6465	In,6667	Eq,68	Ne,6970	BitAnd,71	BitOr,72	BitXor,7374	And,75	Or,76}7778/// name, default value79#[derive(Debug, Clone, PartialEq)]80pub struct Param(pub String, pub Option<LocExpr>);81/// Defined function parameters82#[derive(Debug, Clone, PartialEq)]83pub struct ParamsDesc(pub Vec<Param>);84impl ParamsDesc {85	pub fn with_defaults(&self) -> Vec<Param> {86		self.0.iter().filter(|e| e.1.is_some()).cloned().collect()87	}88}8990#[derive(Debug, Clone, PartialEq)]91pub struct Arg(pub Option<String>, pub LocExpr);92#[derive(Debug, Clone, PartialEq)]93pub struct ArgsDesc(pub Vec<Arg>);9495#[derive(Debug, Clone, PartialEq)]96pub struct BindSpec {97	pub name: String,98	pub params: Option<ParamsDesc>,99	pub value: LocExpr,100}101102#[derive(Debug, Clone, PartialEq)]103pub struct IfSpecData(pub LocExpr);104#[derive(Debug, Clone, PartialEq)]105pub struct ForSpecData(pub String, pub LocExpr);106107#[derive(Debug, Clone, PartialEq)]108pub enum CompSpec {109	IfSpec(IfSpecData),110	ForSpec(ForSpecData),111}112113#[derive(Debug, Clone, PartialEq)]114pub enum ObjBody {115	MemberList(Vec<Member>),116	ObjComp {117		pre_locals: Vec<BindSpec>,118		key: LocExpr,119		value: LocExpr,120		post_locals: Vec<BindSpec>,121		first: ForSpecData,122		rest: Vec<CompSpec>,123	},124}125126#[derive(Debug, Clone, PartialEq)]127pub enum LiteralType {128	This,129	Super,130	Dollar,131	Null,132	True,133	False,134}135136#[derive(Debug, Clone, PartialEq)]137pub struct SliceDesc {138	pub start: Option<LocExpr>,139	pub end: Option<LocExpr>,140	pub step: Option<LocExpr>,141}142143/// Syntax base144#[derive(Debug, Clone, PartialEq)]145pub enum Expr {146	Literal(LiteralType),147148	/// String value: "hello"149	Str(String),150	/// Number: 1, 2.0, 2e+20151	Num(f64),152	/// Variable name: test153	Var(String),154155	/// Array of expressions: [1, 2, "Hello"]156	Arr(Vec<LocExpr>),157	/// Array comprehension:158	/// ```jsonnet159	///  ingredients: [160	///    { kind: kind, qty: 4 / 3 }161	///    for kind in [162	///      'Honey Syrup',163	///      'Lemon Juice',164	///      'Farmers Gin',165	///    ]166	///  ],167	/// ```168	ArrComp(LocExpr, ForSpecData, Vec<CompSpec>),169170	/// Object: {a: 2}171	Obj(ObjBody),172	/// Object extension: var1 {b: 2}173	ObjExtend(LocExpr, ObjBody),174175	/// (obj)176	Parened(LocExpr),177178	/// Params in function definition179	/// hello, world, test = 2180	Params(ParamsDesc),181	/// Args in function call182	/// 2 + 2, 3, named = 6183	Args(ArgsDesc),184185	/// -2186	UnaryOp(UnaryOpType, LocExpr),187	/// 2 - 2188	BinaryOp(LocExpr, BinaryOpType, LocExpr),189	/// assert 2 == 2 : "Math is broken"190	AssertExpr(AssertStmt, LocExpr),191	/// local a = 2; { b: a }192	LocalExpr(Vec<BindSpec>, LocExpr),193194	/// a = 3195	Bind(BindSpec),196	/// import "hello"197	Import(String),198	/// importStr "file.txt"199	ImportStr(String),200	/// error "I'm broken"201	Error(LocExpr),202	/// a(b, c)203	Apply(LocExpr, ArgsDesc),204	///205	Select(LocExpr, String),206	/// a[b]207	Index(LocExpr, LocExpr),208	/// a[1::2]209	Slice(LocExpr, SliceDesc),210	/// function(x) x211	Function(ParamsDesc, LocExpr),212	/// if true == false then 1 else 2213	IfElse {214		cond: IfSpecData,215		cond_then: LocExpr,216		cond_else: Option<LocExpr>,217	},218	/// if 2 = 3219	IfSpec(IfSpecData),220	/// for elem in array221	ForSpec(ForSpecData),222}223224/// file, begin offset, end offset225#[derive(Clone, PartialEq)]226pub struct ExprLocation(pub String, pub usize, pub usize);227impl Debug for ExprLocation {228	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {229		write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)230	}231}232233/// Holds AST expression and its location in source file+234#[derive(Clone, PartialEq)]235pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);236impl Debug for LocExpr {237	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {238		write!(f, "{:?} from {:?}", self.0, self.1)239	}240}241242/// Creates LocExpr from Expr and ExprLocation components243#[macro_export]244macro_rules! loc_expr {245	($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {246		LocExpr(247			std::rc::Rc::new($expr),248			if $need_loc {249				Some(std::rc::Rc::new(ExprLocation($name.to_owned(), $start, $end)))250			} else {251				None252				},253			)254	};255}256257/// Creates LocExpr without location info258#[macro_export]259macro_rules! loc_expr_todo {260	($expr:expr) => {261		LocExpr(Rc::new($expr), None)262	};263}