Add Null Coalescing Operator and Ternary Conditional Operator

Closes #1471
Closes #1439
This commit is contained in:
Tobie Morgan Hitchcock 2022-12-02 21:48:10 +00:00
parent 3d3cab4b65
commit 09021ee103
4 changed files with 108 additions and 14 deletions

View file

@ -19,6 +19,20 @@ pub fn and(a: Value, b: Value) -> Result<Value, Error> {
})
}
pub fn tco(a: Value, b: Value) -> Result<Value, Error> {
Ok(match a.is_truthy() {
true => a,
false => b,
})
}
pub fn nco(a: Value, b: Value) -> Result<Value, Error> {
Ok(match a.is_some() {
true => a,
false => b,
})
}
pub fn add(a: Value, b: Value) -> Result<Value, Error> {
Ok(a.add(b))
}
@ -200,6 +214,66 @@ mod tests {
assert_eq!("0", format!("{}", out));
}
#[test]
fn tco_true() {
let one = Value::from(1);
let two = Value::from(2);
let res = tco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn tco_false_one() {
let one = Value::from(0);
let two = Value::from(1);
let res = tco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn tco_false_two() {
let one = Value::from(1);
let two = Value::from(0);
let res = tco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn nco_true() {
let one = Value::from(1);
let two = Value::from(2);
let res = nco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn nco_false_one() {
let one = Value::None;
let two = Value::from(1);
let res = nco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn nco_false_two() {
let one = Value::from(1);
let two = Value::None;
let res = nco(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn add_basic() {
let one = Value::from(5);

View file

@ -76,12 +76,24 @@ impl Expression {
return Ok(l);
}
}
Operator::Tco => {
if let true = l.is_truthy() {
return Ok(l);
}
}
Operator::Nco => {
if let true = l.is_some() {
return Ok(l);
}
}
_ => {} // Continue
}
let r = self.r.compute(ctx, opt, txn, doc).await?;
match self.o {
Operator::Or => fnc::operate::or(l, r),
Operator::And => fnc::operate::and(l, r),
Operator::Tco => fnc::operate::tco(l, r),
Operator::Nco => fnc::operate::nco(l, r),
Operator::Add => fnc::operate::add(l, r),
Operator::Sub => fnc::operate::sub(l, r),
Operator::Mul => fnc::operate::mul(l, r),

View file

@ -11,8 +11,11 @@ use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
pub enum Operator {
//
Or, // ||
And, // &&
Tco, // ?: Ternary conditional operator
Nco, // ?? Null coalescing operator
//
Add, // +
Sub, // -
@ -21,9 +24,8 @@ pub enum Operator {
Inc, // +=
Dec, // -=
//
Exact, // ==
//
Equal, // =
Exact, // ==
NotEqual, // !=
AllEqual, // *=
AnyEqual, // ?=
@ -65,11 +67,13 @@ impl Operator {
match self {
Operator::Or => 1,
Operator::And => 2,
Operator::Sub => 4,
Operator::Add => 5,
Operator::Mul => 6,
Operator::Div => 7,
_ => 3,
Operator::Tco => 3,
Operator::Nco => 4,
Operator::Sub => 6,
Operator::Add => 7,
Operator::Mul => 8,
Operator::Div => 9,
_ => 5,
}
}
}
@ -79,14 +83,16 @@ impl fmt::Display for Operator {
f.write_str(match self {
Self::Or => "OR",
Self::And => "AND",
Self::Tco => "?:",
Self::Nco => "??",
Self::Add => "+",
Self::Sub => "-",
Self::Mul => "*",
Self::Div => "/",
Self::Inc => "+=",
Self::Dec => "-=",
Self::Exact => "==",
Self::Equal => "=",
Self::Exact => "==",
Self::NotEqual => "!=",
Self::AllEqual => "*=",
Self::AnyEqual => "?=",
@ -129,6 +135,12 @@ pub fn operator(i: &str) -> IResult<&str, Operator> {
pub fn symbols(i: &str) -> IResult<&str, Operator> {
let (i, _) = mightbespace(i)?;
let (i, v) = alt((
alt((
map(tag("||"), |_| Operator::Or),
map(tag("&&"), |_| Operator::And),
map(tag("?:"), |_| Operator::Tco),
map(tag("??"), |_| Operator::Nco),
)),
alt((
map(tag("=="), |_| Operator::Exact),
map(tag("!="), |_| Operator::NotEqual),
@ -178,12 +190,8 @@ pub fn phrases(i: &str) -> IResult<&str, Operator> {
let (i, _) = shouldbespace(i)?;
let (i, v) = alt((
alt((
map(tag_no_case("&&"), |_| Operator::And),
map(tag_no_case("AND"), |_| Operator::And),
map(tag_no_case("||"), |_| Operator::Or),
map(tag_no_case("OR"), |_| Operator::Or),
)),
alt((
map(tag_no_case("AND"), |_| Operator::And),
map(tag_no_case("IS NOT"), |_| Operator::NotEqual),
map(tag_no_case("IS"), |_| Operator::Equal),
)),

View file

@ -610,7 +610,7 @@ impl Value {
}
pub fn is_some(&self) -> bool {
!self.is_none()
!self.is_none() && !self.is_null()
}
pub fn is_true(&self) -> bool {