Skip to content

Commit 5a75f0f

Browse files
l46kokcopybara-github
authored andcommitted
Plan specialized call for Conditional
PiperOrigin-RevId: 839399795
1 parent 33a4cb0 commit 5a75f0f

File tree

4 files changed

+124
-2
lines changed

4 files changed

+124
-2
lines changed

runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ java_library(
1616
":attribute",
1717
":eval_and",
1818
":eval_attribute",
19+
":eval_conditional",
1920
":eval_const",
2021
":eval_create_list",
2122
":eval_create_map",
@@ -168,6 +169,18 @@ java_library(
168169
],
169170
)
170171

172+
java_library(
173+
name = "eval_conditional",
174+
srcs = ["EvalConditional.java"],
175+
deps = [
176+
"//runtime:evaluation_exception",
177+
"//runtime:evaluation_listener",
178+
"//runtime:function_resolver",
179+
"//runtime:interpretable",
180+
"@maven//:com_google_guava_guava",
181+
],
182+
)
183+
171184
java_library(
172185
name = "eval_create_struct",
173186
srcs = ["EvalCreateStruct.java"],
@@ -188,7 +201,7 @@ java_library(
188201
name = "eval_create_list",
189202
srcs = ["EvalCreateList.java"],
190203
deps = [
191-
"//runtime",
204+
"//runtime:evaluation_exception",
192205
"//runtime:evaluation_listener",
193206
"//runtime:function_resolver",
194207
"//runtime:interpretable",
@@ -201,7 +214,7 @@ java_library(
201214
name = "eval_create_map",
202215
srcs = ["EvalCreateMap.java"],
203216
deps = [
204-
"//runtime",
217+
"//runtime:evaluation_exception",
205218
"//runtime:evaluation_listener",
206219
"//runtime:function_resolver",
207220
"//runtime:interpretable",
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.runtime.planner;
16+
17+
import com.google.common.base.Preconditions;
18+
import dev.cel.runtime.CelEvaluationException;
19+
import dev.cel.runtime.CelEvaluationListener;
20+
import dev.cel.runtime.CelFunctionResolver;
21+
import dev.cel.runtime.GlobalResolver;
22+
import dev.cel.runtime.Interpretable;
23+
24+
final class EvalConditional implements Interpretable {
25+
26+
@SuppressWarnings("Immutable")
27+
private final Interpretable[] args;
28+
29+
@Override
30+
public Object eval(GlobalResolver resolver) throws CelEvaluationException {
31+
Interpretable condition = args[0];
32+
Interpretable truthy = args[1];
33+
Interpretable falsy = args[2];
34+
// TODO: Handle unknowns
35+
Object condResult = condition.eval(resolver);
36+
if (!(condResult instanceof Boolean)) {
37+
throw new IllegalArgumentException(
38+
String.format("Expected boolean value, found :%s", condResult));
39+
}
40+
41+
// TODO: Handle exhaustive eval
42+
if ((boolean) condResult) {
43+
return truthy.eval(resolver);
44+
}
45+
46+
return falsy.eval(resolver);
47+
}
48+
49+
@Override
50+
public Object eval(GlobalResolver resolver, CelEvaluationListener listener) {
51+
// TODO: Implement support
52+
throw new UnsupportedOperationException("Not yet supported");
53+
}
54+
55+
@Override
56+
public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver) {
57+
// TODO: Implement support
58+
throw new UnsupportedOperationException("Not yet supported");
59+
}
60+
61+
@Override
62+
public Object eval(
63+
GlobalResolver resolver,
64+
CelFunctionResolver lateBoundFunctionResolver,
65+
CelEvaluationListener listener) {
66+
// TODO: Implement support
67+
throw new UnsupportedOperationException("Not yet supported");
68+
}
69+
70+
static EvalConditional create(Interpretable[] args) {
71+
return new EvalConditional(args);
72+
}
73+
74+
private EvalConditional(Interpretable[] args) {
75+
Preconditions.checkArgument(args.length == 3);
76+
this.args = args;
77+
}
78+
}

runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ private Interpretable planCall(CelExpr expr, PlannerContext ctx) {
177177
return EvalOr.create(evaluatedArgs);
178178
case LOGICAL_AND:
179179
return EvalAnd.create(evaluatedArgs);
180+
case CONDITIONAL:
181+
return EvalConditional.create(evaluatedArgs);
180182
default:
181183
// fall-through
182184
}

runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,35 @@ public void plan_call_logicalAnd_throws(String expression) throws Exception {
526526
assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.DIVIDE_BY_ZERO);
527527
}
528528

529+
@Test
530+
@TestParameters("{expression: 'false ? (1 / 0) > 2 : false', expectedResult: false}")
531+
@TestParameters("{expression: 'false ? (1 / 0) > 2 : true', expectedResult: true}")
532+
@TestParameters("{expression: 'true ? false : (1 / 0) > 2', expectedResult: false}")
533+
@TestParameters("{expression: 'true ? true : (1 / 0) > 2', expectedResult: true}")
534+
public void plan_call_conditional_shortCircuit(String expression, boolean expectedResult)
535+
throws Exception {
536+
CelAbstractSyntaxTree ast = compile(expression);
537+
Program program = PLANNER.plan(ast);
538+
539+
boolean result = (boolean) program.eval();
540+
541+
assertThat(result).isEqualTo(expectedResult);
542+
}
543+
544+
@Test
545+
@TestParameters("{expression: '(1 / 0) > 2 ? true : true'}")
546+
@TestParameters("{expression: 'true ? (1 / 0) > 2 : true'}")
547+
@TestParameters("{expression: 'false ? true : (1 / 0) > 2'}")
548+
public void plan_call_conditional_throws(String expression) throws Exception {
549+
CelAbstractSyntaxTree ast = compile(expression);
550+
Program program = PLANNER.plan(ast);
551+
552+
CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval);
553+
assertThat(e).hasMessageThat().isEqualTo("evaluation error: / by zero");
554+
assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class);
555+
assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.DIVIDE_BY_ZERO);
556+
}
557+
529558
private CelAbstractSyntaxTree compile(String expression) throws Exception {
530559
CelAbstractSyntaxTree ast = CEL_COMPILER.parse(expression).getAst();
531560
if (isParseOnly) {

0 commit comments

Comments
 (0)