Skip to content

Commit 6714726

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
[NaaS] Add cel list extension function in the NaaS evaluator.
PiperOrigin-RevId: 775290530
1 parent b4e6034 commit 6714726

File tree

5 files changed

+188
-4
lines changed

5 files changed

+188
-4
lines changed

extensions/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,12 @@ cc_library(
347347
srcs = ["lists_functions.cc"],
348348
hdrs = ["lists_functions.h"],
349349
deps = [
350+
"//checker:type_checker_builder",
351+
"//checker/internal:builtins_arena",
352+
"//common:decl",
350353
"//common:expr",
351354
"//common:operators",
355+
"//common:type",
352356
"//common:value",
353357
"//common:value_kind",
354358
"//internal:status_macros",
@@ -360,6 +364,7 @@ cc_library(
360364
"//runtime:function_registry",
361365
"//runtime:runtime_options",
362366
"@com_google_absl//absl/base:core_headers",
367+
"@com_google_absl//absl/base:no_destructor",
363368
"@com_google_absl//absl/base:nullability",
364369
"@com_google_absl//absl/container:flat_hash_set",
365370
"@com_google_absl//absl/status",
@@ -377,9 +382,13 @@ cc_test(
377382
srcs = ["lists_functions_test.cc"],
378383
deps = [
379384
":lists_functions",
385+
"//checker:standard_library",
386+
"//checker:validation_result",
380387
"//common:source",
381388
"//common:value",
382389
"//common:value_testing",
390+
"//compiler",
391+
"//compiler:compiler_factory",
383392
"//extensions/protobuf:runtime_adapter",
384393
"//internal:testing",
385394
"//internal:testing_descriptor_pool",

extensions/lists_functions.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <vector>
2222

2323
#include "absl/base/macros.h"
24+
#include "absl/base/no_destructor.h"
2425
#include "absl/base/nullability.h"
2526
#include "absl/container/flat_hash_set.h"
2627
#include "absl/status/status.h"
@@ -29,8 +30,12 @@
2930
#include "absl/strings/string_view.h"
3031
#include "absl/types/optional.h"
3132
#include "absl/types/span.h"
33+
#include "checker/internal/builtins_arena.h"
34+
#include "checker/type_checker_builder.h"
35+
#include "common/decl.h"
3236
#include "common/expr.h"
3337
#include "common/operators.h"
38+
#include "common/type.h"
3439
#include "common/value.h"
3540
#include "common/value_kind.h"
3641
#include "internal/status_macros.h"
@@ -48,6 +53,8 @@
4853
namespace cel::extensions {
4954
namespace {
5055

56+
using ::cel::checker_internal::BuiltinsArena;
57+
5158
// Slow distinct() implementation that uses Equal() to compare values in O(n^2).
5259
absl::Status ListDistinctHeterogeneousImpl(
5360
const ListValue& list,
@@ -525,6 +532,68 @@ absl::Status RegisterListSortFunction(FunctionRegistry& registry) {
525532
return absl::OkStatus();
526533
}
527534

535+
const Type& ListIntType() {
536+
static absl::NoDestructor<Type> kInstance(
537+
ListType(BuiltinsArena(), IntType()));
538+
return *kInstance;
539+
}
540+
541+
const Type& ListTypeParamType() {
542+
static absl::NoDestructor<Type> kInstance(
543+
ListType(BuiltinsArena(), TypeParamType("T")));
544+
return *kInstance;
545+
}
546+
547+
absl::Status RegisterListsCheckerDecls(TypeCheckerBuilder& builder) {
548+
CEL_ASSIGN_OR_RETURN(
549+
FunctionDecl distinct_decl,
550+
MakeFunctionDecl("distinct", MakeMemberOverloadDecl(
551+
"list_distinct", ListTypeParamType(),
552+
ListTypeParamType())));
553+
554+
CEL_ASSIGN_OR_RETURN(
555+
FunctionDecl flatten_decl,
556+
MakeFunctionDecl(
557+
"flatten",
558+
MakeMemberOverloadDecl("list_flatten_int", ListType(), ListType(),
559+
IntType()),
560+
MakeMemberOverloadDecl("list_flatten", ListType(), ListType())));
561+
562+
CEL_ASSIGN_OR_RETURN(
563+
FunctionDecl range_decl,
564+
MakeFunctionDecl(
565+
"lists.range",
566+
MakeOverloadDecl("list_range", ListIntType(), IntType())));
567+
568+
CEL_ASSIGN_OR_RETURN(
569+
FunctionDecl reverse_decl,
570+
MakeFunctionDecl(
571+
"reverse", MakeMemberOverloadDecl("list_reverse", ListTypeParamType(),
572+
ListTypeParamType())));
573+
574+
CEL_ASSIGN_OR_RETURN(
575+
FunctionDecl slice_decl,
576+
MakeFunctionDecl(
577+
"slice",
578+
MakeMemberOverloadDecl("list_slice", ListTypeParamType(),
579+
ListTypeParamType(), IntType(), IntType())));
580+
// TODO(uncreated-issue/83): Update to specific decls for sortable types.
581+
CEL_ASSIGN_OR_RETURN(
582+
FunctionDecl sort_decl,
583+
MakeFunctionDecl("sort",
584+
MakeMemberOverloadDecl("list_sort", ListTypeParamType(),
585+
ListTypeParamType())));
586+
587+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(distinct_decl)));
588+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(flatten_decl)));
589+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(range_decl)));
590+
// MergeFunction is used to combine with the reverse function
591+
// defined in strings extension.
592+
CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(reverse_decl)));
593+
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(slice_decl)));
594+
return builder.AddFunction(std::move(sort_decl));
595+
}
596+
528597
} // namespace
529598

