Skip to content

Commit 3a9f8f5

Browse files
jnthntatumcopybara-github
authored andcommitted
Add compiler library for optionals.
PiperOrigin-RevId: 694191062
1 parent 410d439 commit 3a9f8f5

File tree

4 files changed

+375
-0
lines changed

4 files changed

+375
-0
lines changed

compiler/BUILD

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,39 @@ cc_test(
7676
"@com_google_protobuf//:protobuf",
7777
],
7878
)
79+
80+
cc_library(
81+
name = "optional",
82+
srcs = ["optional.cc"],
83+
hdrs = ["optional.h"],
84+
deps = [
85+
":compiler",
86+
"//checker:optional",
87+
"//checker:type_checker_builder",
88+
"//parser:parser_interface",
89+
"@com_google_absl//absl/status",
90+
],
91+
)
92+
93+
cc_test(
94+
name = "optional_test",
95+
srcs = ["optional_test.cc"],
96+
deps = [
97+
":compiler",
98+
":compiler_factory",
99+
":optional",
100+
"//checker:standard_library",
101+
"//checker:type_check_issue",
102+
"//checker:validation_result",
103+
"//common:decl",
104+
"//common:source",
105+
"//common:type",
106+
"//internal:testing",
107+
"//internal:testing_descriptor_pool",
108+
"//testutil:baseline_tests",
109+
"@com_google_absl//absl/status:status_matchers",
110+
"@com_google_absl//absl/status:statusor",
111+
"@com_google_absl//absl/strings",
112+
"@com_google_cel_spec//proto/cel/expr/conformance/proto3:test_all_types_cc_proto",
113+
],
114+
)

compiler/optional.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2024 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+
#include "compiler/optional.h"
16+
17+
#include <utility>
18+
19+
#include "absl/status/status.h"
20+
#include "checker/optional.h"
21+
#include "checker/type_checker_builder.h"
22+
#include "compiler/compiler.h"
23+
#include "parser/parser_interface.h"
24+
25+
namespace cel {
26+
27+
CompilerLibrary OptionalCompilerLibrary() {
28+
CheckerLibrary checker_library = OptionalCheckerLibrary();
29+
return CompilerLibrary(
30+
std::move(checker_library.id),
31+
[](ParserBuilder& builder) {
32+
builder.GetOptions().enable_optional_syntax = true;
33+
return absl::OkStatus();
34+
},
35+
std::move(checker_library.configure));
36+
}
37+
38+
} // namespace cel

compiler/optional.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2024 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+
#ifndef THIRD_PARTY_CEL_CPP_COMPILER_OPTIONALS_H_
15+
#define THIRD_PARTY_CEL_CPP_COMPILER_OPTIONALS_H_
16+
17+
#include "compiler/compiler.h"
18+
19+
namespace cel {
20+
21+
// CompilerLibrary that enables support for CEL optional types.
22+
CompilerLibrary OptionalCompilerLibrary();
23+
24+
} // namespace cel
25+
26+
#endif // THIRD_PARTY_CEL_CPP_COMPILER_OPTIONALS_H_

