--- 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()) + ), } } --- 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>>, + pub files: Rc>>, +} +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 + "# + ); + } } --- 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 - }, - ) + }, + ) }; }