git.delta.rocks / jrsonnet / refs/commits / 1cc9e6c67fda

difftreelog

feat object assertions

Yaroslav Bolyukin2021-05-23parent: #0ec1408.patch.diff
in: master

6 files changed

modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- 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<ObjValue>) -> Self {
+		self.extend(FxHashMap::default(), None, Some(new_this), new_super_obj)
+	}
+
 	pub fn extend(
 		self,
 		new_bindings: FxHashMap<IStr, LazyVal>,
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- 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<Val> {
 	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(),
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- 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())))
 			}
 		}
 	}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- 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<ObjValue>,
+	assertions: Rc<Vec<AssertStmt>>,
+	assertions_ran: RefCell<FxHashSet<ObjValue>>,
 	this_obj: Option<ObjValue>,
 	this_entries: Rc<FxHashMap<IStr, ObjMember>>,
 	value_cache: RefCell<FxHashMap<CacheKey, Option<Val>>>,
@@ -51,26 +54,32 @@
 }
 
 impl ObjValue {
-	pub fn new(super_obj: Option<Self>, this_entries: Rc<FxHashMap<IStr, ObjMember>>) -> Self {
+	pub fn new(context: Context, super_obj: Option<Self>, this_entries: Rc<FxHashMap<IStr, ObjMember>>, assertions: Rc<Vec<AssertStmt>>) -> 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<Option<Val>> {
+		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<Option<Val>> {
+	pub fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
 		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)
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jrsonnet-parser/src/expr.rs
1use jrsonnet_interner::IStr;2#[cfg(feature = "deserialize")]3use serde::Deserialize;4#[cfg(feature = "serialize")]5use serde::Serialize;6use std::{7	fmt::{Debug, Display},8	ops::Deref,9	path::PathBuf,10	rc::Rc,11};1213#[cfg_attr(feature = "serialize", derive(Serialize))]14#[cfg_attr(feature = "deserialize", derive(Deserialize))]15#[derive(Debug, PartialEq)]16pub enum FieldName {17	/// {fixed: 2}18	Fixed(IStr),19	/// {["dyn"+"amic"]: 3}20	Dyn(LocExpr),21}2223#[cfg_attr(feature = "serialize", derive(Serialize))]24#[cfg_attr(feature = "deserialize", derive(Deserialize))]25#[derive(Debug, Clone, Copy, PartialEq)]26pub enum Visibility {27	/// :28	Normal,29	/// ::30	Hidden,31	/// :::32	Unhide,33}3435impl Visibility {36	pub fn is_visible(&self) -> bool {37		matches!(self, Self::Normal | Self::Unhide)38	}39}4041#[cfg_attr(feature = "serialize", derive(Serialize))]42#[cfg_attr(feature = "deserialize", derive(Deserialize))]43#[derive(Debug, PartialEq)]44pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4546#[cfg_attr(feature = "serialize", derive(Serialize))]47#[cfg_attr(feature = "deserialize", derive(Deserialize))]48#[derive(Debug, PartialEq)]49pub struct FieldMember {50	pub name: FieldName,51	pub plus: bool,52	pub params: Option<ParamsDesc>,53	pub visibility: Visibility,54	pub value: LocExpr,55}5657#[cfg_attr(feature = "serialize", derive(Serialize))]58#[cfg_attr(feature = "deserialize", derive(Deserialize))]59#[derive(Debug, PartialEq)]60pub enum Member {61	Field(FieldMember),62	BindStmt(BindSpec),63	AssertStmt(AssertStmt),64}6566#[cfg_attr(feature = "serialize", derive(Serialize))]67#[cfg_attr(feature = "deserialize", derive(Deserialize))]68#[derive(Debug, Clone, Copy, PartialEq)]69pub enum UnaryOpType {70	Plus,71	Minus,72	BitNot,73	Not,74}75impl Display for UnaryOpType {76	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {77		use UnaryOpType::*;78		write!(79			f,80			"{}",81			match self {82				Plus => "+",83				Minus => "-",84				BitNot => "~",85				Not => "!",86			}87		)88	}89}9091#[cfg_attr(feature = "serialize", derive(Serialize))]92#[cfg_attr(feature = "deserialize", derive(Deserialize))]93#[derive(Debug, Clone, Copy, PartialEq)]94pub enum BinaryOpType {95	Mul,96	Div,9798	/// Implemented as intrinsic, put here for completeness99	Mod,100101	Add,102	Sub,103104	Lhs,105	Rhs,106107	Lt,108	Gt,109	Lte,110	Gte,111112	BitAnd,113	BitOr,114	BitXor,115116	Eq,117	Neq,118119	And,120	Or,121}122impl Display for BinaryOpType {123	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {124		use BinaryOpType::*;125		write!(126			f,127			"{}",128			match self {129				Mul => "*",130				Div => "/",131				Mod => "%",132				Add => "+",133				Sub => "-",134				Lhs => "<<",135				Rhs => ">>",136				Lt => "<",137				Gt => ">",138				Lte => "<=",139				Gte => ">=",140				BitAnd => "&",141				BitOr => "|",142				BitXor => "^",143				Eq => "==",144				New => "!=",145				And => "&&",146				Or => "||",147			}148		)149	}150}151152/// name, default value153#[cfg_attr(feature = "serialize", derive(Serialize))]154#[cfg_attr(feature = "deserialize", derive(Deserialize))]155#[derive(Debug, PartialEq)]156pub struct Param(pub IStr, pub Option<LocExpr>);157158/// Defined function parameters159#[cfg_attr(feature = "serialize", derive(Serialize))]160#[cfg_attr(feature = "deserialize", derive(Deserialize))]161#[derive(Debug, Clone, PartialEq)]162pub struct ParamsDesc(pub Rc<Vec<Param>>);163impl Deref for ParamsDesc {164	type Target = Vec<Param>;165	fn deref(&self) -> &Self::Target {166		&self.0167	}168}169170#[cfg_attr(feature = "serialize", derive(Serialize))]171#[cfg_attr(feature = "deserialize", derive(Deserialize))]172#[derive(Debug, PartialEq)]173pub struct Arg(pub Option<String>, pub LocExpr);174175#[cfg_attr(feature = "serialize", derive(Serialize))]176#[cfg_attr(feature = "deserialize", derive(Deserialize))]177#[derive(Debug, PartialEq)]178pub struct ArgsDesc(pub Vec<Arg>);179impl Deref for ArgsDesc {180	type Target = Vec<Arg>;181	fn deref(&self) -> &Self::Target {182		&self.0183	}184}185186#[cfg_attr(feature = "serialize", derive(Serialize))]187#[cfg_attr(feature = "deserialize", derive(Deserialize))]188#[derive(Debug, Clone, PartialEq)]189pub struct BindSpec {190	pub name: IStr,191	pub params: Option<ParamsDesc>,192	pub value: LocExpr,193}194195#[cfg_attr(feature = "serialize", derive(Serialize))]196#[cfg_attr(feature = "deserialize", derive(Deserialize))]197#[derive(Debug, PartialEq)]198pub struct IfSpecData(pub LocExpr);199200#[cfg_attr(feature = "serialize", derive(Serialize))]201#[cfg_attr(feature = "deserialize", derive(Deserialize))]202#[derive(Debug, PartialEq)]203pub struct ForSpecData(pub IStr, pub LocExpr);204205#[cfg_attr(feature = "serialize", derive(Serialize))]206#[cfg_attr(feature = "deserialize", derive(Deserialize))]207#[derive(Debug, PartialEq)]208pub enum CompSpec {209	IfSpec(IfSpecData),210	ForSpec(ForSpecData),211}212213#[cfg_attr(feature = "serialize", derive(Serialize))]214#[cfg_attr(feature = "deserialize", derive(Deserialize))]215#[derive(Debug, PartialEq)]216pub struct ObjComp {217	pub pre_locals: Vec<BindSpec>,218	pub key: LocExpr,219	pub value: LocExpr,220	pub post_locals: Vec<BindSpec>,221	pub compspecs: Vec<CompSpec>,222}223224#[cfg_attr(feature = "serialize", derive(Serialize))]225#[cfg_attr(feature = "deserialize", derive(Deserialize))]226#[derive(Debug, PartialEq)]227pub enum ObjBody {228	MemberList(Vec<Member>),229	ObjComp(ObjComp),230}231232#[cfg_attr(feature = "serialize", derive(Serialize))]233#[cfg_attr(feature = "deserialize", derive(Deserialize))]234#[derive(Debug, PartialEq, Clone, Copy)]235pub enum LiteralType {236	This,237	Super,238	Dollar,239	Null,240	True,241	False,242}243244#[derive(Debug, PartialEq)]245pub struct SliceDesc {246	pub start: Option<LocExpr>,247	pub end: Option<LocExpr>,248	pub step: Option<LocExpr>,249}250251/// Syntax base252#[cfg_attr(feature = "serialize", derive(Serialize))]253#[cfg_attr(feature = "deserialize", derive(Deserialize))]254#[derive(Debug, PartialEq)]255pub enum Expr {256	Literal(LiteralType),257258	/// String value: "hello"259	Str(IStr),260	/// Number: 1, 2.0, 2e+20261	Num(f64),262	/// Variable name: test263	Var(IStr),264265	/// Array of expressions: [1, 2, "Hello"]266	Arr(Vec<LocExpr>),267	/// Array comprehension:268	/// ```jsonnet269	///  ingredients: [270	///    { kind: kind, qty: 4 / 3 }271	///    for kind in [272	///      'Honey Syrup',273	///      'Lemon Juice',274	///      'Farmers Gin',275	///    ]276	///  ],277	/// ```278	ArrComp(LocExpr, Vec<CompSpec>),279280	/// Object: {a: 2}281	Obj(ObjBody),282	/// Object extension: var1 {b: 2}283	ObjExtend(LocExpr, ObjBody),284285	/// (obj)286	Parened(LocExpr),287288	/// -2289	UnaryOp(UnaryOpType, LocExpr),290	/// 2 - 2291	BinaryOp(LocExpr, BinaryOpType, LocExpr),292	/// assert 2 == 2 : "Math is broken"293	AssertExpr(AssertStmt, LocExpr),294	/// local a = 2; { b: a }295	LocalExpr(Vec<BindSpec>, LocExpr),296297	/// import "hello"298	Import(PathBuf),299	/// importStr "file.txt"300	ImportStr(PathBuf),301	/// error "I'm broken"302	ErrorStmt(LocExpr),303	/// a(b, c)304	Apply(LocExpr, ArgsDesc, bool),305	/// a[b]306	Index(LocExpr, LocExpr),307	/// function(x) x308	Function(ParamsDesc, LocExpr),309	/// std.primitiveEquals310	Intrinsic(IStr),311	/// if true == false then 1 else 2312	IfElse {313		cond: IfSpecData,314		cond_then: LocExpr,315		cond_else: Option<LocExpr>,316	},317}318319/// file, begin offset, end offset320#[cfg_attr(feature = "serialize", derive(Serialize))]321#[cfg_attr(feature = "deserialize", derive(Deserialize))]322#[derive(Clone, PartialEq)]323pub struct ExprLocation(pub Rc<PathBuf>, pub usize, pub usize);324impl Debug for ExprLocation {325	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {326		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)327	}328}329330/// Holds AST expression and its location in source file331#[cfg_attr(feature = "serialize", derive(Serialize))]332#[cfg_attr(feature = "deserialize", derive(Deserialize))]333#[derive(Clone, PartialEq)]334pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);335impl Debug for LocExpr {336	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {337		if f.alternate() {338			write!(f, "{:#?}", self.0)?;339		} else {340			write!(f, "{:?}", self.0)?;341		}342		if let Some(loc) = &self.1 {343			write!(f, " from {:?}", loc)?;344		}345		Ok(())346	}347}348349/// Creates LocExpr from Expr and ExprLocation components350#[macro_export]351macro_rules! loc_expr {352	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {353		LocExpr(354			std::rc::Rc::new($expr),355			if $need_loc {356				Some(ExprLocation($name, $start, $end))357			} else {358				None359			},360		)361	};362}363364/// Creates LocExpr without location info365#[macro_export]366macro_rules! loc_expr_todo {367	($expr:expr) => {368		LocExpr(Rc::new($expr), None)369	};370}
modifiedflake.nixdiffbeforeafterboth
--- 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; });
 }