git.delta.rocks / jrsonnet / refs/commits / 9f3d17001fc1

difftreelog

feat obj comp support

Лач2020-06-11parent: #abdb122.patch.diff
in: master

5 files changed

modifiedcrates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -14,6 +14,8 @@
 
 	UndefinedExternalVariable(String),
 
+	FieldMustBeStringGot(ValType),
+
 	RuntimeError(String),
 	StackOverflow,
 	FractionalIndex,
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,6 +1,6 @@
 use crate::{
 	context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context,
-	ContextCreator, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
+	ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
 };
 use closure::closure;
 use jsonnet_parser::{
@@ -167,13 +167,14 @@
 future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);
 future_wrapper!(ObjValue, FutureObjValue);
 
-pub fn evaluate_comp(
+#[inline(always)]
+pub fn evaluate_comp<T>(
 	context: Context,
-	value: &LocExpr,
+	value: &impl Fn(Context) -> Result<T>,
 	specs: &[CompSpec],
-) -> Result<Option<Vec<Val>>> {
+) -> Result<Option<Vec<T>>> {
 	Ok(match specs.get(0) {
-		None => Some(vec![evaluate(context, &value)?]),
+		None => Some(vec![value(context)?]),
 		Some(CompSpec::IfSpec(IfSpecData(cond))) => {
 			if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {
 				evaluate_comp(context, value, &specs[1..])?
@@ -193,7 +194,7 @@
 							&specs[1..],
 						)?);
 					}
-					Some(out.iter().flatten().flatten().cloned().collect())
+					Some(out.into_iter().flatten().flatten().collect())
 				}
 				_ => panic!("for expression evaluated to non-iterable value"),
 			}
@@ -208,7 +209,7 @@
 			let new_bindings = FutureNewBindings::new();
 			let future_this = FutureObjValue::new();
 			let context_creator = context_creator!(
-				closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+				closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
 					Ok(context.clone().extend_unbound(
 						new_bindings.clone().unwrap(),
 						context.clone().dollar().clone().or_else(||this.clone()),
@@ -301,7 +302,70 @@
 			}
 			future_this.fill(ObjValue::new(None, Rc::new(new_members)))
 		}
-		_ => todo!(),
+		ObjBody::ObjComp {
+			pre_locals,
+			key,
+			value,
+			post_locals,
+			compspecs,
+		} => {
+			let future_this = FutureObjValue::new();
+			let mut new_members = BTreeMap::new();
+			for (k, v) in evaluate_comp(
+				context.clone(),
+				&|ctx| {
+					let new_bindings = FutureNewBindings::new();
+					let context_creator = context_creator!(
+						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+							Ok(context.clone().extend_unbound(
+								new_bindings.clone().unwrap(),
+								context.clone().dollar().clone().or_else(||this.clone()),
+								None,
+								super_obj
+							)?)
+						})
+					);
+					let mut bindings: HashMap<String, LazyBinding> = HashMap::new();
+					for (n, b) in pre_locals
+						.iter()
+						.chain(post_locals.iter())
+						.map(|b| evaluate_binding(b, context_creator.clone()))
+					{
+						bindings.insert(n, b);
+					}
+					let bindings = new_bindings.fill(bindings);
+					let ctx = ctx.extend_unbound(bindings, None, None, None)?;
+					let key = evaluate(ctx.clone(), &key)?;
+					let value = LazyBinding::Bindable(Rc::new(
+						closure!(clone ctx, clone value, |this, _super_obj| {
+							Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))
+						}),
+					));
+
+					Ok((key, value))
+				},
+				&compspecs,
+			)?
+			.unwrap()
+			{
+				match k {
+					Val::Null => {}
+					Val::Str(n) => {
+						new_members.insert(
+							n,
+							ObjMember {
+								add: false,
+								visibility: Visibility::Normal,
+								invoke: v,
+							},
+						);
+					}
+					v => create_error(Error::FieldMustBeStringGot(v.value_type()?))?,
+				}
+			}
+
+			future_this.fill(ObjValue::new(None, Rc::new(new_members)))
+		}
 	})
 }
 
