Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions common/expr_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ namespace tools {
class ProtoToPredicateBuilder;
}

namespace parser_internal {
template <typename ExprNode>
class AstFactoryInterface;
}

class ExprFactory {
protected:
// `IsExprLike` determines whether `T` is some `Expr`. Currently that means
Expand Down Expand Up @@ -385,6 +390,7 @@ class ExprFactory {
friend class ParserMacroExprFactory;
friend class OptimizerExprFactory;
friend class tools::ProtoToPredicateBuilder;
friend class parser_internal::AstFactoryInterface<Expr>;

ExprFactory() : accu_var_(kAccumulatorVariableName) {}

Expand Down
65 changes: 65 additions & 0 deletions parser/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,30 @@
# limitations under the License.

load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//bazel:antlr.bzl", "antlr_cc_library")

package(default_visibility = ["//visibility:public"])

licenses(["notice"])

cc_library(
name = "ast_factory_interface",
hdrs = ["ast_factory_interface.h"],
)

cc_library(
name = "ast_factory",
hdrs = ["ast_factory.h"],
deps = [
":ast_factory_interface",
"//common:constant",
"//common:expr",
"//common:expr_factory",
"@com_google_absl//absl/strings:string_view",
],
)

cc_library(
name = "options",
hdrs = ["options.h"],
Expand All @@ -29,3 +47,50 @@ antlr_cc_library(
src = "Cel.g4",
package = "cel_parser_internal",
)

cc_library(
name = "lexer",
srcs = ["lexer.cc"],
hdrs = ["lexer.h"],
deps = [
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:no_destructor",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:function_ref",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
],
)

cc_test(
name = "ast_factory_test",
srcs = ["ast_factory_test.cc"],
deps = [
":ast_factory",
"//common:expr",
"//internal:testing",
"@com_google_absl//absl/strings:string_view",
],
)

cc_test(
name = "lexer_test",
srcs = ["lexer_test.cc"],
deps = [
":lexer",
"//internal:testing",
],
)

cc_test(
name = "lexer_fuzzer",
size = "large",
srcs = ["lexer_fuzzer.cc"],
deps = [
":lexer",
"//internal:testing",
"//testing/fuzzing:fuzztest",
],
)
182 changes: 182 additions & 0 deletions parser/internal/ast_factory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_H_
#define THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_H_

#include <cstdint>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "absl/strings/string_view.h"
#include "common/constant.h"
#include "common/expr.h"
#include "common/expr_factory.h"
#include "parser/internal/ast_factory_interface.h"

namespace cel::parser_internal {

// Explicit specialization of `AstFactoryInterface` for `cel::Expr` AST nodes.

template <>
class ListNodeBuilder<cel::Expr> {
public:
explicit ListNodeBuilder(int64_t id) {
expr_.set_id(id);
expr_.mutable_list_expr();
}

ListNodeBuilder& Add(cel::Expr element, bool optional = false) {
cel::ListExpr& list_val = expr_.mutable_list_expr();
cel::ListExprElement expr_element;
expr_element.set_expr(std::move(element));
expr_element.set_optional(optional);
list_val.mutable_elements().push_back(std::move(expr_element));
return *this;
}

cel::Expr Build() { return std::move(expr_); }

private:
cel::Expr expr_;
};

template <>
class MapNodeBuilder<cel::Expr> {
public:
explicit MapNodeBuilder(int64_t id) {
expr_.set_id(id);
expr_.mutable_map_expr();
}

MapNodeBuilder& Add(int64_t id, cel::Expr key, cel::Expr value,
bool optional = false) {
cel::MapExpr& map_val = expr_.mutable_map_expr();
cel::MapExprEntry entry;
entry.set_id(id);
entry.set_key(std::move(key));
entry.set_value(std::move(value));
entry.set_optional(optional);
map_val.mutable_entries().push_back(std::move(entry));
return *this;
}

cel::Expr Build() { return std::move(expr_); }

private:
cel::Expr expr_;
};

template <>
class StructNodeBuilder<cel::Expr> {
public:
explicit StructNodeBuilder(int64_t id, std::string name) {
expr_.set_id(id);
expr_.mutable_struct_expr().set_name(std::move(name));
}

StructNodeBuilder& Add(int64_t id, std::string name, cel::Expr value,
bool optional = false) {
cel::StructExpr& struct_val = expr_.mutable_struct_expr();
cel::StructExprField field;
field.set_id(id);
field.set_name(std::move(name));
field.set_value(std::move(value));
field.set_optional(optional);
struct_val.mutable_fields().push_back(std::move(field));
return *this;
}

cel::Expr Build() { return std::move(expr_); }

private:
cel::Expr expr_;
};

template <>
class AstFactoryInterface<cel::Expr> : public cel::ExprFactory {
public:
AstFactoryInterface() = default;
AstFactoryInterface(const AstFactoryInterface&) = delete;
AstFactoryInterface(AstFactoryInterface&&) = delete;
AstFactoryInterface& operator=(const AstFactoryInterface&) = delete;
AstFactoryInterface& operator=(AstFactoryInterface&&) = delete;

~AstFactoryInterface() = default;

// Node inspection and encapsulation API
int64_t GetId(const cel::Expr& expr) const { return expr.id(); }

bool IsEmpty(const cel::Expr& expr) const { return expr.id() == 0; }

bool IsConst(const cel::Expr& expr) const { return expr.has_const_expr(); }

bool IsIdent(const cel::Expr& expr) const { return expr.has_ident_expr(); }

absl::string_view GetIdentName(const cel::Expr& expr) const {
return expr.has_ident_expr() ? absl::string_view(expr.ident_expr().name())
: absl::string_view();
}

bool IsSelect(const cel::Expr& expr) const { return expr.has_select_expr(); }

bool IsPresenceTest(const cel::Expr& expr) const {
return expr.has_select_expr() && expr.select_expr().test_only();
}

const cel::Expr* GetSelectOperand(const cel::Expr& expr) const {
return expr.has_select_expr() ? &expr.select_expr().operand() : nullptr;
}

absl::string_view GetSelectField(const cel::Expr& expr) const {
return expr.has_select_expr()
? absl::string_view(expr.select_expr().field())
: absl::string_view();
}

// Node creation API
using cel::ExprFactory::NewBoolConst;
using cel::ExprFactory::NewBytesConst;
using cel::ExprFactory::NewCall;
using cel::ExprFactory::NewDoubleConst;
using cel::ExprFactory::NewIdent;
using cel::ExprFactory::NewIntConst;
using cel::ExprFactory::NewMemberCall;
using cel::ExprFactory::NewNullConst;
using cel::ExprFactory::NewPresenceTest;
using cel::ExprFactory::NewSelect;
using cel::ExprFactory::NewStringConst;
using cel::ExprFactory::NewUintConst;
using cel::ExprFactory::NewUnspecified;

ListNodeBuilder<cel::Expr> NewListBuilder(int64_t id) {
return ListNodeBuilder<cel::Expr>(id);
}

StructNodeBuilder<cel::Expr> NewStructBuilder(int64_t id, std::string name) {
return StructNodeBuilder<cel::Expr>(id, std::move(name));
}

MapNodeBuilder<cel::Expr> NewMapBuilder(int64_t id) {
return MapNodeBuilder<cel::Expr>(id);
}
};

using AstFactory = AstFactoryInterface<cel::Expr>;

} // namespace cel::parser_internal

#endif // THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_H_
104 changes: 104 additions & 0 deletions parser/internal/ast_factory_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_INTERFACE_H_
#define THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_INTERFACE_H_

#include <cstdint>
#include <string>
#include <string_view>
#include <vector>

namespace cel::parser_internal {

// Interface for decoupling parser logic from the underlying AST node
// data structures.
//
// By parameterizing the parser and factory on `ExprNode`, alternative AST node
// representations (such as `cel::Expr`) can be constructed without modifying
// parser rules.
//
// To implement AST construction using an alternative AST structure:
// 1. Define or specify your custom node type `MyNode`.
// 2. Implement a concrete factory specialization `AstFactoryInterface<MyNode>`
// that provides inspection (`GetId`, `IsSelect`, etc.) and creation
// (`NewCall`, `NewListBuilder`, etc.) operations for `MyNode`.
// 3. Instantiate the parser worker with your node type:
// `PrattParserWorker<MyNode>`.

template <typename ExprNode>
class ListNodeBuilder {
public:
ListNodeBuilder& Add(ExprNode element, bool optional = false);
ExprNode Build();
};

template <typename ExprNode>
class MapNodeBuilder {
public:
MapNodeBuilder& Add(int64_t id, ExprNode key, ExprNode value,
bool optional = false);
ExprNode Build();
};

template <typename ExprNode>
class StructNodeBuilder {
public:
StructNodeBuilder& Add(int64_t id, std::string name, ExprNode value,
bool optional = false);
ExprNode Build();
};

template <typename ExprNode>
class AstFactoryInterface {
public:
AstFactoryInterface() = default;
AstFactoryInterface(const AstFactoryInterface&) = delete;
AstFactoryInterface(AstFactoryInterface&&) = delete;
AstFactoryInterface& operator=(const AstFactoryInterface&) = delete;
AstFactoryInterface& operator=(AstFactoryInterface&&) = delete;

int64_t GetId(const ExprNode& expr) const;
bool IsEmpty(const ExprNode& expr) const;
bool IsConst(const ExprNode& expr) const;
bool IsIdent(const ExprNode& expr) const;
std::string_view GetIdentName(const ExprNode& expr) const;
bool IsSelect(const ExprNode& expr) const;
bool IsPresenceTest(const ExprNode& expr) const;
const ExprNode* GetSelectOperand(const ExprNode& expr) const;
std::string_view GetSelectField(const ExprNode& expr) const;

ExprNode NewUnspecified(int64_t id);
ExprNode NewNullConst(int64_t id);
ExprNode NewBoolConst(int64_t id, bool value);
ExprNode NewIntConst(int64_t id, int64_t value);
ExprNode NewUintConst(int64_t id, uint64_t value);
ExprNode NewDoubleConst(int64_t id, double value);
ExprNode NewBytesConst(int64_t id, std::string value);
ExprNode NewStringConst(int64_t id, std::string value);
ExprNode NewIdent(int64_t id, std::string name);
ExprNode NewSelect(int64_t id, ExprNode operand, std::string field);
ExprNode NewPresenceTest(int64_t id, ExprNode operand, std::string field);
ExprNode NewCall(int64_t id, std::string function,
std::vector<ExprNode> args);
ExprNode NewMemberCall(int64_t id, std::string function, ExprNode target,
std::vector<ExprNode> args);
ListNodeBuilder<ExprNode> NewListBuilder(int64_t id);
MapNodeBuilder<ExprNode> NewMapBuilder(int64_t id);
StructNodeBuilder<ExprNode> NewStructBuilder(int64_t id, std::string name);
};

} // namespace cel::parser_internal

#endif // THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_AST_FACTORY_INTERFACE_H_
Loading
Loading