difftreelog
feat experimental null-coaelse operator
in: master
6 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth24exp-object-iteration = []24exp-object-iteration = []25# Bigint type25# Bigint type26exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]26exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]27# obj?.field, obj?.['field']28exp-null-coaelse = ["jrsonnet-parser/exp-null-coaelse"]272928# Improves performance, and implements some useful things using nightly-only features30# Improves performance, and implements some useful things using nightly-only features29nightly = ["hashbrown/nightly"]31nightly = ["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.rsdiffbeforeafterboth88 Ok(match (evaluate(ctx.clone(), a)?, op, b) {88 Ok(match (evaluate(ctx.clone(), a)?, op, b) {89 (Bool(true), Or, _o) => Val::Bool(true),89 (Bool(true), Or, _o) => Val::Bool(true),90 (Bool(false), And, _o) => Val::Bool(false),90 (Bool(false), And, _o) => Val::Bool(false),91 #[cfg(feature = "exp-null-coaelse")]92 (Null, NullCoaelse, eb) => evaluate(ctx, eb)?,93 #[cfg(feature = "exp-null-coaelse")]94 (a, NullCoaelse, _o) => a,91 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,95 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,92 })96 })93}97}crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth10[features]10[features]11default = []11default = []12exp-destruct = []12exp-destruct = []13exp-null-coaelse = []13# Implement serialization of AST using structdump14# Implement serialization of AST using structdump14#15#15# Structdump generates code, which exactly replicated passed AST16# Structdump generates code, which exactly replicated passed ASTcrates/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.rsdiffbeforeafterboth288 rule unaryop(x: rule<()>) -> ()288 rule unaryop(x: rule<()>) -> ()289 = quiet!{ x() } / expected!("<unary op>")289 = quiet!{ x() } / expected!("<unary op>")290290291291 rule ensure_null_coaelse()292 = "" {?293 #[cfg(not(feature = "exp-null-coaelse"))] return Err("!!!experimental null coaelscing was not enabled");294 #[cfg(feature = "exp-null-coaelse")] Ok(())295 }292 use BinaryOpType::*;296 use BinaryOpType::*;293 use UnaryOpType::*;297 use UnaryOpType::*;294 rule expr(s: &ParserSettings) -> LocExpr298 rule expr(s: &ParserSettings) -> LocExpr295 = precedence! {299 = precedence! {296 start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.source.clone(), start as u32, end as u32)) }300 start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.source.clone(), start as u32, end as u32)) }297 --301 --298 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}302 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}303 a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ {304 #[cfg(feature = "exp-null-coaelse")] return expr_bin!(a NullCoaelse b);305 unreachable!("ensure_null_coaelse will fail if feature is not enabled")306 }299 --307 --300 a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}308 a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}301 --309 --329 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}337 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}330 --338 --331 a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}339 a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}332 a:(@) _ "." _ a:position!() e:id_loc(s) b:position!() {Expr::Index(a, e)}340 indexable:(@) _ null_coaelse:("?" _ ensure_null_coaelse())? "." _ index:id_loc(s) {Expr::Index{341 indexable, index,342 #[cfg(feature = "exp-null-coaelse")]343 null_coaelse: null_coaelse.is_some(),344 }}333 a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}345 indexable:(@) _ null_coaelse:("?" _ "." _ ensure_null_coaelse())? "[" _ index:expr(s) _ "]" {Expr::Index{346 indexable, index,347 #[cfg(feature = "exp-null-coaelse")]348 null_coaelse: null_coaelse.is_some(),349 }}334 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}350 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}335 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}351 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}336 --352 --