Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Unary-test expression with null value #689

Merged
merged 3 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -356,21 +356,14 @@ class FeelInterpreter {
}

private def unaryOpDual(
x: Val,
y: Val,
c: (Val, Val, Val) => Boolean,
f: Boolean => Val)(implicit context: EvalContext): Val =
x: Val,
y: Val,
c: (Val, Val, Val) => Boolean,
f: Boolean => Val)(implicit context: EvalContext): Val =
withVal(
input,
_ match {
case ValNull => f(false)
case _ if (x == ValNull || y == ValNull) => f(false)
case i if (!i.isComparable) => ValError(s"$i is not comparable")
case _ if (!x.isComparable) => ValError(s"$x is not comparable")
case _ if (!y.isComparable) => ValError(s"$y is not comparable")
case i if (i.getClass != x.getClass) =>
ValError(s"$i can not be compared to $x")
case i if (i.getClass != y.getClass) =>
input, {
case i if (!i.isComparable || !x.isComparable || !y.isComparable) => ValNull
case i if (i.getClass != x.getClass || i.getClass != y.getClass) =>
ValError(s"$i can not be compared to $y")
case i => f(c(i, x, y))
}
Expand Down
10 changes: 8 additions & 2 deletions src/main/scala/org/camunda/feel/impl/parser/FeelParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,15 @@ object FeelParser {
case tests => AtLeastOne(tests.toList)
}

// boolean literals are ambiguous for unary-tests. give precedence to comparison with input.
// Expressions are a subset of positive-unary-test. However, we need to give precedence to the
// following cases to avoid ambiguous behavior:
// - comparison with a boolean (e.g. `true`, `false`)
// - comparison with less or greater than (e.g. `< 3`, `>= 5`)
// - comparison with an interval (e.g. `[2..5]`)
private def positiveUnaryTest[_: P]: P[Exp] =
(boolean.map(InputEqualTo) ~ End) | expression.map(UnaryTestExpression)
(boolean.map(InputEqualTo) ~ End) |
simplePositiveUnaryTest |
expression.map(UnaryTestExpression)

private def anyInput[_: P]: P[Exp] =
P(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class InterpreterNumberExpressionTest
eval("null < 2") should be(ValNull)
}

ignore should "compare with 'null in'" in {
it should "compare null with 'in' operator" in {
eval("null in < 2") should be(ValNull)
eval("null in (2..4)") should be(ValNull)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,14 @@ class InterpreterUnaryTest
evalUnaryTests(null, "3") should be(ValBoolean(false))
}

ignore should "compare to null with < or >" in {
it should "compare null with less/greater than" in {
evalUnaryTests(null, "< 3") should be(ValNull)
evalUnaryTests(null, "<= 3") should be(ValNull)
evalUnaryTests(null, "> 3") should be(ValNull)
evalUnaryTests(null, ">= 3") should be(ValNull)
}

it should "compare null with interval" in {
evalUnaryTests(null, "(0..10)") should be(ValNull)
}

Expand Down