From 1cc9e6c67fda282755d18762335e8bb74692a618 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 23 May 2021 15:46:26 +0000 Subject: [PATCH] feat: object assertions --- --- a/crates/jrsonnet-evaluator/src/ctx.rs +++ b/crates/jrsonnet-evaluator/src/ctx.rs @@ -85,6 +85,10 @@ self.extend(new_bindings, None, None, None) } + pub fn with_this_super(self, new_this: ObjValue, new_super_obj: Option) -> Self { + self.extend(FxHashMap::default(), None, Some(new_this), new_super_obj) + } + pub fn extend( self, new_bindings: FxHashMap, --- a/crates/jrsonnet-evaluator/src/evaluate.rs +++ b/crates/jrsonnet-evaluator/src/evaluate.rs @@ -261,6 +261,7 @@ } let mut new_members = FxHashMap::default(); + let mut assertions = Vec::new(); for member in members.iter() { match member { Member::Field(FieldMember { @@ -325,10 +326,12 @@ ); } Member::BindStmt(_) => {} - Member::AssertStmt(_) => {} + Member::AssertStmt(stmt) => { + assertions.push(stmt.clone()); + } } } - let this = ObjValue::new(None, Rc::new(new_members)); + let this = ObjValue::new(context, None, Rc::new(new_members), Rc::new(assertions)); future_this.fill(this.clone()); Ok(this) } @@ -385,7 +388,7 @@ } } - let this = ObjValue::new(None, Rc::new(new_members)); + let this = ObjValue::new(context, None, Rc::new(new_members), Rc::new(Vec::new())); future_this.fill(this.clone()); this } @@ -413,6 +416,36 @@ }) } +pub fn evaluate_assert( + context: Context, + assertion: &AssertStmt, +) -> Result<()> { + let value = &assertion.0; + let msg = &assertion.1; + let assertion_result = push( + value.1.as_ref(), + || "assertion condition".to_owned(), + || { + evaluate(context.clone(), value)? + .try_cast_bool("assertion condition should be of type `boolean`") + }, + )?; + if !assertion_result { + push( + value.1.as_ref(), + || "assertion failure".to_owned(), + || { + if let Some(msg) = msg { + throw!(AssertionFailed(evaluate(context, msg)?.to_string()?)); + } else { + throw!(AssertionFailed(Val::Null.to_string()?)); + } + }, + )? + } + Ok(()) +} + pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result { use Expr::*; let LocExpr(expr, _loc) = lexpr; @@ -552,30 +585,9 @@ evaluate_method(context, "anonymous".into(), params.clone(), body.clone()) } Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))), - AssertExpr(AssertStmt(value, msg), returned) => { - let assertion_result = push( - value.1.as_ref(), - || "assertion condition".to_owned(), - || { - evaluate(context.clone(), value)? - .try_cast_bool("assertion condition should be of type `boolean`") - }, - )?; - if assertion_result { - evaluate(context, returned)? - } else { - push( - value.1.as_ref(), - || "assertion failure".to_owned(), - || { - if let Some(msg) = msg { - throw!(AssertionFailed(evaluate(context, msg)?.to_string()?)); - } else { - throw!(AssertionFailed(Val::Null.to_string()?)); - } - }, - )? - } + AssertExpr(assert, returned) => { + evaluate_assert(context.clone(), &assert)?; + evaluate(context, returned)? } ErrorStmt(e) => push( loc.as_ref(), --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -1,4 +1,5 @@ use crate::{ + Context, error::{Error::*, LocError, Result}, throw, LazyBinding, LazyVal, ObjMember, ObjValue, Val, }; @@ -76,7 +77,7 @@ }, ); } - Self::Obj(ObjValue::new(None, Rc::new(entries))) + Self::Obj(ObjValue::new(Context::new(), None, Rc::new(entries), Rc::new(Vec::new()))) } } } --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -1,7 +1,7 @@ -use crate::{evaluate_add_op, LazyBinding, Result, Val}; +use crate::{Context, evaluate_add_op, evaluate_assert, LazyBinding, Result, Val}; use jrsonnet_interner::IStr; -use jrsonnet_parser::{ExprLocation, Visibility}; -use rustc_hash::FxHashMap; +use jrsonnet_parser::{ExprLocation, LocExpr, Visibility, AssertStmt}; +use rustc_hash::{FxHashMap, FxHashSet}; use std::hash::{Hash, Hasher}; use std::{cell::RefCell, fmt::Debug, hash::BuildHasherDefault, rc::Rc}; @@ -17,7 +17,10 @@ type CacheKey = (IStr, ObjValue); #[derive(Debug)] pub struct ObjValueInternals { + context: Context, super_obj: Option, + assertions: Rc>, + assertions_ran: RefCell>, this_obj: Option, this_entries: Rc>, value_cache: RefCell>>, @@ -51,26 +54,32 @@ } impl ObjValue { - pub fn new(super_obj: Option, this_entries: Rc>) -> Self { + pub fn new(context: Context, super_obj: Option, this_entries: Rc>, assertions: Rc>) -> Self { Self(Rc::new(ObjValueInternals { + context, super_obj, + assertions, + assertions_ran: RefCell::new(FxHashSet::default()), this_obj: None, this_entries, value_cache: RefCell::new(FxHashMap::default()), })) } pub fn new_empty() -> Self { - Self::new(None, Rc::new(FxHashMap::default())) + Self::new(Context::new(), None, Rc::new(FxHashMap::default()), Rc::new(Vec::new())) } pub fn extend_from(&self, super_obj: Self) -> Self { match &self.0.super_obj { - None => Self::new(Some(super_obj), self.0.this_entries.clone()), - Some(v) => Self::new(Some(v.extend_from(super_obj)), self.0.this_entries.clone()), + None => Self::new(self.0.context.clone(), Some(super_obj), self.0.this_entries.clone(), self.0.assertions.clone()), + Some(v) => Self::new(self.0.context.clone(), Some(v.extend_from(super_obj)), self.0.this_entries.clone(), self.0.assertions.clone()), } } pub fn with_this(&self, this_obj: Self) -> Self { Self(Rc::new(ObjValueInternals { + context: self.0.context.clone(), super_obj: self.0.super_obj.clone(), + assertions: self.0.assertions.clone(), + assertions_ran: RefCell::new(FxHashSet::default()), this_obj: Some(this_obj), this_entries: self.0.this_entries.clone(), value_cache: RefCell::new(FxHashMap::default()), @@ -167,16 +176,17 @@ } pub fn get(&self, key: IStr) -> Result> { + self.run_assertions(self.0.this_obj.as_ref().unwrap_or(self))?; self.get_raw(key, self.0.this_obj.as_ref()) } pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self { let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default()); new.insert(key, value); - Self::new(Some(self), Rc::new(new)) + Self::new(Context::new(), Some(self), Rc::new(new), Rc::new(Vec::new())) } - pub(crate) fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result> { + pub fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result> { let real_this = real_this.unwrap_or(self); let cache_key = (key.clone(), real_this.clone()); @@ -210,6 +220,24 @@ .evaluate(Some(real_this.clone()), self.0.super_obj.clone())? .evaluate() } + fn run_assertions(&self, real_this: &Self) -> Result<()> { + if self.0.assertions_ran.borrow().contains(&real_this) { + // Assertions already ran + } else { + self.0.assertions_ran.borrow_mut().insert(real_this.clone()); + for assertion in self.0.assertions.iter() { + println!("{:#?}", assertion); + if let Err(e) = evaluate_assert(self.0.context.clone().with_this_super(real_this.clone(), self.0.super_obj.clone()), &assertion) { + self.0.assertions_ran.borrow_mut().remove(&real_this); + return Err(e) + } + } + if let Some(super_obj) = &self.0.super_obj { + super_obj.run_assertions(&real_this)?; + } + } + Ok(()) + } pub fn ptr_eq(a: &Self, b: &Self) -> bool { Rc::ptr_eq(&a.0, &b.0) --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -40,7 +40,7 @@ #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct AssertStmt(pub LocExpr, pub Option); #[cfg_attr(feature = "serialize", derive(Serialize))] --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ pname = "jrsonnet"; version = "0.1.0"; src = self; - cargoSha256 = "sha256-5RzjO9McVqG8+1+p+wRvygYCnemPjUAVB9TpWOp2ipA="; + cargoSha256 = "sha256-6VhaQi3L2LWzR0cq7oRG81MDbrKJbzSNPcvYSoQ5ISo="; }; in { defaultPackage = jrsonnet; }); } -- gitstuff