530599
absl::Status RegisterListsFunctions(FunctionRegistry& registry,
@@ -545,4 +614,8 @@ absl::Status RegisterListsMacros(MacroRegistry& registry,
545614
return registry.RegisterMacros(lists_macros());
546615
}
547616

617+
CheckerLibrary ListsCheckerLibrary() {
618+
return {.id = "cel.lib.ext.lists", .configure = RegisterListsCheckerDecls};
619+
}
620+
548621
} // namespace cel::extensions

extensions/lists_functions.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define THIRD_PARTY_CEL_CPP_EXTENSIONS_LISTS_FUNCTIONS_H_
1717

1818
#include "absl/status/status.h"
19+
#include "checker/type_checker_builder.h"
1920
#include "parser/macro_registry.h"
2021
#include "parser/options.h"
2122
#include "runtime/function_registry.h"
@@ -46,6 +47,23 @@ absl::Status RegisterListsFunctions(FunctionRegistry& registry,
4647
absl::Status RegisterListsMacros(MacroRegistry& registry,
4748
const ParserOptions& options);
4849

50+
// Type check declarations for the lists extension library.
51+
// Provides decls for the following functions:
52+
//
53+
// lists.range(n: int) -> list(int)
54+
//
55+
// <list(T)>.distinct() -> list(T)
56+
//
57+
// <list(dyn)>.flatten() -> list(dyn)
58+
// <list(dyn)>.flatten(limit: int) -> list(dyn)
59+
//
60+
// <list(T)>.reverse() -> list(T)
61+
//
62+
// <list(T)>.sort() -> list(T)
63+
//
64+
// <list(T)>.slice(start: int, end: int) -> list(T)
65+
CheckerLibrary ListsCheckerLibrary();
66+
4967
} // namespace cel::extensions
5068

5169
#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_SETS_FUNCTIONS_H_

extensions/lists_functions_test.cc

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@
1717
#include <memory>
1818
#include <string>
1919
#include <utility>
20+
#include <vector>
2021

2122
#include "cel/expr/syntax.pb.h"
2223
#include "absl/status/status.h"
2324
#include "absl/status/status_matchers.h"
25+
#include "checker/standard_library.h"
26+
#include "checker/validation_result.h"
2427
#include "common/source.h"
2528
#include "common/value.h"
2629
#include "common/value_testing.h"
30+
#include "compiler/compiler.h"
31+
#include "compiler/compiler_factory.h"
2732
#include "extensions/protobuf/runtime_adapter.h"
2833
#include "internal/testing.h"
2934
#include "internal/testing_descriptor_pool.h"
@@ -38,17 +43,19 @@
3843
#include "runtime/runtime_options.h"
3944
#include "runtime/standard_runtime_builder_factory.h"
4045
#include "google/protobuf/arena.h"
46+
#include "google/protobuf/descriptor.h"
4147

