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
6767
68pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {68pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {
69 match (op, b) {69 match (op, b) {
70 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),70 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()),
71 (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),71 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),
72 (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),
73 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),72 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),
74 }73 }
75}74}
7675
77pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {76pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {
78 match (a, op, b) {77 match (a, op, b) {
79 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),78 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.evaluate(), o, b),
80 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),79 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.evaluate()),
8180
82 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),81 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
83 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),
84 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),82 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),
8583
86 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),84 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
87 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),85 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
8886
89 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {87 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
90 bool_val(false)
91 }
92 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
93 bool_val(false)
94 }
95 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {88 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
96 bool_val(false)
97 }
98 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
99 bool_val(true)
100 }
10189
102 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),90 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
10391
134 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {122 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {
135 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)123 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)
136 }124 }
125 (a, BinaryOpType::Eq, b) => bool_val(a == b),
126 (a, BinaryOpType::Ne, b) => bool_val(a != b),
137 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),127 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
138 }128 }
139}129}
238228
239pub fn evaluate(context: Context, expr: &LocExpr) -> Val {229pub fn evaluate(context: Context, expr: &LocExpr) -> Val {
240 use Expr::*;230 use Expr::*;
241 let LocExpr(expr, _location) = expr;231 let LocExpr(expr, loc) = expr;
242 match &**expr {232 match &**expr {
243 Literal(LiteralType::This) => Val::Obj(233 Literal(LiteralType::This) => Val::Obj(
244 context234 context
252 .clone()242 .clone()
253 .unwrap_or_else(|| panic!("super not found")),243 .unwrap_or_else(|| panic!("super not found")),
254 ),244 ),
255 Literal(t) => Val::Literal(t.clone()),245 Literal(LiteralType::True) => Val::Bool(true),
246 Literal(LiteralType::False) => Val::Bool(false),
247 Literal(LiteralType::Null) => Val::Null,
256 Parened(e) => evaluate(context, e),248 Parened(e) => evaluate(context, e),
257 Str(v) => Val::Str(v.clone()),249 Str(v) => Val::Str(v.clone()),
258 Num(v) => Val::Num(*v),250 Num(v) => Val::Num(*v),
383 cond_then,375 cond_then,
384 cond_else,376 cond_else,
385 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {377 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {
386 Val::Literal(LiteralType::True) => evaluate(context, cond_then),378 Val::Bool(true) => evaluate(context, cond_then),
387 Val::Literal(LiteralType::False) => match cond_else {379 Val::Bool(false) => match cond_else {
388 Some(v) => evaluate(context, v),380 Some(v) => evaluate(context, v),
389 None => Val::Literal(LiteralType::False),381 None => Val::Bool(false),
390 },382 },
391 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),383 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),
392 },384 },
393 _ => panic!("evaluation not implemented: {:?}", expr),385 _ => panic!(
386 "evaluation not implemented: {:?}",
387 LocExpr(expr.clone(), loc.clone())
388 ),
394 }389 }
395}390}
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
--- 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
-			},
-		)
+				},
+			)
 	};
 }