@@ -405,7 +469,7 @@
 		}
 		ArrComp(expr, compspecs) => Val::Arr(
 			// First compspec should be forspec, so no "None" possible here
-			evaluate_comp(context, expr, compspecs)?.unwrap(),
+			evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap(),
 		),
 		Obj(body) => Val::Obj(evaluate_object(context, body.clone())?),
 		ObjExtend(s, t) => evaluate_add_op(
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -493,6 +493,14 @@
 	}
 
 	#[test]
+	fn object_comp() {
+		assert_json!(
+			r#"{local t = "a", ["h"+i+"_"+z]: if "h"+(i-1)+"_"+z in self then t+1 else 0+t for i in [1,2,3] for z in [2,3,4] if z != i}"#,
+			"{\"h1_2\": \"0a\",\"h1_3\": \"0a\",\"h1_4\": \"0a\",\"h2_3\": \"a1\",\"h2_4\": \"a1\",\"h3_2\": \"0a\",\"h3_4\": \"a1\"}"
+		)
+	}
+
+	#[test]
 	fn direct_self() {
 		println!(
 			"{:#?}",
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jsonnet-parser/src/expr.rs
1use serde::{Deserialize, Serialize};2use std::{fmt::Debug, ops::Deref, path::PathBuf, rc::Rc};34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]5pub enum FieldName {6	/// {fixed: 2}7	Fixed(String),8	/// {["dyn"+"amic"]: 3}9	Dyn(LocExpr),10}1112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]13pub enum Visibility {14	/// :15	Normal,16	/// ::17	Hidden,18	/// :::19	Unhide,20}2122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]23pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]26pub struct FieldMember {27	pub name: FieldName,28	pub plus: bool,29	pub params: Option<ParamsDesc>,30	pub visibility: Visibility,31	pub value: LocExpr,32}3334#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]35pub enum Member {36	Field(FieldMember),37	BindStmt(BindSpec),38	AssertStmt(AssertStmt),39}4041#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]42pub enum UnaryOpType {43	Plus,44	Minus,45	BitNot,46	Not,47}4849#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]50pub enum BinaryOpType {51	Mul,52	Div,5354	Add,55	Sub,5657	Lhs,58	Rhs,5960	Lt,61	Gt,62	Lte,63	Gte,6465	BitAnd,66	BitOr,67	BitXor,6869	And,70	Or,71}7273/// name, default value74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]75pub struct Param(pub String, pub Option<LocExpr>);76/// Defined function parameters77#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]78pub struct ParamsDesc(pub Vec<Param>);79impl Deref for ParamsDesc {80	type Target = Vec<Param>;81	fn deref(&self) -> &Self::Target {82		&self.083	}84}8586#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]87pub struct Arg(pub Option<String>, pub LocExpr);88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]89pub struct ArgsDesc(pub Vec<Arg>);90impl Deref for ArgsDesc {91	type Target = Vec<Arg>;92	fn deref(&self) -> &Self::Target {93		&self.094	}95}9697#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]98pub struct BindSpec {99	pub name: String,100	pub params: Option<ParamsDesc>,101	pub value: LocExpr,102}103104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]105pub struct IfSpecData(pub LocExpr);106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]107pub struct ForSpecData(pub String, pub LocExpr);108109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]110pub enum CompSpec {111	IfSpec(IfSpecData),112	ForSpec(ForSpecData),113}114115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]116pub enum ObjBody {117	MemberList(Vec<Member>),118	ObjComp {119		pre_locals: Vec<BindSpec>,120		key: LocExpr,121		value: LocExpr,122		post_locals: Vec<BindSpec>,123		rest: Vec<CompSpec>,124	},125}126127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]128pub enum LiteralType {129	This,130	Super,131	Dollar,132	Null,133	True,134	False,135}136137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]138pub struct SliceDesc {139	pub start: Option<LocExpr>,140	pub end: Option<LocExpr>,141	pub step: Option<LocExpr>,142}143144/// Syntax base145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]146pub enum Expr {147	Literal(LiteralType),148149	/// String value: "hello"150	Str(String),151	/// Number: 1, 2.0, 2e+20152	Num(f64),153	/// Variable name: test154	Var(String),155156	/// Array of expressions: [1, 2, "Hello"]157	Arr(Vec<LocExpr>),158	/// Array comprehension:159	/// ```jsonnet160	///  ingredients: [161	///    { kind: kind, qty: 4 / 3 }162	///    for kind in [163	///      'Honey Syrup',164	///      'Lemon Juice',165	///      'Farmers Gin',166	///    ]167	///  ],168	/// ```169	ArrComp(LocExpr, Vec<CompSpec>),170171	/// Object: {a: 2}172	Obj(ObjBody),173	/// Object extension: var1 {b: 2}174	ObjExtend(LocExpr, ObjBody),175176	/// (obj)177	Parened(LocExpr),178179	/// Params in function definition180	/// hello, world, test = 2181	Params(ParamsDesc),182	/// Args in function call183	/// 2 + 2, 3, named = 6184	Args(ArgsDesc),185186	/// -2187	UnaryOp(UnaryOpType, LocExpr),188	/// 2 - 2189	BinaryOp(LocExpr, BinaryOpType, LocExpr),190	/// assert 2 == 2 : "Math is broken"191	AssertExpr(AssertStmt, LocExpr),192	/// local a = 2; { b: a }193	LocalExpr(Vec<BindSpec>, LocExpr),194195	/// a = 3196	Bind(BindSpec),197	/// import "hello"198	Import(PathBuf),199	/// importStr "file.txt"200	ImportStr(PathBuf),201	/// error "I'm broken"202	Error(LocExpr),203	/// a(b, c)204	Apply(LocExpr, ArgsDesc, bool),205	///206	Select(LocExpr, String),207	/// a[b]208	Index(LocExpr, LocExpr),209	/// a[1::2]210	Slice(LocExpr, SliceDesc),211	/// function(x) x212	Function(ParamsDesc, LocExpr),213	/// if true == false then 1 else 2214	IfElse {215		cond: IfSpecData,216		cond_then: LocExpr,217		cond_else: Option<LocExpr>,218	},219	/// if 2 = 3220	IfSpec(IfSpecData),221	/// for elem in array222	ForSpec(ForSpecData),223}224225/// file, begin offset, end offset226#[derive(Clone, PartialEq, Serialize, Deserialize)]227pub struct ExprLocation(pub PathBuf, pub usize, pub usize);228impl Debug for ExprLocation {229	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {230		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)231	}232}233234/// Holds AST expression and its location in source file+235#[derive(Clone, PartialEq, Serialize, Deserialize)]236pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);237impl Debug for LocExpr {238	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {239		write!(f, "{:?} from {:?}", self.0, self.1)240	}241}242243/// Creates LocExpr from Expr and ExprLocation components244#[macro_export]245macro_rules! loc_expr {246	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {247		LocExpr(248			std::rc::Rc::new($expr),249			if $need_loc {250				Some(std::rc::Rc::new(ExprLocation(251					$name.to_owned(),252					$start,253					$end,254				)))255			} else {256				None257				},258			)259	};260}261262/// Creates LocExpr without location info263#[macro_export]264macro_rules! loc_expr_todo {265	($expr:expr) => {266		LocExpr(Rc::new($expr), None)267	};268}
modifiedcrates/jsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -138,7 +138,7 @@
 					key,
 					value,
 					post_locals,
-					rest: [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat(),
+					compspecs: [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat(),
 				}
 			}
 			/ members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}