Skip to content

Commit 9e28867

Browse files
jnthntatumcopybara-github
authored andcommitted
Update exercise2 to use the type checker and demonstrate configuring variables.
PiperOrigin-RevId: 751613540
1 parent 6fcf3e8 commit 9e28867

File tree

8 files changed

+179
-76
lines changed

8 files changed

+179
-76
lines changed

codelab/BUILD

+5-1
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,21 @@ cc_library(
6969
srcs = ["exercise2.cc"],
7070
hdrs = ["exercise2.h"],
7171
deps = [
72+
":cel_compiler",
73+
"//compiler",
74+
"//compiler:compiler_factory",
75+
"//compiler:standard_library",
7276
"//eval/public:activation",
7377
"//eval/public:builtin_func_registrar",
7478
"//eval/public:cel_expr_builder_factory",
7579
"//eval/public:cel_expression",
7680
"//eval/public:cel_options",
7781
"//eval/public:cel_value",
7882
"//internal:status_macros",
79-
"//parser",
8083
"@com_google_absl//absl/status",
8184
"@com_google_absl//absl/status:statusor",
8285
"@com_google_absl//absl/strings",
86+
"@com_google_cel_spec//proto/cel/expr:checked_cc_proto",
8387
"@com_google_cel_spec//proto/cel/expr:syntax_cc_proto",
8488
"@com_google_googleapis//google/rpc/context:attribute_context_cc_proto",
8589
"@com_google_protobuf//:protobuf",

codelab/exercise2.cc

+57-20
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,33 @@
1515
#include "codelab/exercise2.h"
1616

1717
#include <memory>
18-
#include <string>
1918

19+
#include "cel/expr/checked.pb.h"
2020
#include "cel/expr/syntax.pb.h"
2121
#include "google/rpc/context/attribute_context.pb.h"
22-
#include "google/protobuf/arena.h"
2322
#include "absl/status/status.h"
2423
#include "absl/status/statusor.h"
2524
#include "absl/strings/str_cat.h"
2625
#include "absl/strings/string_view.h"
26+
#include "codelab/cel_compiler.h"
27+
#include "compiler/compiler.h"
28+
#include "compiler/compiler_factory.h"
29+
#include "compiler/standard_library.h"
2730
#include "eval/public/activation.h"
2831
#include "eval/public/builtin_func_registrar.h"
2932
#include "eval/public/cel_expr_builder_factory.h"
3033
#include "eval/public/cel_expression.h"
3134
#include "eval/public/cel_options.h"
3235
#include "eval/public/cel_value.h"
3336
#include "internal/status_macros.h"
34-
#include "parser/parser.h"
3537
#include "google/protobuf/arena.h"
38+
#include "google/protobuf/descriptor.h"
39+
#include "google/protobuf/message.h"
3640

3741
namespace cel_codelab {
3842
namespace {
3943

40-
using ::cel::expr::ParsedExpr;
41-
using ::google::api::expr::parser::Parse;
44+
using ::cel::expr::CheckedExpr;
4245
using ::google::api::expr::runtime::Activation;
4346
using ::google::api::expr::runtime::CelError;
4447
using ::google::api::expr::runtime::CelExpression;
@@ -49,23 +52,45 @@ using ::google::api::expr::runtime::InterpreterOptions;
4952
using ::google::api::expr::runtime::RegisterBuiltinFunctions;
5053
using ::google::rpc::context::AttributeContext;
5154

55+
absl::StatusOr<std::unique_ptr<cel::Compiler>> MakeCelCompiler() {
56+
// Note: we are using the generated descriptor pool here for simplicity, but
57+
// it has the drawback of including all message types that are linked into the
58+
// binary instead of just the ones expected for the CEL environment.
59+
google::protobuf::LinkMessageReflection<AttributeContext>();
60+
CEL_ASSIGN_OR_RETURN(
61+
std::unique_ptr<cel::CompilerBuilder> builder,
62+
cel::NewCompilerBuilder(google::protobuf::DescriptorPool::generated_pool()));
63+
64+
CEL_RETURN_IF_ERROR(builder->AddLibrary(cel::StandardCompilerLibrary()));
65+
// === Start Codelab ===
66+
// Add 'AttributeContext' as a context message to the type checker and a
67+
// boolean variable 'bool_var'. Relevant functions are on the
68+
// TypeCheckerBuilder class (see CompilerBuilder::GetCheckerBuilder).
69+
//
70+
// We're reusing the same compiler for both evaluation paths here for brevity,
71+
// but it's likely a better fit to configure a separate compiler per use case.
72+
// === End Codelab ===
73+
74+
return builder->Build();
75+
}
76+
5277
// Parse a cel expression and evaluate it against the given activation and
5378
// arena.
54-
absl::StatusOr<bool> ParseAndEvaluate(absl::string_view cel_expr,
55-
const Activation& activation,
56-
google::protobuf::Arena* arena) {
57-
CEL_ASSIGN_OR_RETURN(ParsedExpr parsed_expr, Parse(cel_expr));
58-
79+
absl::StatusOr<bool> EvalCheckedExpr(const CheckedExpr& checked_expr,
80+
const Activation& activation,
81+
google::protobuf::Arena* arena) {
5982
// Setup a default environment for building expressions.
6083
InterpreterOptions options;
61-
std::unique_ptr<CelExpressionBuilder> builder =
62-
CreateCelExpressionBuilder(options);
84+
std::unique_ptr<CelExpressionBuilder> builder = CreateCelExpressionBuilder(
85+
google::protobuf::DescriptorPool::generated_pool(),
86+
google::protobuf::MessageFactory::generated_factory(), options);
6387
CEL_RETURN_IF_ERROR(
6488
RegisterBuiltinFunctions(builder->GetRegistry(), options));
6589

90+
// Note, the expression_plan below is reusable for different inputs, but we
91+
// create one just in time for evaluation here.
6692
CEL_ASSIGN_OR_RETURN(std::unique_ptr<CelExpression> expression_plan,
67-
builder->CreateExpression(&parsed_expr.expr(),
68-
&parsed_expr.source_info()));
93+
builder->CreateExpression(&checked_expr));
6994

7095
CEL_ASSIGN_OR_RETURN(CelValue result,
7196
expression_plan->Evaluate(activation, arena));
@@ -81,26 +106,38 @@ absl::StatusOr<bool> ParseAndEvaluate(absl::string_view cel_expr,
81106
}
82107
} // namespace
83108

84-
absl::StatusOr<bool> ParseAndEvaluate(absl::string_view cel_expr,
85-
bool bool_var) {
109+
absl::StatusOr<bool> CompileAndEvaluateWithBoolVar(absl::string_view cel_expr,
110+
bool bool_var) {
111+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Compiler> compiler,
112+
MakeCelCompiler());
113+
114+
CEL_ASSIGN_OR_RETURN(CheckedExpr checked_expr,
115+
CompileToCheckedExpr(*compiler, cel_expr));
116+
86117
Activation activation;
87118
google::protobuf::Arena arena;
88119
// === Start Codelab ===
89120
// Update the activation to bind the bool argument to 'bool_var'
90121
// === End Codelab ===
91122

92-
return ParseAndEvaluate(cel_expr, activation, &arena);
123+
return EvalCheckedExpr(checked_expr, activation, &arena);
93124
}
94125

95-
absl::StatusOr<bool> ParseAndEvaluate(absl::string_view cel_expr,
96-
const AttributeContext& context) {
126+
absl::StatusOr<bool> CompileAndEvaluateWithContext(
127+
absl::string_view cel_expr, const AttributeContext& context) {
128+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Compiler> compiler,
129+
MakeCelCompiler());
130+
131+
CEL_ASSIGN_OR_RETURN(CheckedExpr checked_expr,
132+
CompileToCheckedExpr(*compiler, cel_expr));
133+
97134
Activation activation;
98135
google::protobuf::Arena arena;
99136
// === Start Codelab ===
100137
// Update the activation to bind the AttributeContext.
101138
// === End Codelab ===
102139

103-
return ParseAndEvaluate(cel_expr, activation, &arena);
140+
return EvalCheckedExpr(checked_expr, activation, &arena);
104141
}
105142

106143
} // namespace cel_codelab

codelab/exercise2.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@
2121

2222
namespace cel_codelab {
2323

24-
// Parse a cel expression and evaluate it. Binds a simple boolean to the
24+
// Compile a cel expression and evaluate it. Binds a simple boolean to the
2525
// activation as 'bool_var' for use in the expression.
2626
//
2727
// cel_expr should result in a bool, otherwise an InvalidArgument error is
2828
// returned.
29-
absl::StatusOr<bool> ParseAndEvaluate(absl::string_view cel_expr,
30-
bool bool_var);
29+
absl::StatusOr<bool> CompileAndEvaluateWithBoolVar(absl::string_view cel_expr,
30+
bool bool_var);
3131

32-
// Parse a cel expression and evaluate it. Binds an instance of the
32+
// Compile a cel expression and evaluate it. Binds an instance of the
3333
// AttributeContext message to the activation (binding the subfields directly).
34-
absl::StatusOr<bool> ParseAndEvaluate(
34+
absl::StatusOr<bool> CompileAndEvaluateWithContext(
3535
absl::string_view cel_expr,
3636
const google::rpc::context::AttributeContext& context);
3737

codelab/exercise2_test.cc

+19-11
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,18 @@ using ::google::protobuf::TextFormat;
2929
using ::testing::HasSubstr;
3030

3131
TEST(Exercise2Var, Simple) {
32-
EXPECT_THAT(ParseAndEvaluate("bool_var", false), IsOkAndHolds(false));
33-
EXPECT_THAT(ParseAndEvaluate("bool_var", true), IsOkAndHolds(true));
34-
EXPECT_THAT(ParseAndEvaluate("bool_var || true", false), IsOkAndHolds(true));
35-
EXPECT_THAT(ParseAndEvaluate("bool_var && false", true), IsOkAndHolds(false));
32+
EXPECT_THAT(CompileAndEvaluateWithBoolVar("bool_var", false),
33+
IsOkAndHolds(false));
34+
EXPECT_THAT(CompileAndEvaluateWithBoolVar("bool_var", true),
35+
IsOkAndHolds(true));
36+
EXPECT_THAT(CompileAndEvaluateWithBoolVar("bool_var || true", false),
37+
IsOkAndHolds(true));
38+
EXPECT_THAT(CompileAndEvaluateWithBoolVar("bool_var && false", true),
39+
IsOkAndHolds(false));
3640
}
3741

3842
TEST(Exercise2Var, WrongTypeResultError) {
39-
EXPECT_THAT(ParseAndEvaluate("'not a bool'", false),
43+
EXPECT_THAT(CompileAndEvaluateWithBoolVar("'not a bool'", false),
4044
StatusIs(absl::StatusCode::kInvalidArgument,
4145
HasSubstr("expected 'bool' result got 'string")));
4246
}
@@ -50,13 +54,17 @@ TEST(Exercise2Context, Simple) {
5054
)pb",
5155
&context));
5256

53-
EXPECT_THAT(ParseAndEvaluate("source.ip == '192.168.28.1'", context),
54-
IsOkAndHolds(true));
55-
EXPECT_THAT(ParseAndEvaluate("request.host == 'api.example.com'", context),
57+
EXPECT_THAT(
58+
CompileAndEvaluateWithContext("source.ip == '192.168.28.1'", context),
59+
IsOkAndHolds(true));
60+
EXPECT_THAT(CompileAndEvaluateWithContext("request.host == 'api.example.com'",
61+
context),
5662
IsOkAndHolds(false));
57-
EXPECT_THAT(ParseAndEvaluate("request.host == 'www.example.com'", context),
63+
EXPECT_THAT(CompileAndEvaluateWithContext("request.host == 'www.example.com'",
64+
context),
5865
IsOkAndHolds(true));
59-
EXPECT_THAT(ParseAndEvaluate("destination.ip != '192.168.56.1'", context),
66+
EXPECT_THAT(CompileAndEvaluateWithContext("destination.ip != '192.168.56.1'",
67+
context),
6068
IsOkAndHolds(false));
6169
}
6270

@@ -65,7 +73,7 @@ TEST(Exercise2Context, WrongTypeResultError) {
6573

6674
// For this codelab, we expect the bind default option which will return
6775
// proto api defaults for unset fields.
68-
EXPECT_THAT(ParseAndEvaluate("request.host", context),
76+
EXPECT_THAT(CompileAndEvaluateWithContext("request.host", context),
6977
StatusIs(absl::StatusCode::kInvalidArgument,
7078
HasSubstr("expected 'bool' result got 'string")));
7179
}

codelab/exercise3_test.cc

+12-8
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ using ::google::rpc::context::AttributeContext;
2828

2929
// Helper for a simple CelExpression with no context.
3030
absl::StatusOr<bool> TruthTableTest(absl::string_view statement) {
31-
return ParseAndEvaluate(statement, /*unused*/ false);
31+
return CompileAndEvaluateWithBoolVar(statement, /*unused*/ false);
3232
}
3333

3434
TEST(Exercise3, LogicalOr) {
@@ -85,25 +85,29 @@ TEST(Exercise3, Ternary) {
8585
}
8686

8787
TEST(Exercise3, BadFieldAccess) {
88-
// This type of error is normally caught by the type checker, but we can
89-
// surface it here since we are only parsing. The following expressions are
90-
// mistaken from the field 'request.host'
9188
AttributeContext context;
9289

90+
// This type of error is normally caught by the type checker, to allow
91+
// it to surface here we use the dyn() operator to defer checking to runtime.
92+
// typo-ed field name from 'request.host'
9393
EXPECT_THAT(
94-
ParseAndEvaluate("request.hostname == 'localhost' && true", context),
94+
CompileAndEvaluateWithContext(
95+
"dyn(request).hostname == 'localhost' && true", context),
9596
StatusIs(absl::StatusCode::kNotFound, "no_such_field : hostname"));
9697
// Wrong
9798
EXPECT_THAT(
98-
ParseAndEvaluate("request.hostname == 'localhost' && false", context),
99+
CompileAndEvaluateWithContext(
100+
"dyn(request).hostname == 'localhost' && false", context),
99101
StatusIs(absl::StatusCode::kNotFound, "no_such_field : hostname"));
100102

101103
// Wrong
102104
EXPECT_THAT(
103-
ParseAndEvaluate("request.hostname == 'localhost' || true", context),
105+
CompileAndEvaluateWithContext(
106+
"dyn(request).hostname == 'localhost' || true", context),
104107
StatusIs(absl::StatusCode::kNotFound, "no_such_field : hostname"));
105108
EXPECT_THAT(
106-
ParseAndEvaluate("request.hostname == 'localhost' || false", context),
109+
CompileAndEvaluateWithContext(
110+
"dyn(request).hostname == 'localhost' || false", context),
107111
StatusIs(absl::StatusCode::kNotFound, "no_such_field : hostname"));
108112
}
109113

codelab/solutions/BUILD

+8-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ cc_library(
5252
srcs = ["exercise2.cc"],
5353
hdrs = ["//codelab:exercise2.h"],
5454
deps = [
55+
"//checker:type_checker_builder",
56+
"//codelab:cel_compiler",
57+
"//common:decl",
58+
"//common:type",
59+
"//compiler",
60+
"//compiler:compiler_factory",
61+
"//compiler:standard_library",
5562
"//eval/public:activation",
5663
"//eval/public:activation_bind_helper",
5764
"//eval/public:builtin_func_registrar",
@@ -60,10 +67,10 @@ cc_library(
6067
"//eval/public:cel_options",
6168
"//eval/public:cel_value",
6269
"//internal:status_macros",
63-
"//parser",
6470
"@com_google_absl//absl/status",
6571
"@com_google_absl//absl/status:statusor",
6672
"@com_google_absl//absl/strings",
73+
"@com_google_cel_spec//proto/cel/expr:checked_cc_proto",
6774
"@com_google_cel_spec//proto/cel/expr:syntax_cc_proto",
6875
"@com_google_googleapis//google/rpc/context:attribute_context_cc_proto",
6976
"@com_google_protobuf//:protobuf",

0 commit comments

Comments
 (0)