difftreelog
feat obj comp support
in: master
5 files changed
crates/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,
crates/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(
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth492 );492 );493 }493 }494495 #[test]496 fn object_comp() {497 assert_json!(498 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}"#,499 "{\"h1_2\": \"0a\",\"h1_3\": \"0a\",\"h1_4\": \"0a\",\"h2_3\": \"a1\",\"h2_4\": \"a1\",\"h3_2\": \"0a\",\"h3_4\": \"a1\"}"500 )501 }494502495 #[test]503 #[test]496 fn direct_self() {504 fn direct_self() {crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -120,7 +120,7 @@
key: LocExpr,
value: LocExpr,
post_locals: Vec<BindSpec>,
- rest: Vec<CompSpec>,
+ compspecs: Vec<CompSpec>,
},
}
crates/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)}