compiler/optional_test.cc

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright 2024 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+
#include "compiler/optional.h"
15+
16+
#include <memory>
17+
#include <string>
18+
#include <utility>
19+
20+
#include "absl/status/status_matchers.h"
21+
#include "absl/status/statusor.h"
22+
#include "absl/strings/ascii.h"
23+
#include "absl/strings/str_cat.h"
24+
#include "absl/strings/str_join.h"
25+
#include "checker/standard_library.h"
26+
#include "checker/type_check_issue.h"
27+
#include "checker/validation_result.h"
28+
#include "common/decl.h"
29+
#include "common/source.h"
30+
#include "common/type.h"
31+
#include "compiler/compiler.h"
32+
#include "compiler/compiler_factory.h"
33+
#include "internal/testing.h"
34+
#include "internal/testing_descriptor_pool.h"
35+
#include "testutil/baseline_tests.h"
36+
#include "cel/expr/conformance/proto3/test_all_types.pb.h"
37+
38+
namespace cel {
39+
namespace {
40+
41+
using ::absl_testing::IsOk;
42+
using ::cel::expr::conformance::proto3::TestAllTypes;
43+
using ::cel::test::FormatBaselineAst;
44+
using ::testing::HasSubstr;
45+
46+
struct TestCase {
47+
std::string expr;
48+
std::string expected_ast;
49+
};
50+
51+
class OptionalTest : public testing::TestWithParam<TestCase> {};
52+
53+
std::string FormatIssues(const ValidationResult& result) {
54+
const Source* source = result.GetSource();
55+
return absl::StrJoin(
56+
result.GetIssues(), "\n",
57+
[=](std::string* out, const TypeCheckIssue& issue) {
58+
absl::StrAppend(
59+
out, (source) ? issue.ToDisplayString(*source) : issue.message());
60+
});
61+
}
62+
63+
TEST_P(OptionalTest, OptionalsEnabled) {
64+
const TestCase& test_case = GetParam();
65+
66+
ASSERT_OK_AND_ASSIGN(
67+
auto builder,
68+
NewCompilerBuilder(cel::internal::GetSharedTestingDescriptorPool()));
69+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
70+
ASSERT_THAT(builder->AddLibrary(OptionalCompilerLibrary()), IsOk());
71+
ASSERT_THAT(builder->GetCheckerBuilder().AddVariable(MakeVariableDecl(
72+
"msg", MessageType(TestAllTypes::descriptor()))),
73+
IsOk());
74+
75+
ASSERT_OK_AND_ASSIGN(auto compiler, std::move(*builder).Build());
76+
77+
absl::StatusOr<ValidationResult> maybe_result =
78+
compiler->Compile(test_case.expr);
79+
80+
ASSERT_OK_AND_ASSIGN(ValidationResult result, std::move(maybe_result));
81+
ASSERT_TRUE(result.IsValid()) << FormatIssues(result);
82+
EXPECT_EQ(FormatBaselineAst(*result.GetAst()),
83+
absl::StripAsciiWhitespace(test_case.expected_ast))
84+
<< test_case.expr;
85+
}
86+
87+
INSTANTIATE_TEST_SUITE_P(
88+
OptionalTest, OptionalTest,
89+
::testing::Values(
90+
TestCase{
91+
.expr = "msg.?single_int64",
92+
.expected_ast = R"(
93+
_?._(
94+
msg~cel.expr.conformance.proto3.TestAllTypes^msg,
95+
"single_int64"
96+
)~optional_type(int)^select_optional_field)",
97+
},
98+
TestCase{
99+
.expr = "optional.of('foo')",
100+
.expected_ast = R"(
101+
optional.of(
102+
"foo"~string
103+
)~optional_type(string)^optional_of)",
104+
},
105+
TestCase{
106+
.expr = "optional.of('foo').optMap(x, x)",
107+
.expected_ast = R"(
108+
_?_:_(
109+
optional.of(
110+
"foo"~string
111+
)~optional_type(string)^optional_of.hasValue()~bool^optional_hasValue,
112+
optional.of(
113+
__comprehension__(
114+
// Variable
115+
#unused,
116+
// Target
117+
[]~list(dyn),
118+
// Accumulator
119+
x,
120+
// Init
121+
optional.of(
122+
"foo"~string
123+
)~optional_type(string)^optional_of.value()~string^optional_value,
124+
// LoopCondition
125+
false~bool,
126+
// LoopStep
127+
x~string^x,
128+
// Result
129+
x~string^x)~string
130+
)~optional_type(string)^optional_of,
131+
optional.none()~optional_type(string)^optional_none
132+
)~optional_type(string)^conditional
133+
)",
134+
},
135+
TestCase{
136+
.expr = "optional.of('foo').optFlatMap(x, optional.of(x))",
137+
.expected_ast = R"(
138+
_?_:_(
139+
optional.of(
140+
"foo"~string
141+
)~optional_type(string)^optional_of.hasValue()~bool^optional_hasValue,
142+
__comprehension__(
143+
// Variable
144+
#unused,
145+
// Target
146+
[]~list(dyn),
147+
// Accumulator
148+
x,
149+
// Init
150+
optional.of(
151+
"foo"~string
152+
)~optional_type(string)^optional_of.value()~string^optional_value,
153+
// LoopCondition
154+
false~bool,
155+
// LoopStep
156+
x~string^x,
157+
// Result
158+
optional.of(
159+
x~string^x
160+
)~optional_type(string)^optional_of)~optional_type(string),
161+
optional.none()~optional_type(string)^optional_none
162+
)~optional_type(string)^conditional
163+
)",
164+
},
165+
TestCase{
166+
.expr = "optional.ofNonZeroValue(1)",
167+
.expected_ast = R"(
168+
optional.ofNonZeroValue(
169+
1~int
170+
)~optional_type(int)^optional_ofNonZeroValue
171+
)",
172+
},
173+
TestCase{
174+
.expr = "[0][?1]",
175+
.expected_ast = R"(
176+
_[?_](
177+
[
178+
0~int
179+
]~list(int),
180+
1~int
181+
)~optional_type(int)^list_optindex_optional_int
182+
)",
183+
},
184+
TestCase{
185+
.expr = "{0: 2}[?1]",
186+
.expected_ast = R"(
187+
_[?_](
188+
{
189+
0~int:2~int
190+
}~map(int, int),
191+
1~int
192+
)~optional_type(int)^map_optindex_optional_value
193+
)",
194+
},
195+
TestCase{
196+
.expr = "msg.?repeated_int64[1]",
197+
.expected_ast = R"(
198+
_[_](
199+
_?._(
200+
msg~cel.expr.conformance.proto3.TestAllTypes^msg,
201+
"repeated_int64"
202+
)~optional_type(list(int))^select_optional_field,
203+
1~int
204+
)~optional_type(int)^optional_list_index_int
205+
)",
206+
},
207+
TestCase{
208+
.expr = "msg.?map_int64_int64[1]",
209+
.expected_ast = R"(
210+
_[_](
211+
_?._(
212+
msg~cel.expr.conformance.proto3.TestAllTypes^msg,
213+
"map_int64_int64"
214+
)~optional_type(map(int, int))^select_optional_field,
215+
1~int
216+
)~optional_type(int)^optional_map_index_value
217+
)",
218+
},
219+
TestCase{
220+
.expr = "optional.of(1).or(optional.of(2))",
221+
.expected_ast = R"(
222+
optional.of(
223+
1~int
224+
)~optional_type(int)^optional_of.or(
225+
optional.of(
226+
2~int
227+
)~optional_type(int)^optional_of
228+
)~optional_type(int)^optional_or_optional)",
229+
},
230+
TestCase{
231+
.expr = "optional.of(1).orValue(2)",
232+
.expected_ast = R"(
233+
optional.of(
234+
1~int
235+
)~optional_type(int)^optional_of.orValue(
236+
2~int
237+
)~int^optional_orValue_value
238+
)",
239+
},
240+
TestCase{
241+
.expr = "optional.of(1).value()",
242+
.expected_ast = R"(
243+
optional.of(
244+
1~int
245+
)~optional_type(int)^optional_of.value()~int^optional_value
246+
)",
247+
},
248+
TestCase{
249+
.expr = "optional.of(1).hasValue()",
250+
.expected_ast = R"(
251+
optional.of(
252+
1~int
253+
)~optional_type(int)^optional_of.hasValue()~bool^optional_hasValue
254+
)",
255+
}));
256+
257+
TEST(OptionalTest, NotEnabled) {
258+
ASSERT_OK_AND_ASSIGN(
259+
auto builder,
260+
NewCompilerBuilder(cel::internal::GetSharedTestingDescriptorPool()));
261+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
262+
ASSERT_THAT(builder->GetCheckerBuilder().AddVariable(MakeVariableDecl(
263+
"msg", MessageType(TestAllTypes::descriptor()))),
264+
IsOk());
265+
266+
ASSERT_OK_AND_ASSIGN(auto compiler, std::move(*builder).Build());
267+
268+
ASSERT_OK_AND_ASSIGN(auto result, compiler->Compile("optional.of(1)"));
269+
270+
EXPECT_THAT(FormatIssues(result),
271+
HasSubstr("undeclared reference to 'optional'"));
272+
}
273+
274+
} // namespace
275+
} // namespace cel

0 commit comments

Comments
 (0)