4248
namespace cel::extensions {
4349
namespace {
44-
using ::cel::expr::Expr;
45-
using ::cel::expr::ParsedExpr;
46-
using ::cel::expr::SourceInfo;
4750

4851
using ::absl_testing::IsOk;
4952
using ::absl_testing::StatusIs;
5053
using ::cel::test::ErrorValueIs;
54+
using ::cel::expr::Expr;
55+
using ::cel::expr::ParsedExpr;
56+
using ::cel::expr::SourceInfo;
5157
using ::testing::HasSubstr;
58+
using ::testing::ValuesIn;
5259

5360
struct TestInfo {
5461
std::string expr;
@@ -273,5 +280,80 @@ TEST(ListsFunctionsTest, ListSortByMacroParseError) {
273280
HasSubstr("sortBy can only be applied to")));
274281
}
275282

283+
struct ListCheckerTestCase {
284+
const std::string expr;
285+
bool is_valid;
286+
};
287+
288+
class ListsCheckerLibraryTest
289+
: public ::testing::TestWithParam<ListCheckerTestCase> {
290+
public:
291+
void SetUp() override {
292+
// Arrange: Configure the compiler.
293+
// Add the lists checker library to the compiler builder.
294+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<CompilerBuilder> compiler_builder,
295+
NewCompilerBuilder(descriptor_pool_));
296+
ASSERT_THAT(compiler_builder->AddLibrary(StandardCheckerLibrary()), IsOk());
297+
ASSERT_THAT(compiler_builder->AddLibrary(ListsCheckerLibrary()), IsOk());
298+
ASSERT_OK_AND_ASSIGN(compiler_, std::move(*compiler_builder).Build());
299+
}
300+
301+
const google::protobuf::DescriptorPool* descriptor_pool_ =
302+
internal::GetTestingDescriptorPool();
303+
std::unique_ptr<Compiler> compiler_;
304+
};
305+
306+
TEST_P(ListsCheckerLibraryTest, ListsFunctionsTypeCheckerSuccess) {
307+
// Act & Assert: Compile the expression and validate the result.
308+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
309+
compiler_->Compile(GetParam().expr));
310+
EXPECT_EQ(result.IsValid(), GetParam().is_valid);
311+
}
312+
313+
// Returns a vector of test cases for the ListsCheckerLibraryTest.
314+
// Returns both positive and negative test cases for the lists functions.
315+
std::vector<ListCheckerTestCase> createListsCheckerParams() {
316+
return {
317+
// lists.distinct()
318+
{R"([1,2,3,4,4].distinct() == [1,2,3,4])", true},
319+
{R"('abc'.distinct() == [1,2,3,4])", false},
320+
{R"([1,2,3,4,4].distinct() == 'abc')", false},
321+
{R"([1,2,3,4,4].distinct(1) == [1,2,3,4])", false},
322+
// lists.flatten()
323+
{R"([1,2,3,4].flatten() == [1,2,3,4])", true},
324+
{R"([1,2,3,4].flatten(1) == [1,2,3,4])", true},
325+
{R"('abc'.flatten() == [1,2,3,4])", false},
326+
{R"([1,2,3,4].flatten() == 'abc')", false},
327+
{R"('abc'.flatten(1) == [1,2,3,4])", false},
328+
{R"([1,2,3,4].flatten('abc') == [1,2,3,4])", false},
329+
{R"([1,2,3,4].flatten(1) == 'abc')", false},
330+
// lists.range()
331+
{R"(lists.range(4) == [0,1,2,3])", true},
332+
{R"(lists.range('abc') == [])", false},
333+
{R"(lists.range(4) == 'abc')", false},
334+
{R"(lists.range(4, 4) == [0,1,2,3])", false},
335+
// lists.reverse()
336+
{R"([1,2,3,4].reverse() == [4,3,2,1])", true},
337+
{R"('abc'.reverse() == [])", false},
338+
{R"([1,2,3,4].reverse() == 'abc')", false},
339+
{R"([1,2,3,4].reverse(1) == [4,3,2,1])", false},
340+
// lists.slice()
341+
{R"([1,2,3,4].slice(0, 4) == [1,2,3,4])", true},
342+
{R"('abc'.slice(0, 4) == [1,2,3,4])", false},
343+
{R"([1,2,3,4].slice('abc', 4) == [1,2,3,4])", false},
344+
{R"([1,2,3,4].slice(0, 'abc') == [1,2,3,4])", false},
345+
{R"([1,2,3,4].slice(0, 4) == 'abc')", false},
346+
{R"([1,2,3,4].slice(0, 2, 3) == [1,2,3,4])", false},
347+
// lists.sort()
348+
{R"([1,2,3,4].sort() == [1,2,3,4])", true},
349+
{R"('abc'.sort() == [])", false},
350+
{R"([1,2,3,4].sort() == 'abc')", false},
351+
{R"([1,2,3,4].sort(2) == [1,2,3,4])", false},
352+
};
353+
}
354+
355+
INSTANTIATE_TEST_SUITE_P(ListsCheckerLibraryTest, ListsCheckerLibraryTest,
356+
ValuesIn(createListsCheckerParams()));
357+
276358
} // namespace
277359
} // namespace cel::extensions

extensions/strings.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,9 @@ absl::Status RegisterStringsDecls(TypeCheckerBuilder& builder) {
398398
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(upper_ascii_decl)));
399399
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(format_decl)));
400400
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(quote_decl)));
401-
CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(reverse_decl)));
401+
// MergeFunction is used to combine with the reverse function
402+
// defined in cel.lib.ext.lists extension.
403+
CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(reverse_decl)));
402404

403405
return absl::OkStatus();
404406
}

0 commit comments

Comments
 (0)