diff --git a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel index e90fe6d19..4a550ede8 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel @@ -16,6 +16,7 @@ java_library( ":attribute", ":eval_attribute", ":eval_const", + ":eval_create_list", ":planned_program", "//:auto_value", "//common:cel_ast", @@ -90,3 +91,16 @@ java_library( "@maven//:com_google_guava_guava", ], ) + +java_library( + name = "eval_create_list", + srcs = ["EvalCreateList.java"], + deps = [ + "//runtime", + "//runtime:evaluation_listener", + "//runtime:function_resolver", + "//runtime:interpretable", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:com_google_guava_guava", + ], +) diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java new file mode 100644 index 000000000..6a1917475 --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java @@ -0,0 +1,67 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.runtime.planner; + +import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.Immutable; +import dev.cel.runtime.CelEvaluationException; +import dev.cel.runtime.CelEvaluationListener; +import dev.cel.runtime.CelFunctionResolver; +import dev.cel.runtime.GlobalResolver; +import dev.cel.runtime.Interpretable; + +@Immutable +final class EvalCreateList implements Interpretable { + + // Array contents are not mutated + @SuppressWarnings("Immutable") + private final Interpretable[] values; + + @Override + public Object eval(GlobalResolver resolver) throws CelEvaluationException { + ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(values.length); + for (Interpretable value : values) { + builder.add(value.eval(resolver)); + } + return builder.build(); + } + + @Override + public Object eval(GlobalResolver resolver, CelEvaluationListener listener) { + // TODO: Implement support + throw new UnsupportedOperationException("Not yet supported"); + } + + @Override + public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver) { + // TODO: Implement support + throw new UnsupportedOperationException("Not yet supported"); + } + + @Override + public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) { + // TODO: Implement support + throw new UnsupportedOperationException("Not yet supported"); + } + + static EvalCreateList create(Interpretable[] values) { + return new EvalCreateList(values); + } + + private EvalCreateList(Interpretable[] values) { + this.values = values; + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java index e482a0bac..80ffdab6a 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java @@ -15,6 +15,7 @@ package dev.cel.runtime.planner; import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import javax.annotation.concurrent.ThreadSafe; import dev.cel.common.CelAbstractSyntaxTree; @@ -22,6 +23,7 @@ import dev.cel.common.annotations.Internal; import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; +import dev.cel.common.ast.CelExpr.CelList; import dev.cel.common.ast.CelReference; import dev.cel.common.types.CelKind; import dev.cel.common.types.CelType; @@ -65,6 +67,8 @@ private Interpretable plan(CelExpr celExpr, PlannerContext ctx) { return planConstant(celExpr.constant()); case IDENT: return planIdent(celExpr, ctx); + case LIST: + return planCreateList(celExpr, ctx); case NOT_SET: throw new UnsupportedOperationException("Unsupported kind: " + celExpr.getKind()); default: @@ -124,6 +128,19 @@ private Interpretable planCheckedIdent( return EvalAttribute.create(attributeFactory.newAbsoluteAttribute(identRef.name())); } + private Interpretable planCreateList(CelExpr celExpr, PlannerContext ctx) { + CelList list = celExpr.list(); + + ImmutableList elements = list.elements(); + Interpretable[] values = new Interpretable[elements.size()]; + + for (int i = 0; i < elements.size(); i++) { + values[i] = plan(elements.get(i), ctx); + } + + return EvalCreateList.create(values); + } + @AutoValue abstract static class PlannerContext { diff --git a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java index ce159755f..a761e4925 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java +++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java @@ -18,6 +18,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertThrows; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; @@ -88,7 +89,7 @@ public void plan_constant(@TestParameter ConstantTestCase testCase) throws Excep } @Test - public void planIdent_enum() throws Exception { + public void plan_ident_enum() throws Exception { if (isParseOnly) { // TODO Skip for now, requires attribute qualification return; @@ -103,7 +104,7 @@ public void planIdent_enum() throws Exception { } @Test - public void planIdent_variable() throws Exception { + public void plan_ident_variable() throws Exception { CelAbstractSyntaxTree ast = compile("int_var"); Program program = PLANNER.plan(ast); @@ -112,6 +113,17 @@ public void planIdent_variable() throws Exception { assertThat(result).isEqualTo(1); } + @Test + @SuppressWarnings("unchecked") // test only + public void plan_createList() throws Exception { + CelAbstractSyntaxTree ast = compile("[1, 'foo', true, [2, false]]"); + Program program = PLANNER.plan(ast); + + ImmutableList result = (ImmutableList) program.eval(); + + assertThat(result).containsExactly(1L, "foo", true, ImmutableList.of(2L, false)).inOrder(); + } + @Test public void planIdent_typeLiteral(@TestParameter TypeLiteralTestCase testCase) throws Exception { if (isParseOnly) {