Skip to content

Commit

Permalink
Fully implement optional syntax for parser
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 572605079
  • Loading branch information
jcking authored and copybara-github committed Oct 11, 2023
1 parent 4b772f9 commit 11d6651
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 68 deletions.
3 changes: 2 additions & 1 deletion base/ast_internal/expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ bool CreateStruct::Entry::operator==(const Entry& other) const {
} else if (has_map_key() && other.has_map_key()) {
has_same_key = map_key() == other.map_key();
}
return id_ == other.id_ && has_same_key && value() == other.value();
return id_ == other.id_ && has_same_key && value() == other.value() &&
optional_entry_ == other.optional_entry();
}

const Expr& Comprehension::iter_range() const {
Expand Down
35 changes: 32 additions & 3 deletions base/ast_internal/expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,18 +353,30 @@ class CreateList {
public:
CreateList() = default;
explicit CreateList(std::vector<Expr> elements);
CreateList(std::vector<Expr> elements, std::vector<int32_t> optional_indices);

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

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

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

void set_optional_indices(std::vector<int32_t> optional_indices) {
optional_indices_ = std::move(optional_indices);
}

const std::vector<int32_t>& optional_indices() const {
return optional_indices_;
}

std::vector<int32_t>& optional_indices() { return optional_indices_; }

bool operator==(const CreateList& other) const;

private:
// The elements part of the list.
std::vector<Expr> elements_;
std::vector<int32_t> optional_indices_;
};

// A map or message creation expression.
Expand All @@ -379,8 +391,12 @@ class CreateStruct {
public:
using KeyKind = absl::variant<std::string, std::unique_ptr<Expr>>;
Entry() = default;
Entry(int64_t id, KeyKind key_kind, std::unique_ptr<Expr> value)
: id_(id), key_kind_(std::move(key_kind)), value_(std::move(value)) {}
Entry(int64_t id, KeyKind key_kind, std::unique_ptr<Expr> value,
bool optional_entry = false)
: id_(id),
key_kind_(std::move(key_kind)),
value_(std::move(value)),
optional_entry_(optional_entry) {}

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

Expand Down Expand Up @@ -437,6 +453,12 @@ class CreateStruct {
return *value_;
}

bool optional_entry() const { return optional_entry_; }

void set_optional_entry(bool optional_entry) {
optional_entry_ = optional_entry;
}

bool operator==(const Entry& other) const;

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

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

inline CreateList::CreateList(std::vector<Expr> elements,
std::vector<int32_t> optional_indices)
: elements_(std::move(elements)),
optional_indices_(std::move(optional_indices)) {}

inline void CreateList::set_elements(std::vector<Expr> elements) {
elements_ = std::move(elements);
}

inline bool CreateList::operator==(const CreateList& other) const {
return elements_ == other.elements_;
return elements_ == other.elements_ &&
optional_indices_ == other.optional_indices_;
}

inline FunctionType::FunctionType(std::unique_ptr<Type> result_type,
Expand Down
3 changes: 3 additions & 0 deletions common/operators.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ const char* CelOperator::FILTER = "filter";
const char* CelOperator::NOT_STRICTLY_FALSE = "@not_strictly_false";
const char* CelOperator::IN = "@in";

const absl::string_view CelOperator::OPT_INDEX = "_[?_]";
const absl::string_view CelOperator::OPT_SELECT = "_?._";

int LookupPrecedence(const std::string& op) {
auto precs = Precedences();
auto p = precs.find(op);
Expand Down
3 changes: 3 additions & 0 deletions common/operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ struct CelOperator {
// Named operators, must not have be valid identifiers.
static const char* NOT_STRICTLY_FALSE;
static const char* IN;

static const absl::string_view OPT_INDEX;
static const absl::string_view OPT_SELECT;
};

// These give access to all or some specific precedence value.
Expand Down
5 changes: 4 additions & 1 deletion extensions/protobuf/ast_converters.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ absl::StatusOr<CreateList> ConvertCreateList(
std::stack<ConversionStackEntry>& stack) {
CreateList ret_val;
ret_val.set_elements(std::vector<Expr>(create_list.elements_size()));
ret_val.set_optional_indices(
std::vector<int32_t>(create_list.optional_indices().begin(),
create_list.optional_indices().end()));

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

return result;
Expand Down
6 changes: 6 additions & 0 deletions extensions/protobuf/ast_converters_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace {
using ::cel::ast_internal::NullValue;
using ::cel::ast_internal::PrimitiveType;
using ::cel::ast_internal::WellKnownType;
using testing::ElementsAreArray;
using cel::internal::StatusIs;

TEST(AstConvertersTest, IdentToNative) {
Expand Down Expand Up @@ -110,6 +111,7 @@ TEST(AstConvertersTest, CreateListToNative) {
list_expr {
elements { ident_expr { name: "elem1" } }
elements { ident_expr { name: "elem2" } }
optional_indices: [ 0 ]
}
)pb",
&expr));
Expand All @@ -124,6 +126,8 @@ TEST(AstConvertersTest, CreateListToNative) {
auto& native_elem2 = native_create_list.elements()[1];
ASSERT_TRUE(native_elem2.has_ident_expr());
ASSERT_EQ(native_elem2.ident_expr().name(), "elem2");
ASSERT_THAT(native_create_list.optional_indices(),
ElementsAreArray(expr.list_expr().optional_indices()));
}

TEST(AstConvertersTest, CreateStructToNative) {
Expand All @@ -135,6 +139,7 @@ TEST(AstConvertersTest, CreateStructToNative) {
id: 1
field_key: "key1"
value { ident_expr { name: "value1" } }
optional_entry: true
}
entries {
id: 2
Expand All @@ -155,6 +160,7 @@ TEST(AstConvertersTest, CreateStructToNative) {
ASSERT_EQ(native_entry1.field_key(), "key1");
ASSERT_TRUE(native_entry1.value().has_ident_expr());
ASSERT_EQ(native_entry1.value().ident_expr().name(), "value1");
ASSERT_TRUE(native_entry1.optional_entry());
auto& native_entry2 = native_struct.entries()[1];
EXPECT_EQ(native_entry2.id(), 2);
ASSERT_TRUE(native_entry2.has_map_key());
Expand Down
78 changes: 78 additions & 0 deletions parser/macro.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@

#include "parser/macro.h"

#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "common/operators.h"
#include "internal/lexis.h"
#include "parser/source_factory.h"
Expand Down Expand Up @@ -169,4 +174,77 @@ std::vector<Macro> Macro::AllMacros() {
};
}

Macro Macro::OptMap() {
return Macro(
"optMap", 2,
[](const std::shared_ptr<SourceFactory>& sf, int64_t macro_id,
const Expr& target, const std::vector<Expr>& args) -> Expr {
if (args.size() != 2) {
return sf->ReportError(args[0].id(), "optMap() requires 2 arguments");
}
if (!args[0].has_ident_expr()) {
return sf->ReportError(
args[0].id(),
"optMap() variable name must be a simple identifier");
}
const auto& var_name = args[0].ident_expr().name();
const auto& map_expr = args[1];

std::vector<Expr> call_args;
call_args.resize(3);
call_args[0] =
sf->NewReceiverCallForMacro(macro_id, "hasValue", target, {});
auto iter_range = sf->NewListForMacro(macro_id, {});
auto accu_init =
sf->NewReceiverCallForMacro(macro_id, "value", target, {});
auto condition = sf->NewLiteralBoolForMacro(macro_id, false);
auto step = sf->NewIdentForMacro(macro_id, var_name);
const auto& result = map_expr;
auto fold = sf->FoldForMacro(macro_id, "#unused", iter_range, var_name,
accu_init, condition, step, result);
call_args[1] =
sf->NewGlobalCallForMacro(macro_id, "optional.of", {fold});
call_args[2] = sf->NewGlobalCallForMacro(macro_id, "optional.none", {});
return sf->NewGlobalCallForMacro(macro_id, CelOperator::CONDITIONAL,
call_args);
},
true);
}

Macro Macro::OptFlatMap() {
return Macro(
"optFlatMap", 2,
[](const std::shared_ptr<SourceFactory>& sf, int64_t macro_id,
const Expr& target, const std::vector<Expr>& args) -> Expr {
if (args.size() != 2) {
return sf->ReportError(args[0].id(),
"optFlatMap() requires 2 arguments");
}
if (!args[0].has_ident_expr()) {
return sf->ReportError(
args[0].id(),
"optFlatMap() variable name must be a simple identifier");
}
const auto& var_name = args[0].ident_expr().name();
const auto& map_expr = args[1];
std::vector<Expr> call_args;
call_args.resize(3);
call_args[0] =
sf->NewReceiverCallForMacro(macro_id, "hasValue", target, {});
auto iter_range = sf->NewListForMacro(macro_id, {});
auto accu_init =
sf->NewReceiverCallForMacro(macro_id, "value", target, {});
auto condition = sf->NewLiteralBoolForMacro(macro_id, false);
auto step = sf->NewIdentForMacro(macro_id, var_name);
const auto& result = map_expr;
call_args[1] =
sf->FoldForMacro(macro_id, "#unused", iter_range, var_name,
accu_init, condition, step, result);
call_args[2] = sf->NewGlobalCallForMacro(macro_id, "optional.none", {});
return sf->NewGlobalCallForMacro(macro_id, CelOperator::CONDITIONAL,
call_args);
},
true);
}

} // namespace cel
4 changes: 4 additions & 0 deletions parser/macro.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class Macro final {

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

static Macro OptMap();

static Macro OptFlatMap();

private:
std::string key_;
size_t arg_count_;
Expand Down
3 changes: 3 additions & 0 deletions parser/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ struct ParserOptions final {

// Add macro calls to macro_calls list in source_info.
bool add_macro_calls = ::cel_parser_internal::kDefaultAddMacroCalls;

// Enable support for optional syntax.
bool enable_optional_syntax = false;
};

} // namespace cel
Expand Down
Loading

0 comments on commit 11d6651

Please sign in to comment.