Skip to content

Commit 11d6651

Browse files
jckingcopybara-github
authored andcommitted
Fully implement optional syntax for parser
PiperOrigin-RevId: 572605079
1 parent 4b772f9 commit 11d6651

File tree

15 files changed

+250
-68
lines changed

15 files changed

+250
-68
lines changed

base/ast_internal/expr.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ bool CreateStruct::Entry::operator==(const Entry& other) const {
7171
} else if (has_map_key() && other.has_map_key()) {
7272
has_same_key = map_key() == other.map_key();
7373
}
74-
return id_ == other.id_ && has_same_key && value() == other.value();
74+
return id_ == other.id_ && has_same_key && value() == other.value() &&
75+
optional_entry_ == other.optional_entry();
7576
}
7677

7778
const Expr& Comprehension::iter_range() const {

base/ast_internal/expr.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,18 +353,30 @@ class CreateList {
353353
public:
354354
CreateList() = default;
355355
explicit CreateList(std::vector<Expr> elements);
356+
CreateList(std::vector<Expr> elements, std::vector<int32_t> optional_indices);
356357

357358
void set_elements(std::vector<Expr> elements);
358359

359360
const std::vector<Expr>& elements() const { return elements_; }
360361

361362
std::vector<Expr>& mutable_elements() { return elements_; }
362363

364+
void set_optional_indices(std::vector<int32_t> optional_indices) {
365+
optional_indices_ = std::move(optional_indices);
366+
}
367+
368+
const std::vector<int32_t>& optional_indices() const {
369+
return optional_indices_;
370+
}
371+
372+
std::vector<int32_t>& optional_indices() { return optional_indices_; }
373+
363374
bool operator==(const CreateList& other) const;
364375

365376
private:
366377
// The elements part of the list.
367378
std::vector<Expr> elements_;
379+
std::vector<int32_t> optional_indices_;
368380
};
369381

370382
// A map or message creation expression.
@@ -379,8 +391,12 @@ class CreateStruct {
379391
public:
380392
using KeyKind = absl::variant<std::string, std::unique_ptr<Expr>>;
381393
Entry() = default;
382-
Entry(int64_t id, KeyKind key_kind, std::unique_ptr<Expr> value)
383-
: id_(id), key_kind_(std::move(key_kind)), value_(std::move(value)) {}
394+
Entry(int64_t id, KeyKind key_kind, std::unique_ptr<Expr> value,
395+
bool optional_entry = false)
396+
: id_(id),
397+
key_kind_(std::move(key_kind)),
398+
value_(std::move(value)),
399+
optional_entry_(optional_entry) {}
384400

385401
void set_id(int64_t id) { id_ = id; }
386402

@@ -437,6 +453,12 @@ class CreateStruct {
437453
return *value_;
438454
}
439455

456+
bool optional_entry() const { return optional_entry_; }
457+
458+
void set_optional_entry(bool optional_entry) {
459+
optional_entry_ = optional_entry;
460+
}
461+
440462
bool operator==(const Entry& other) const;
441463

442464
bool operator!=(const Entry& other) const { return !operator==(other); }
@@ -450,6 +472,7 @@ class CreateStruct {
450472
KeyKind key_kind_;
451473
// Required. The value assigned to the key.
452474
std::unique_ptr<Expr> value_;
475+
bool optional_entry_ = false;
453476
};
454477

455478
CreateStruct() = default;
@@ -1602,12 +1625,18 @@ inline void Call::set_args(std::vector<Expr> args) { args_ = std::move(args); }
16021625
inline CreateList::CreateList(std::vector<Expr> elements)
16031626
: elements_(std::move(elements)) {}
16041627

1628+
inline CreateList::CreateList(std::vector<Expr> elements,
1629+
std::vector<int32_t> optional_indices)
1630+
: elements_(std::move(elements)),
1631+
optional_indices_(std::move(optional_indices)) {}
1632+
16051633
inline void CreateList::set_elements(std::vector<Expr> elements) {
16061634
elements_ = std::move(elements);
16071635
}
16081636

16091637
inline bool CreateList::operator==(const CreateList& other) const {
1610-
return elements_ == other.elements_;
1638+
return elements_ == other.elements_ &&
1639+
optional_indices_ == other.optional_indices_;
16111640
}
16121641

16131642
inline FunctionType::FunctionType(std::unique_ptr<Type> result_type,

common/operators.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ const char* CelOperator::FILTER = "filter";
167167
const char* CelOperator::NOT_STRICTLY_FALSE = "@not_strictly_false";
168168
const char* CelOperator::IN = "@in";
169169

170+
const absl::string_view CelOperator::OPT_INDEX = "_[?_]";
171+
const absl::string_view CelOperator::OPT_SELECT = "_?._";
172+
170173
int LookupPrecedence(const std::string& op) {
171174
auto precs = Precedences();
172175
auto p = precs.find(op);

common/operators.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct CelOperator {
4444
// Named operators, must not have be valid identifiers.
4545
static const char* NOT_STRICTLY_FALSE;
4646
static const char* IN;
47+
48+
static const absl::string_view OPT_INDEX;
49+
static const absl::string_view OPT_SELECT;
4750
};
4851

4952
// These give access to all or some specific precedence value.

extensions/protobuf/ast_converters.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ absl::StatusOr<CreateList> ConvertCreateList(
106106
std::stack<ConversionStackEntry>& stack) {
107107
CreateList ret_val;
108108
ret_val.set_elements(std::vector<Expr>(create_list.elements_size()));
109+
ret_val.set_optional_indices(
110+
std::vector<int32_t>(create_list.optional_indices().begin(),
111+
create_list.optional_indices().end()));
109112

110113
for (int i = 0; i < ret_val.elements().size(); i++) {
111114
stack.push({&ret_val.mutable_elements()[i], &create_list.elements(i)});
@@ -142,7 +145,7 @@ absl::StatusOr<CreateStruct::Entry> ConvertCreateStructEntry(
142145
"google::api::expr::v1alpha1::Expr::CreateStruct::Entry missing value");
143146
}
144147
CreateStruct::Entry result(entry.id(), std::move(native_key),
145-
std::make_unique<Expr>());
148+
std::make_unique<Expr>(), entry.optional_entry());
146149
stack.push({&result.mutable_value(), &entry.value()});
147150

148151
return result;

extensions/protobuf/ast_converters_test.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace {
3737
using ::cel::ast_internal::NullValue;
3838
using ::cel::ast_internal::PrimitiveType;
3939
using ::cel::ast_internal::WellKnownType;
40+
using testing::ElementsAreArray;
4041
using cel::internal::StatusIs;
4142

4243
TEST(AstConvertersTest, IdentToNative) {
@@ -110,6 +111,7 @@ TEST(AstConvertersTest, CreateListToNative) {
110111
list_expr {
111112
elements { ident_expr { name: "elem1" } }
112113
elements { ident_expr { name: "elem2" } }
114+
optional_indices: [ 0 ]
113115
}
114116
)pb",
115117
&expr));
@@ -124,6 +126,8 @@ TEST(AstConvertersTest, CreateListToNative) {
124126
auto& native_elem2 = native_create_list.elements()[1];
125127
ASSERT_TRUE(native_elem2.has_ident_expr());
126128
ASSERT_EQ(native_elem2.ident_expr().name(), "elem2");
129+
ASSERT_THAT(native_create_list.optional_indices(),
130+
ElementsAreArray(expr.list_expr().optional_indices()));
127131
}
128132

129133
TEST(AstConvertersTest, CreateStructToNative) {
@@ -135,6 +139,7 @@ TEST(AstConvertersTest, CreateStructToNative) {
135139
id: 1
136140
field_key: "key1"
137141
value { ident_expr { name: "value1" } }
142+
optional_entry: true
138143
}
139144
entries {
140145
id: 2
@@ -155,6 +160,7 @@ TEST(AstConvertersTest, CreateStructToNative) {
155160
ASSERT_EQ(native_entry1.field_key(), "key1");
156161
ASSERT_TRUE(native_entry1.value().has_ident_expr());
157162
ASSERT_EQ(native_entry1.value().ident_expr().name(), "value1");
163+
ASSERT_TRUE(native_entry1.optional_entry());
158164
auto& native_entry2 = native_struct.entries()[1];
159165
EXPECT_EQ(native_entry2.id(), 2);
160166
ASSERT_TRUE(native_entry2.has_map_key());

parser/macro.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@
1414

1515
#include "parser/macro.h"
1616

17+
#include <cstddef>
1718
#include <cstdint>
19+
#include <memory>
1820
#include <utility>
21+
#include <vector>
1922

2023
#include "absl/status/status.h"
24+
#include "absl/status/statusor.h"
2125
#include "absl/strings/str_cat.h"
2226
#include "absl/strings/str_format.h"
27+
#include "absl/strings/string_view.h"
2328
#include "common/operators.h"
2429
#include "internal/lexis.h"
2530
#include "parser/source_factory.h"
@@ -169,4 +174,77 @@ std::vector<Macro> Macro::AllMacros() {
169174
};
170175
}
171176

177+
Macro Macro::OptMap() {
178+
return Macro(
179+
"optMap", 2,
180+
[](const std::shared_ptr<SourceFactory>& sf, int64_t macro_id,
181+
const Expr& target, const std::vector<Expr>& args) -> Expr {
182+
if (args.size() != 2) {
183+
return sf->ReportError(args[0].id(), "optMap() requires 2 arguments");
184+
}
185+
if (!args[0].has_ident_expr()) {
186+
return sf->ReportError(
187+
args[0].id(),
188+
"optMap() variable name must be a simple identifier");
189+
}
190+
const auto& var_name = args[0].ident_expr().name();
191+
const auto& map_expr = args[1];
192+
193+
std::vector<Expr> call_args;
194+
call_args.resize(3);
195+
call_args[0] =
196+
sf->NewReceiverCallForMacro(macro_id, "hasValue", target, {});
197+
auto iter_range = sf->NewListForMacro(macro_id, {});
198+
auto accu_init =
199+
sf->NewReceiverCallForMacro(macro_id, "value", target, {});
200+
auto condition = sf->NewLiteralBoolForMacro(macro_id, false);
201+
auto step = sf->NewIdentForMacro(macro_id, var_name);
202+
const auto& result = map_expr;
203+
auto fold = sf->FoldForMacro(macro_id, "#unused", iter_range, var_name,
204+
accu_init, condition, step, result);
205+
call_args[1] =
206+
sf->NewGlobalCallForMacro(macro_id, "optional.of", {fold});
207+
call_args[2] = sf->NewGlobalCallForMacro(macro_id, "optional.none", {});
208+
return sf->NewGlobalCallForMacro(macro_id, CelOperator::CONDITIONAL,
209+
call_args);
210+
},
211+
true);
212+
}
213+
214+
Macro Macro::OptFlatMap() {
215+
return Macro(
216+
"optFlatMap", 2,
217+
[](const std::shared_ptr<SourceFactory>& sf, int64_t macro_id,
218+
const Expr& target, const std::vector<Expr>& args) -> Expr {
219+
if (args.size() != 2) {
220+
return sf->ReportError(args[0].id(),
221+
"optFlatMap() requires 2 arguments");
222+
}
223+
if (!args[0].has_ident_expr()) {
224+
return sf->ReportError(
225+
args[0].id(),
226+
"optFlatMap() variable name must be a simple identifier");
227+
}
228+
const auto& var_name = args[0].ident_expr().name();
229+
const auto& map_expr = args[1];
230+
std::vector<Expr> call_args;
231+
call_args.resize(3);
232+
call_args[0] =
233+
sf->NewReceiverCallForMacro(macro_id, "hasValue", target, {});
234+
auto iter_range = sf->NewListForMacro(macro_id, {});
235+
auto accu_init =
236+
sf->NewReceiverCallForMacro(macro_id, "value", target, {});
237+
auto condition = sf->NewLiteralBoolForMacro(macro_id, false);
238+
auto step = sf->NewIdentForMacro(macro_id, var_name);
239+
const auto& result = map_expr;
240+
call_args[1] =
241+
sf->FoldForMacro(macro_id, "#unused", iter_range, var_name,
242+
accu_init, condition, step, result);
243+
call_args[2] = sf->NewGlobalCallForMacro(macro_id, "optional.none", {});
244+
return sf->NewGlobalCallForMacro(macro_id, CelOperator::CONDITIONAL,
245+
call_args);
246+
},
247+
true);
248+
}
249+
172250
} // namespace cel

parser/macro.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class Macro final {
123123

124124
static std::vector<Macro> AllMacros();
125125

126+
static Macro OptMap();
127+
128+
static Macro OptFlatMap();
129+
126130
private:
127131
std::string key_;
128132
size_t arg_count_;

parser/options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct ParserOptions final {
4444

4545
// Add macro calls to macro_calls list in source_info.
4646
bool add_macro_calls = ::cel_parser_internal::kDefaultAddMacroCalls;
47+
48+
// Enable support for optional syntax.
49+
bool enable_optional_syntax = false;
4750
};
4851

4952
} // namespace cel

0 commit comments

Comments
 (0)