difftreelog
feat experimental null-coaelse operator
in: master
6 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -24,6 +24,8 @@
exp-object-iteration = []
# Bigint type
exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]
+# obj?.field, obj?.['field']
+exp-null-coaelse = ["jrsonnet-parser/exp-null-coaelse"]
# Improves performance, and implements some useful things using nightly-only features
nightly = ["hashbrown/nightly"]
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -446,7 +446,12 @@
|| format!("variable <{name}> access"),
|| ctx.binding(name.clone())?.evaluate(),
)?,
- Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {
+ Index {
+ indexable: LocExpr(v, _),
+ index,
+ #[cfg(feature = "exp-null-coaelse")]
+ null_coaelse,
+ } if matches!(&**v, Expr::Literal(LiteralType::Super)) => {
let name = evaluate(ctx.clone(), index)?;
let Val::Str(name) = name else {
throw!(ValueIndexMustBeTypeGot(
@@ -455,17 +460,37 @@
name.value_type(),
))
};
- ctx.super_obj()
- .expect("no super found")
- .get_for(name.into_flat(), ctx.this().expect("no this found").clone())?
- .expect("value not found")
+ let Some(super_obj) = ctx.super_obj() else {
+ throw!(NoSuperFound)
+ };
+ let this = ctx
+ .this()
+ .expect("no this found, while super present, should not happen");
+ let key = name.into_flat();
+ match super_obj.get_for(key.clone(), this.clone())? {
+ Some(v) => v,
+ #[cfg(feature = "exp-null-coaelse")]
+ None if *null_coaelse => Val::Null,
+ None => {
+ let suggestions = suggest_object_fields(super_obj, key.clone());
+
+ throw!(NoSuchField(key, suggestions))
+ }
+ }
}
- Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {
+ Index {
+ indexable,
+ index,
+ #[cfg(feature = "exp-null-coaelse")]
+ null_coaelse,
+ } => match (evaluate(ctx.clone(), indexable)?, evaluate(ctx, index)?) {
(Val::Obj(v), Val::Str(key)) => State::push(
CallLocation::new(loc),
|| format!("field <{key}> access"),
|| match v.get(key.clone().into_flat()) {
Ok(Some(v)) => Ok(v),
+ #[cfg(feature = "exp-null-coaelse")]
+ Ok(None) if *null_coaelse => Ok(Val::Null),
Ok(None) => {
let suggestions = suggest_object_fields(&v, key.clone().into_flat());
@@ -514,6 +539,8 @@
ValType::Num,
n.value_type(),
)),
+ #[cfg(feature = "exp-null-coaelse")]
+ (Val::Null, _) if *null_coaelse => Val::Null,
(v, _) => throw!(CantIndexInto(v.value_type())),
},
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -88,6 +88,10 @@
Ok(match (evaluate(ctx.clone(), a)?, op, b) {
(Bool(true), Or, _o) => Val::Bool(true),
(Bool(false), And, _o) => Val::Bool(false),
+ #[cfg(feature = "exp-null-coaelse")]
+ (Null, NullCoaelse, eb) => evaluate(ctx, eb)?,
+ #[cfg(feature = "exp-null-coaelse")]
+ (a, NullCoaelse, _o) => a,
(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,
})
}
crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -10,6 +10,7 @@
[features]
default = []
exp-destruct = []
+exp-null-coaelse = []
# Implement serialization of AST using structdump
#
# Structdump generates code, which exactly replicated passed AST
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth122122123 And,123 And,124 Or,124 Or,125 #[cfg(feature = "exp-null-coaelse")]126 NullCoaelse,125127126 // Equialent to std.objectHasEx(a, b, true)128 // Equialent to std.objectHasEx(a, b, true)127 In,129 In,153 And => "&&",155 And => "&&",154 Or => "||",156 Or => "||",155 In => "in",157 In => "in",158 #[cfg(feature = "exp-null-coaelse")]159 NullCoaelse => "??",156 }160 }157 )161 )158 }162 }399 ErrorStmt(LocExpr),403 ErrorStmt(LocExpr),400 /// a(b, c)404 /// a(b, c)401 Apply(LocExpr, ArgsDesc, bool),405 Apply(LocExpr, ArgsDesc, bool),402 /// a[b]406 /// a[b], a.b, a?.b403 Index(LocExpr, LocExpr),407 Index {408 indexable: LocExpr,409 index: LocExpr,410 #[cfg(feature = "exp-null-coaelse")]411 null_coaelse: bool,412 },404 /// function(x) x413 /// function(x) x405 Function(ParamsDesc, LocExpr),414 Function(ParamsDesc, LocExpr),406 /// if true == false then 1 else 2415 /// if true == false then 1 else 2crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -288,7 +288,11 @@
rule unaryop(x: rule<()>) -> ()
= quiet!{ x() } / expected!("<unary op>")
-
+ rule ensure_null_coaelse()
+ = "" {?
+ #[cfg(not(feature = "exp-null-coaelse"))] return Err("!!!experimental null coaelscing was not enabled");
+ #[cfg(feature = "exp-null-coaelse")] Ok(())
+ }
use BinaryOpType::*;
use UnaryOpType::*;
rule expr(s: &ParserSettings) -> LocExpr
@@ -296,6 +300,10 @@
start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.source.clone(), start as u32, end as u32)) }
--
a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
+ a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ {
+ #[cfg(feature = "exp-null-coaelse")] return expr_bin!(a NullCoaelse b);
+ unreachable!("ensure_null_coaelse will fail if feature is not enabled")
+ }
--
a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}
--
@@ -329,8 +337,16 @@
unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
--
a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}
- a:(@) _ "." _ a:position!() e:id_loc(s) b:position!() {Expr::Index(a, e)}
- a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}
+ indexable:(@) _ null_coaelse:("?" _ ensure_null_coaelse())? "." _ index:id_loc(s) {Expr::Index{
+ indexable, index,
+ #[cfg(feature = "exp-null-coaelse")]
+ null_coaelse: null_coaelse.is_some(),
+ }}
+ indexable:(@) _ null_coaelse:("?" _ "." _ ensure_null_coaelse())? "[" _ index:expr(s) _ "]" {Expr::Index{
+ indexable, index,
+ #[cfg(feature = "exp-null-coaelse")]
+ null_coaelse: null_coaelse.is_some(),
+ }}
a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}
a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}
--