Skip to content

Commit 6df9988

Browse files
committed
Plan fold
1 parent 9f749de commit 6df9988

9 files changed

Lines changed: 341 additions & 6 deletions

File tree

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import dev.cel.common.types.CelType;
66
import dev.cel.common.types.CelTypeProvider;
77
import dev.cel.runtime.GlobalResolver;
8+
import java.util.stream.Collectors;
89

910
@Immutable
1011
interface Attribute {
@@ -22,7 +23,9 @@ public Object resolve(GlobalResolver ctx) {
2223
}
2324
}
2425

25-
throw new IllegalArgumentException("no such attribute(s): %s");
26+
throw new IllegalArgumentException(String.format("no such attribute(s): %s", attributes.stream()
27+
.map(Attribute::toString)
28+
.collect(Collectors.joining(","))));
2629
}
2730

2831
MaybeAttribute(ImmutableList<Attribute> attributes) {
@@ -49,7 +52,7 @@ public Object resolve(GlobalResolver ctx) {
4952
}
5053
}
5154

52-
throw new IllegalArgumentException("no such attribute(s): %s");
55+
throw new IllegalArgumentException(String.format("no such attribute(s): %s", String.join(",", namespacedNames)));
5356
}
5457

5558
NamespacedAttribute(

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ java_library(
2020
":eval_create_list",
2121
":eval_create_map",
2222
":eval_create_struct",
23+
":eval_fold",
2324
":eval_or",
2425
":eval_unary",
2526
":eval_var_args_call",
@@ -250,6 +251,23 @@ java_library(
250251
],
251252
)
252253

254+
java_library(
255+
name = "eval_fold",
256+
srcs = ["EvalFold.java"],
257+
deps = [
258+
":attribute",
259+
":cel_value_interpretable",
260+
"//common/values",
261+
"//common/values:cel_value",
262+
"//common/values:cel_value_provider",
263+
"//runtime:evaluation_exception",
264+
"//runtime:interpretable",
265+
"@maven//:com_google_errorprone_error_prone_annotations",
266+
"@maven//:com_google_guava_guava",
267+
"@maven//:org_jspecify_jspecify",
268+
],
269+
)
270+
253271
java_library(
254272
name = "eval_helpers",
255273
srcs = ["EvalHelpers.java"],
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package dev.cel.runtime.planner;
2+
3+
import com.google.errorprone.annotations.Immutable;
4+
import dev.cel.common.values.CelValue;
5+
import dev.cel.common.values.IntValue;
6+
import dev.cel.runtime.CelEvaluationException;
7+
import dev.cel.runtime.GlobalResolver;
8+
import java.util.Collection;
9+
import java.util.Iterator;
10+
import org.jspecify.annotations.Nullable;
11+
12+
@Immutable
13+
final class EvalFold implements CelValueInterpretable {
14+
15+
private final String accuVar;
16+
private final CelValueInterpretable accuInit;
17+
private final String iterVar;
18+
private final String iterVar2;
19+
private final CelValueInterpretable iterRange;
20+
private final CelValueInterpretable condition;
21+
private final CelValueInterpretable loopStep;
22+
private final CelValueInterpretable result;
23+
24+
@Override
25+
public CelValue eval(GlobalResolver resolver) throws CelEvaluationException {
26+
// TODO: Consider creating a folder abstraction like in cel-go. This requires some legwork in attribute qualification.
27+
Collection<CelValue> foldRange = (Collection<CelValue>) iterRange.eval(resolver);
28+
29+
Folder folder = new Folder(
30+
resolver,
31+
accuVar,
32+
iterVar,
33+
iterVar2
34+
);
35+
36+
folder.accuVal = accuInit.eval(folder);
37+
38+
// TODO: Implement scoping
39+
long index = 0;
40+
for (Iterator<CelValue> iterator = foldRange.iterator(); iterator.hasNext(); ) {
41+
if (iterVar2.isEmpty()) {
42+
folder.iterVarVal = iterator.next();
43+
} else {
44+
folder.iterVarVal = IntValue.create(index);
45+
folder.iterVar2Val = iterator.next();
46+
}
47+
folder.accuVal = loopStep.eval(folder);
48+
index++;
49+
}
50+
51+
CelValue resultValue = result.eval(folder);
52+
// CelValue resultValue = resolveName("@result", resolver);
53+
54+
return resultValue;
55+
}
56+
57+
private static class Folder implements GlobalResolver {
58+
private final GlobalResolver resolver;
59+
private final String accuVar;
60+
private final String iterVar;
61+
private final String iterVar2;
62+
63+
private CelValue iterVarVal;
64+
private CelValue iterVar2Val;
65+
private CelValue accuVal;
66+
67+
private Folder(
68+
GlobalResolver resolver,
69+
String accuVar,
70+
String iterVar,
71+
String iterVar2
72+
) {
73+
this.resolver = resolver;
74+
this.accuVar = accuVar;
75+
this.iterVar = iterVar;
76+
this.iterVar2 = iterVar2;
77+
}
78+
79+
@Override
80+
public @Nullable Object resolve(String name) {
81+
if (name.equals(accuVar)) {
82+
return accuVal;
83+
}
84+
85+
// Todo: !f.computeResult check
86+
if (name.equals(iterVar)) {
87+
return this.iterVarVal;
88+
}
89+
90+
if (name.equals(iterVar2)) {
91+
return this.iterVar2Val;
92+
}
93+
94+
return resolver.resolve(name);
95+
}
96+
}
97+
98+
static EvalFold create(
99+
String accuVar,
100+
CelValueInterpretable accuInit,
101+
String iterVar,
102+
String iterVar2,
103+
CelValueInterpretable iterRange,
104+
CelValueInterpretable condition,
105+
CelValueInterpretable loopStep,
106+
CelValueInterpretable result) {
107+
return new EvalFold(
108+
accuVar,
109+
accuInit,
110+
iterVar,
111+
iterVar2,
112+
iterRange,
113+
condition,
114+
loopStep,
115+
result
116+
);
117+
}
118+
119+
private EvalFold(
120+
String accuVar,
121+
CelValueInterpretable accuInit,
122+
String iterVar,
123+
String iterVar2,
124+
CelValueInterpretable iterRange,
125+
CelValueInterpretable condition,
126+
CelValueInterpretable loopStep,
127+
CelValueInterpretable result) {
128+
this.accuVar = accuVar;
129+
this.accuInit = accuInit;
130+
this.iterVar = iterVar;
131+
this.iterVar2 = iterVar2;
132+
this.iterRange = iterRange;
133+
this.condition = condition;
134+
this.loopStep = loopStep;
135+
this.result = result;
136+
}
137+
}

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import dev.cel.common.ast.CelConstant;
1111
import dev.cel.common.ast.CelExpr;
1212
import dev.cel.common.ast.CelExpr.CelCall;
13+
import dev.cel.common.ast.CelExpr.CelComprehension;
1314
import dev.cel.common.ast.CelExpr.CelList;
1415
import dev.cel.common.ast.CelExpr.CelMap;
1516
import dev.cel.common.ast.CelExpr.CelStruct;
@@ -60,7 +61,7 @@ private CelValueInterpretable plan(
6061
case MAP:
6162
return planCreateMap(celExpr, ctx);
6263
case COMPREHENSION:
63-
break;
64+
return planComprehension(celExpr, ctx);
6465
case NOT_SET:
6566
throw new UnsupportedOperationException("Unsupported kind: " + celExpr.getKind());
6667
}
@@ -137,7 +138,7 @@ private CelValueInterpretable planCall(CelExpr expr, PlannerContext ctx) {
137138
}
138139

139140
if (resolvedOverload == null) {
140-
resolvedOverload = dispatcher.findOverload(functionName).orElseThrow(() -> new NoSuchElementException("TODO: Overload not found"));
141+
resolvedOverload = dispatcher.findOverload(functionName).orElseThrow(() -> new NoSuchElementException("Overload not found: " + functionName));
141142
}
142143

143144
switch (argCount) {
@@ -199,6 +200,27 @@ private CelValueInterpretable planCreateMap(CelExpr celExpr, PlannerContext ctx)
199200
return EvalCreateMap.create(keys, values);
200201
}
201202

203+
private CelValueInterpretable planComprehension(CelExpr expr, PlannerContext ctx) {
204+
CelComprehension comprehension = expr.comprehension();
205+
206+
CelValueInterpretable accuInit = plan(comprehension.accuInit(), ctx);
207+
CelValueInterpretable iterRange = plan(comprehension.iterRange(), ctx);
208+
CelValueInterpretable loopCondition = plan(comprehension.loopCondition(), ctx);
209+
CelValueInterpretable loopStep = plan(comprehension.loopStep(), ctx);
210+
CelValueInterpretable result = plan(comprehension.result(), ctx);
211+
212+
return EvalFold.create(
213+
comprehension.accuVar(),
214+
accuInit,
215+
comprehension.iterVar(),
216+
comprehension.iterVar2(),
217+
iterRange,
218+
loopCondition,
219+
loopStep,
220+
result
221+
);
222+
}
223+
202224
/**
203225
* resolveFunction determines the call target, function name, and overload name (when unambiguous) from the given call expr.
204226
*/

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,42 @@ cel_android_library(
14191419
],
14201420
)
14211421

1422+
java_library(
1423+
name = "not_strictly_false",
1424+
srcs = ["NotStrictlyFalseFunction.java"],
1425+
tags = [
1426+
],
1427+
deps = [
1428+
":standard_overload",
1429+
"//common:error_codes",
1430+
"//common:options",
1431+
"//common:runtime_exception",
1432+
"//runtime:function_binding",
1433+
"//runtime:runtime_equality",
1434+
"//runtime:runtime_helpers",
1435+
"//runtime/standard:standard_function",
1436+
"@maven//:com_google_guava_guava",
1437+
],
1438+
)
1439+
1440+
cel_android_library(
1441+
name = "not_strictly_false_android",
1442+
srcs = ["NotStrictlyFalseFunction.java"],
1443+
tags = [
1444+
],
1445+
deps = [
1446+
":standard_function_android",
1447+
":standard_overload_android",
1448+
"//common:error_codes",
1449+
"//common:options",
1450+
"//common:runtime_exception",
1451+
"//runtime:function_binding_android",
1452+
"//runtime:runtime_equality_android",
1453+
"//runtime:runtime_helpers_android",
1454+
"@maven_android//:com_google_guava_guava",
1455+
],
1456+
)
1457+
14221458
java_library(
14231459
name = "standard_overload",
14241460
srcs = ["CelStandardOverload.java"],
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.standard;
16+
17+
import com.google.common.collect.ImmutableSet;
18+
import dev.cel.common.CelOptions;
19+
import dev.cel.runtime.CelFunctionBinding;
20+
import dev.cel.runtime.RuntimeEquality;
21+
22+
/** Standard function for {@code matches}. */
23+
public final class NotStrictlyFalseFunction extends CelStandardFunction {
24+
private static final NotStrictlyFalseFunction ALL_OVERLOADS = new NotStrictlyFalseFunction(ImmutableSet.copyOf(NotStrictlyFalseOverload.values()));
25+
26+
public static NotStrictlyFalseFunction create() {
27+
return ALL_OVERLOADS;
28+
}
29+
/** Overloads for the standard function. */
30+
public enum NotStrictlyFalseOverload implements CelStandardOverload {
31+
NOT_STRICTLY_FALSE(
32+
(celOptions, runtimeEquality) ->
33+
CelFunctionBinding.from(
34+
"not_strictly_false",
35+
Object.class,
36+
(Object value) -> {
37+
if (value instanceof Boolean) {
38+
return value;
39+
}
40+
41+
return true;
42+
})),
43+
;
44+
45+
private final FunctionBindingCreator bindingCreator;
46+
47+
@Override
48+
public CelFunctionBinding newFunctionBinding(
49+
CelOptions celOptions, RuntimeEquality runtimeEquality) {
50+
return bindingCreator.create(celOptions, runtimeEquality);
51+
}
52+
53+
NotStrictlyFalseOverload(FunctionBindingCreator bindingCreator) {
54+
this.bindingCreator = bindingCreator;
55+
}
56+
}
57+
58+
private NotStrictlyFalseFunction(ImmutableSet<CelStandardOverload> overloads) {
59+
super(overloads);
60+
}
61+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ java_library(
4848
"//common/values:proto_message_value_provider",
4949
"//compiler",
5050
"//compiler:compiler_builder",
51+
"//extensions",
5152
"//extensions:optional_library",
5253
"//parser:macro",
5354
"//parser:operator",
@@ -77,8 +78,13 @@ java_library(
7778
"//runtime:unknown_options",
7879
"//runtime/planner:program_planner",
7980
"//runtime/standard:divide",
81+
"//runtime/standard:equals",
8082
"//runtime/standard:greater",
83+
"//runtime/standard:greater_equals",
8184
"//runtime/standard:index",
85+
"//runtime/standard:less",
86+
"//runtime/standard:logical_not",
87+
"//runtime/standard:not_strictly_false",
8288
"//runtime/standard:standard_function",
8389
"//testing/protos:message_with_enum_cel_java_proto",
8490
"//testing/protos:message_with_enum_java_proto",

0 commit comments

Comments
 (0)