From 3ae34602eb264fd1dee6991fb2a9998ab1e9ef95 Mon Sep 17 00:00:00 2001 From: Roland Fredenhagen Date: Thu, 5 Jan 2023 20:00:40 +0100 Subject: [PATCH] Make some operators lazily evaluated (#627) * Make some operators lazily evaluated Fixes #624 * Add to CHANGELOG.md --- CHANGELOG.md | 1 + crates/simplexpr/src/eval.rs | 53 ++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb1fe5b..9c6daac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to eww will be listed here, starting at changes since versio ### Features - Add support for safe access (`?.`) in simplexpr (By: oldwomanjosiah) - Allow floating-point numbers in percentages for window-geometry +- Made `and`, `or` and `?:` lazily evaluated in simplexpr (By: ModProg) ## [0.4.0] (04.09.2022) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 8a75325..07c0066 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -179,35 +179,43 @@ impl SimplExpr { } SimplExpr::BinOp(span, a, op, b) => { let a = a.eval(values)?; - let b = b.eval(values)?; + let b = || b.eval(values); + // Lazy operators let dynval = match op { - BinOp::Equals => DynVal::from(a == b), - BinOp::NotEquals => DynVal::from(a != b), - BinOp::And => DynVal::from(a.as_bool()? && b.as_bool()?), - BinOp::Or => DynVal::from(a.as_bool()? || b.as_bool()?), - BinOp::Plus => match (a.as_f64(), b.as_f64()) { - (Ok(a), Ok(b)) => DynVal::from(a + b), - _ => DynVal::from(format!("{}{}", a.as_string()?, b.as_string()?)), - }, - BinOp::Minus => DynVal::from(a.as_f64()? - b.as_f64()?), - BinOp::Times => DynVal::from(a.as_f64()? * b.as_f64()?), - BinOp::Div => DynVal::from(a.as_f64()? / b.as_f64()?), - BinOp::Mod => DynVal::from(a.as_f64()? % b.as_f64()?), - BinOp::GT => DynVal::from(a.as_f64()? > b.as_f64()?), - BinOp::LT => DynVal::from(a.as_f64()? < b.as_f64()?), - BinOp::GE => DynVal::from(a.as_f64()? >= b.as_f64()?), - BinOp::LE => DynVal::from(a.as_f64()? <= b.as_f64()?), + BinOp::And => DynVal::from(a.as_bool()? && b()?.as_bool()?), + BinOp::Or => DynVal::from(a.as_bool()? || b()?.as_bool()?), BinOp::Elvis => { let is_null = matches!(serde_json::from_str(&a.0), Ok(serde_json::Value::Null)); if a.0.is_empty() || is_null { - b + b()? } else { a } } - BinOp::RegexMatch => { - let regex = regex::Regex::new(&b.as_string()?)?; - DynVal::from(regex.is_match(&a.as_string()?)) + // Eager operators + _ => { + let b = b()?; + match op { + BinOp::Equals => DynVal::from(a == b), + BinOp::NotEquals => DynVal::from(a != b), + BinOp::Plus => match (a.as_f64(), b.as_f64()) { + (Ok(a), Ok(b)) => DynVal::from(a + b), + _ => DynVal::from(format!("{}{}", a.as_string()?, b.as_string()?)), + }, + BinOp::Minus => DynVal::from(a.as_f64()? - b.as_f64()?), + BinOp::Times => DynVal::from(a.as_f64()? * b.as_f64()?), + BinOp::Div => DynVal::from(a.as_f64()? / b.as_f64()?), + BinOp::Mod => DynVal::from(a.as_f64()? % b.as_f64()?), + BinOp::GT => DynVal::from(a.as_f64()? > b.as_f64()?), + BinOp::LT => DynVal::from(a.as_f64()? < b.as_f64()?), + BinOp::GE => DynVal::from(a.as_f64()? >= b.as_f64()?), + BinOp::LE => DynVal::from(a.as_f64()? <= b.as_f64()?), + BinOp::RegexMatch => { + let regex = regex::Regex::new(&b.as_string()?)?; + DynVal::from(regex.is_match(&a.as_string()?)) + } + _ => unreachable!("Lazy operators already handled"), + } } }; Ok(dynval.at(*span)) @@ -395,5 +403,8 @@ mod tests { safe_access_to_missing(r#"{ "a": { "b": 2 } }.b?.b"#) => Ok(DynVal::from(&serde_json::Value::Null)), normal_access_to_existing(r#"{ "a": { "b": 2 } }.a.b"#) => Ok(DynVal::from(2)), normal_access_to_missing(r#"{ "a": { "b": 2 } }.b.b"#) => Err(super::EvalError::CannotIndex("null".to_string())), + lazy_evaluation_and(r#"false && "null".test"#) => Ok(DynVal::from(false)), + lazy_evaluation_or(r#"true || "null".test"#) => Ok(DynVal::from(true)), + lazy_evaluation_elvis(r#""test"?: "null".test"#) => Ok(DynVal::from("test")), } }