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.rsdiffbeforeafterboth446 || format!("variable <{name}> access"),446 || format!("variable <{name}> access"),447 || ctx.binding(name.clone())?.evaluate(),447 || ctx.binding(name.clone())?.evaluate(),448 )?,448 )?,449 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {449 Index {450 indexable: LocExpr(v, _),451 index,452 #[cfg(feature = "exp-null-coaelse")]453 null_coaelse,454 } if matches!(&**v, Expr::Literal(LiteralType::Super)) => {450 let name = evaluate(ctx.clone(), index)?;455 let name = evaluate(ctx.clone(), index)?;451 let Val::Str(name) = name else {456 let Val::Str(name) = name else {452 throw!(ValueIndexMustBeTypeGot(457 throw!(ValueIndexMustBeTypeGot(455 name.value_type(),460 name.value_type(),456 ))461 ))457 };462 };458 ctx.super_obj()463 let Some(super_obj) = ctx.super_obj() else {464 throw!(NoSuperFound)465 };466 let this = ctx459 .expect("no super found")467 .this()468 .expect("no this found, while super present, should not happen");460 .get_for(name.into_flat(), ctx.this().expect("no this found").clone())?469 let key = name.into_flat();461 .expect("value not found")470 match super_obj.get_for(key.clone(), this.clone())? {471 Some(v) => v,472 #[cfg(feature = "exp-null-coaelse")]473 None if *null_coaelse => Val::Null,474 None => {475 let suggestions = suggest_object_fields(super_obj, key.clone());476477 throw!(NoSuchField(key, suggestions))478 }479 }462 }480 }463 Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {481 Index {482 indexable,483 index,484 #[cfg(feature = "exp-null-coaelse")]485 null_coaelse,486 } => match (evaluate(ctx.clone(), indexable)?, evaluate(ctx, index)?) {464 (Val::Obj(v), Val::Str(key)) => State::push(487 (Val::Obj(v), Val::Str(key)) => State::push(465 CallLocation::new(loc),488 CallLocation::new(loc),466 || format!("field <{key}> access"),489 || format!("field <{key}> access"),467 || match v.get(key.clone().into_flat()) {490 || match v.get(key.clone().into_flat()) {468 Ok(Some(v)) => Ok(v),491 Ok(Some(v)) => Ok(v),492 #[cfg(feature = "exp-null-coaelse")]493 Ok(None) if *null_coaelse => Ok(Val::Null),469 Ok(None) => {494 Ok(None) => {470 let suggestions = suggest_object_fields(&v, key.clone().into_flat());495 let suggestions = suggest_object_fields(&v, key.clone().into_flat());471496514 ValType::Num,539 ValType::Num,515 n.value_type(),540 n.value_type(),516 )),541 )),542 #[cfg(feature = "exp-null-coaelse")]543 (Val::Null, _) if *null_coaelse => Val::Null,517544518 (v, _) => throw!(CantIndexInto(v.value_type())),545 (v, _) => throw!(CantIndexInto(v.value_type())),519 },546 },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.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -122,6 +122,8 @@
And,
Or,
+ #[cfg(feature = "exp-null-coaelse")]
+ NullCoaelse,
// Equialent to std.objectHasEx(a, b, true)
In,
@@ -153,6 +155,8 @@
And => "&&",
Or => "||",
In => "in",
+ #[cfg(feature = "exp-null-coaelse")]
+ NullCoaelse => "??",
}
)
}
@@ -399,8 +403,13 @@
ErrorStmt(LocExpr),
/// a(b, c)
Apply(LocExpr, ArgsDesc, bool),
- /// a[b]
- Index(LocExpr, LocExpr),
+ /// a[b], a.b, a?.b
+ Index {
+ indexable: LocExpr,
+ index: LocExpr,
+ #[cfg(feature = "exp-null-coaelse")]
+ null_coaelse: bool,
+ },
/// function(x) x
Function(ParamsDesc, LocExpr),
/// if true == false then 1 else 2
crates/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)}
--