Skip to content
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 @@ -110,7 +110,7 @@ public void standardLibrarySubset_favorExclusion() throws Exception {
FunctionSelector.create("matches", ImmutableSet.of()),
FunctionSelector.create(
"timestamp", ImmutableSet.of("string_to_timestamp"))))
.setExcludedMacros(ImmutableSet.of("map", "filter"))
.setExcludedMacros(ImmutableSet.of("map", "existsOne", "filter"))
.build());
}

Expand Down
3 changes: 3 additions & 0 deletions common/src/main/java/dev/cel/common/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public enum Operator {
HAS("has"),
ALL("all"),
EXISTS("exists"),
@Deprecated // Prefer EXISTS_ONE_NEW.
EXISTS_ONE("exists_one"),
EXISTS_ONE_NEW("existsOne"),
MAP("map"),
FILTER("filter"),
NOT_STRICTLY_FALSE("@not_strictly_false"),
Expand Down Expand Up @@ -109,6 +111,7 @@ public static Optional<Operator> find(String text) {
.put(EQUALS.getFunction(), EQUALS)
.put(EXISTS.getFunction(), EXISTS)
.put(EXISTS_ONE.getFunction(), EXISTS_ONE)
.put(EXISTS_ONE_NEW.getFunction(), EXISTS_ONE_NEW)
.put(FILTER.getFunction(), FILTER)
.put(GREATER.getFunction(), GREATER)
.put(GREATER_EQUALS.getFunction(), GREATER_EQUALS)
Expand Down
3 changes: 3 additions & 0 deletions conformance/src/test/java/dev/cel/conformance/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ java_library(
"//common:options",
"//common/types:cel_proto_types",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
Expand Down Expand Up @@ -49,6 +50,7 @@ java_library(
tags = ["conformance_maven"],
deps = MAVEN_JAR_DEPS + [
"//:java_truth",
"//compiler:compiler_builder",
"//testing:expr_value_utils",
"@cel_spec//proto/cel/expr:expr_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
Expand All @@ -75,6 +77,7 @@ _ALL_TESTS = [
"@cel_spec//tests/simple:testdata/lists.textproto",
"@cel_spec//tests/simple:testdata/logic.textproto",
"@cel_spec//tests/simple:testdata/macros.textproto",
"@cel_spec//tests/simple:testdata/macros2.textproto",
"@cel_spec//tests/simple:testdata/math_ext.textproto",
"@cel_spec//tests/simple:testdata/namespace.textproto",
"@cel_spec//tests/simple:testdata/optionals.textproto",
Expand Down
55 changes: 26 additions & 29 deletions conformance/src/test/java/dev/cel/conformance/ConformanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import dev.cel.common.CelValidationResult;
import dev.cel.common.types.CelProtoTypes;
import dev.cel.compiler.CelCompilerFactory;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.expr.conformance.test.SimpleTest;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
Expand All @@ -45,6 +46,7 @@
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntime.Program;
import dev.cel.runtime.CelRuntimeFactory;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Map;
import org.junit.runners.model.Statement;

Expand All @@ -61,31 +63,37 @@ public final class ConformanceTest extends Statement {
.enableQuotedIdentifierSyntax(true)
.build();

private static final ImmutableList<CelCompilerLibrary> CANONICAL_COMPILER_EXTENSIONS =
ImmutableList.of(
CelExtensions.bindings(),
CelExtensions.comprehensions(),
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.protos(),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE);

private static final ImmutableList<CelRuntimeLibrary> CANONICAL_RUNTIME_EXTENSIONS =
ImmutableList.of(
CelExtensions.comprehensions(),
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE);

private static final CelParser PARSER_WITH_MACROS =
CelParserFactory.standardCelParserBuilder()
.setOptions(OPTIONS)
.addLibraries(
CelExtensions.bindings(),
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.protos(),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE)
.addLibraries(CANONICAL_COMPILER_EXTENSIONS)
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
.build();

private static final CelParser PARSER_WITHOUT_MACROS =
CelParserFactory.standardCelParserBuilder()
.setOptions(OPTIONS)
.addLibraries(
CelExtensions.bindings(),
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.protos(),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE)
.addLibraries(CANONICAL_COMPILER_EXTENSIONS)
.setStandardMacros()
.build();

Expand All @@ -104,13 +112,7 @@ private static CelChecker getChecker(SimpleTest test) throws Exception {
.setContainer(CelContainer.ofName(test.getContainer()))
.addDeclarations(decls.build())
.addFileTypes(dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor())
.addLibraries(
CelExtensions.bindings(),
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE)
.addLibraries(CANONICAL_COMPILER_EXTENSIONS)
.addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor())
.addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor())
.build();
Expand All @@ -119,12 +121,7 @@ private static CelChecker getChecker(SimpleTest test) throws Exception {
private static final CelRuntime RUNTIME =
CelRuntimeFactory.standardCelRuntimeBuilder()
.setOptions(OPTIONS)
.addLibraries(
CelExtensions.encoders(OPTIONS),
CelExtensions.math(OPTIONS),
CelExtensions.sets(OPTIONS),
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE)
.addLibraries(CANONICAL_RUNTIME_EXTENSIONS)
.setExtensionRegistry(DEFAULT_EXTENSION_REGISTRY)
.addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor())
.addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ public ImmutableSet<CelMacro> macros() {
Operator.EXISTS_ONE.getFunction(),
3,
CelComprehensionsExtensions::expandExistsOneMacro),
CelMacro.newReceiverMacro(
Operator.EXISTS_ONE_NEW.getFunction(),
3,
CelComprehensionsExtensions::expandExistsOneMacro),
CelMacro.newReceiverMacro(
"transformList", 3, CelComprehensionsExtensions::transformListMacro),
CelMacro.newReceiverMacro(
Expand Down
8 changes: 8 additions & 0 deletions parser/src/main/java/dev/cel/parser/CelStandardMacro.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ public enum CelStandardMacro {
CelMacro.newReceiverMacro(
Operator.EXISTS_ONE.getFunction(), 2, CelStandardMacro::expandExistsOneMacro)),

/**
* Boolean comprehension which asserts that a predicate holds true for exactly one element in the
* input range.
*/
EXISTS_ONE_NEW(
CelMacro.newReceiverMacro(
Operator.EXISTS_ONE_NEW.getFunction(), 2, CelStandardMacro::expandExistsOneMacro)),

/**
* Comprehension which applies a transform to each element in the input range and produces a list
* of equivalent size as output.
Expand Down
10 changes: 9 additions & 1 deletion parser/src/test/java/dev/cel/parser/CelParserImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import com.google.testing.junit.testparameterinjector.TestParameters;
Expand Down Expand Up @@ -260,10 +261,17 @@ public void parse_exprUnderMaxRecursionLimit_doesNotThrow(
@TestParameters("{expression: 'A.all(a?b, c)'}")
@TestParameters("{expression: 'A.exists(a?b, c)'}")
@TestParameters("{expression: 'A.exists_one(a?b, c)'}")
@TestParameters("{expression: 'A.existsOne(a?b, c)'}")
@TestParameters("{expression: 'A.filter(a?b, c)'}")
public void parse_macroArgumentContainsSyntaxError_throws(String expression) {
CelParser parser =
CelParserImpl.newBuilder().setStandardMacros(CelStandardMacro.STANDARD_MACROS).build();
CelParserImpl.newBuilder()
.setStandardMacros(
ImmutableSet.<CelStandardMacro>builder()
.addAll(CelStandardMacro.STANDARD_MACROS)
.add(CelStandardMacro.EXISTS_ONE_NEW)
.build())
.build();

CelValidationResult parseResult = parser.parse(expression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
Expand Down Expand Up @@ -57,7 +58,11 @@
public final class CelParserParameterizedTest extends BaselineTestCase {
private static final CelParser PARSER =
CelParserFactory.standardCelParserBuilder()
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
.setStandardMacros(
ImmutableSet.<CelStandardMacro>builder()
.addAll(CelStandardMacro.STANDARD_MACROS)
.add(CelStandardMacro.EXISTS_ONE_NEW)
.build())
.addLibraries(CelOptionalLibrary.INSTANCE)
.addMacros(
CelMacro.newGlobalVarArgMacro("noop_macro", (a, b, c) -> Optional.empty()),
Expand Down Expand Up @@ -162,6 +167,7 @@ public void parser() {
runTest(PARSER, "aaa.bbb(ccc)");
runTest(PARSER, "has(m.f)");
runTest(PARSER, "m.exists_one(v, f)");
runTest(PARSER, "m.existsOne(v, f)");
runTest(PARSER, "m.map(v, f)");
runTest(PARSER, "m.map(v, p, f)");
runTest(PARSER, "m.filter(v, p)");
Expand Down
17 changes: 17 additions & 0 deletions parser/src/test/java/dev/cel/parser/CelStandardMacroTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public void getFunction() {
assertThat(CelStandardMacro.EXISTS.getFunction()).isEqualTo(Operator.EXISTS.getFunction());
assertThat(CelStandardMacro.EXISTS_ONE.getFunction())
.isEqualTo(Operator.EXISTS_ONE.getFunction());
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getFunction())
.isEqualTo(Operator.EXISTS_ONE_NEW.getFunction());
assertThat(CelStandardMacro.FILTER.getFunction()).isEqualTo(Operator.FILTER.getFunction());
assertThat(CelStandardMacro.MAP.getFunction()).isEqualTo(Operator.MAP.getFunction());
assertThat(CelStandardMacro.MAP_FILTER.getFunction()).isEqualTo(Operator.MAP.getFunction());
Expand Down Expand Up @@ -90,6 +92,21 @@ public void testExistsOne() {
.isEqualTo(CelStandardMacro.EXISTS_ONE.getDefinition().getKey().hashCode());
}

@Test
public void testExistsOneNew() {
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getFunction())
.isEqualTo(Operator.EXISTS_ONE_NEW.getFunction());
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().getArgumentCount()).isEqualTo(2);
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().isReceiverStyle()).isTrue();
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().getKey())
.isEqualTo("existsOne:2:true");
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().isVariadic()).isFalse();
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().toString())
.isEqualTo(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().getKey());
assertThat(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().hashCode())
.isEqualTo(CelStandardMacro.EXISTS_ONE_NEW.getDefinition().getKey().hashCode());
}

@Test
public void testMap2() {
assertThat(CelStandardMacro.MAP.getFunction()).isEqualTo(Operator.MAP.getFunction());
Expand Down
57 changes: 57 additions & 0 deletions parser/src/test/resources/parser.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,63 @@ M: m^#1:Expr.Ident#.exists_one(
f^#4:Expr.Ident#
)^#0:Expr.Call#

I: m.existsOne(v, f)
=====>
P: __comprehension__(
// Variable
v,
// Target
m^#1:Expr.Ident#,
// Accumulator
@result,
// Init
0^#5:int64#,
// LoopCondition
true^#6:bool#,
// LoopStep
_?_:_(
f^#4:Expr.Ident#,
_+_(
@result^#7:Expr.Ident#,
1^#8:int64#
)^#9:Expr.Call#,
@result^#10:Expr.Ident#
)^#11:Expr.Call#,
// Result
_==_(
@result^#12:Expr.Ident#,
1^#13:int64#
)^#14:Expr.Call#)^#15:Expr.Comprehension#
L: __comprehension__(
// Variable
v,
// Target
m^#1[1,0]#,
// Accumulator
@result,
// Init
0^#5[1,11]#,
// LoopCondition
true^#6[1,11]#,
// LoopStep
_?_:_(
f^#4[1,15]#,
_+_(
@result^#7[1,11]#,
1^#8[1,11]#
)^#9[1,11]#,
@result^#10[1,11]#
)^#11[1,11]#,
// Result
_==_(
@result^#12[1,11]#,
1^#13[1,11]#
)^#14[1,11]#)^#15[1,11]#
M: m^#1:Expr.Ident#.existsOne(
v^#3:Expr.Ident#,
f^#4:Expr.Ident#
)^#0:Expr.Call#

I: m.map(v, f)
=====>
P: __comprehension__(
Expand Down
Loading