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
120 key: LocExpr,120 key: LocExpr,
121 value: LocExpr,121 value: LocExpr,
122 post_locals: Vec<BindSpec>,122 post_locals: Vec<BindSpec>,
123 rest: Vec<CompSpec>,123 compspecs: Vec<CompSpec>,
124 },124 },
125}125}
126126
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)}