Skip to content

Commit a43cbbf

Browse files
jnthntatumcopybara-github
authored andcommitted
Add ParserBuilderInterface and ParserInterface classes. Add factory for stateful parser object.
PiperOrigin-RevId: 693047557
1 parent af71fd2 commit a43cbbf

File tree

5 files changed

+216
-0
lines changed

5 files changed

+216
-0
lines changed

parser/BUILD

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ cc_library(
3232
":macro_expr_factory",
3333
":macro_registry",
3434
":options",
35+
":parser_interface",
3536
":source_factory",
37+
"//base/ast_internal:ast_impl",
3638
"//base/ast_internal:expr",
3739
"//common:ast",
3840
"//common:constant",
@@ -51,6 +53,7 @@ cc_library(
5153
"@com_google_absl//absl/container:btree",
5254
"@com_google_absl//absl/container:flat_hash_map",
5355
"@com_google_absl//absl/functional:overload",
56+
"@com_google_absl//absl/log:absl_check",
5457
"@com_google_absl//absl/memory",
5558
"@com_google_absl//absl/status",
5659
"@com_google_absl//absl/status:statusor",
@@ -173,12 +176,15 @@ cc_test(
173176
":options",
174177
":parser",
175178
":source_factory",
179+
"//base/ast_internal:ast_impl",
176180
"//common:constant",
177181
"//common:expr",
182+
"//common:source",
178183
"//internal:benchmark",
179184
"//internal:testing",
180185
"//testutil:expr_printer",
181186
"@com_google_absl//absl/algorithm:container",
187+
"@com_google_absl//absl/status",
182188
"@com_google_absl//absl/status:status_matchers",
183189
"@com_google_absl//absl/strings",
184190
"@com_google_absl//absl/strings:str_format",
@@ -199,3 +205,16 @@ cc_library(
199205
"@com_google_absl//absl/status",
200206
],
201207
)
208+
209+
cc_library(
210+
name = "parser_interface",
211+
hdrs = ["parser_interface.h"],
212+
deps = [
213+
":macro",
214+
":options",
215+
"//common:ast",
216+
"//common:source",
217+
"@com_google_absl//absl/status",
218+
"@com_google_absl//absl/status:statusor",
219+
],
220+
)

parser/parser.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "absl/container/btree_map.h"
3737
#include "absl/container/flat_hash_map.h"
3838
#include "absl/functional/overload.h"
39+
#include "absl/log/absl_check.h"
3940
#include "absl/memory/memory.h"
4041
#include "absl/status/status.h"
4142
#include "absl/status/statusor.h"
@@ -50,6 +51,7 @@
5051
#include "absl/types/span.h"
5152
#include "absl/types/variant.h"
5253
#include "antlr4-runtime.h"
54+
#include "base/ast_internal/ast_impl.h"
5355
#include "base/ast_internal/expr.h"
5456
#include "common/ast.h"
5557
#include "common/constant.h"
@@ -69,6 +71,7 @@
6971
#include "parser/macro_expr_factory.h"
7072
#include "parser/macro_registry.h"
7173
#include "parser/options.h"
74+
#include "parser/parser_interface.h"
7275
#include "parser/source_factory.h"
7376

7477
namespace google::api::expr::parser {
@@ -1659,6 +1662,61 @@ absl::StatusOr<ParseResult> ParseImpl(const cel::Source& source,
16591662
}
16601663
}
16611664

1665+
class ParserImpl : public cel::Parser {
1666+
public:
1667+
explicit ParserImpl(const ParserOptions& options,
1668+
cel::MacroRegistry macro_registry)
1669+
: options_(options), macro_registry_(std::move(macro_registry)) {}
1670+
absl::StatusOr<std::unique_ptr<cel::Ast>> Parse(
1671+
const cel::Source& source) const override {
1672+
CEL_ASSIGN_OR_RETURN(auto parse_result,
1673+
ParseImpl(source, macro_registry_, options_));
1674+
return std::make_unique<cel::ast_internal::AstImpl>(
1675+
std::move(parse_result.expr), std::move(parse_result.source_info));
1676+
}
1677+
1678+
private:
1679+
const ParserOptions options_;
1680+
const cel::MacroRegistry macro_registry_;
1681+
};
1682+
1683+
class ParserBuilderImpl : public cel::ParserBuilder {
1684+
public:
1685+
explicit ParserBuilderImpl(const ParserOptions& options)
1686+
: options_(options) {}
1687+
1688+
ParserOptions& GetOptions() override { return options_; }
1689+
1690+
absl::Status AddMacro(const cel::Macro& macro) override {
1691+
for (const auto& existing_macro : macros_) {
1692+
if (existing_macro.key() == macro.key()) {
1693+
return absl::AlreadyExistsError(
1694+
absl::StrCat("macro already exists: ", macro.key()));
1695+
}
1696+
}
1697+
macros_.push_back(macro);
1698+
return absl::OkStatus();
1699+
}
1700+
1701+
absl::StatusOr<std::unique_ptr<cel::Parser>> Build() && override {
1702+
cel::MacroRegistry macro_registry;
1703+
1704+
if (!options_.disable_standard_macros) {
1705+
CEL_RETURN_IF_ERROR(macro_registry.RegisterMacros(Macro::AllMacros()));
1706+
}
1707+
if (options_.enable_optional_syntax) {
1708+
CEL_RETURN_IF_ERROR(macro_registry.RegisterMacro(cel::OptMapMacro()));
1709+
CEL_RETURN_IF_ERROR(macro_registry.RegisterMacro(cel::OptFlatMapMacro()));
1710+
}
1711+
CEL_RETURN_IF_ERROR(macro_registry.RegisterMacros(macros_));
1712+
return std::make_unique<ParserImpl>(options_, std::move(macro_registry));
1713+
}
1714+
1715+
private:
1716+
ParserOptions options_;
1717+
std::vector<cel::Macro> macros_;
1718+
};
1719+
16621720
} // namespace
16631721

16641722
absl::StatusOr<ParsedExpr> Parse(absl::string_view expression,
@@ -1719,3 +1777,16 @@ absl::StatusOr<cel::expr::ParsedExpr> Parse(
17191777
}
17201778

17211779
} // namespace google::api::expr::parser
1780+
1781+
namespace cel {
1782+
1783+
// Creates a new parser builder.
1784+
//
1785+
// Intended for use with the Compiler class, most users should prefer the free
1786+
// functions above for independent parsing of expressions.
1787+
std::unique_ptr<ParserBuilder> NewParserBuilder(const ParserOptions& options) {
1788+
return std::make_unique<google::api::expr::parser::ParserBuilderImpl>(
1789+
options);
1790+
}
1791+
1792+
} // namespace cel

parser/parser.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef THIRD_PARTY_CEL_CPP_PARSER_PARSER_H_
2222
#define THIRD_PARTY_CEL_CPP_PARSER_PARSER_H_
2323

24+
#include <memory>
2425
#include <utility>
2526
#include <vector>
2627

@@ -31,6 +32,7 @@
3132
#include "parser/macro.h"
3233
#include "parser/macro_registry.h"
3334
#include "parser/options.h"
35+
#include "parser/parser_interface.h"
3436
#include "parser/source_factory.h"
3537

3638
namespace google::api::expr::parser {
@@ -88,4 +90,13 @@ absl::StatusOr<cel::expr::ParsedExpr> Parse(
8890

8991
} // namespace google::api::expr::parser
9092

93+
namespace cel {
94+
// Creates a new parser builder.
95+
//
96+
// Intended for use with the Compiler class, most users should prefer the free
97+
// functions above for independent parsing of expressions.
98+
std::unique_ptr<ParserBuilder> NewParserBuilder(
99+
const ParserOptions& options = {});
100+
} // namespace cel
101+
91102
#endif // THIRD_PARTY_CEL_CPP_PARSER_PARSER_H_

parser/parser_interface.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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_PARSER_PARSER_INTERFACE_H_
15+
#define THIRD_PARTY_CEL_CPP_PARSER_PARSER_INTERFACE_H_
16+
17+
#include <memory>
18+
19+
#include "absl/status/status.h"
20+
#include "absl/status/statusor.h"
21+
#include "common/ast.h"
22+
#include "common/source.h"
23+
#include "parser/macro.h"
24+
#include "parser/options.h"
25+
26+
namespace cel {
27+
28+
class Parser;
29+
30+
// Interface for building a CEL parser, see comments on `Parser` below.
31+
class ParserBuilder {
32+
public:
33+
virtual ~ParserBuilder() = default;
34+
35+
// Returns the (mutable) current parser options.
36+
virtual ParserOptions& GetOptions() = 0;
37+
38+
// Adds a macro to the parser.
39+
// Standard macros should be automatically added based on parser options.
40+
virtual absl::Status AddMacro(const cel::Macro& macro) = 0;
41+
42+
// Builds a new parser instance, may error if incompatible macros are added.
43+
virtual absl::StatusOr<std::unique_ptr<Parser>> Build() && = 0;
44+
};
45+
46+
// Interface for stateful CEL parser objects for use with a `Compiler`
47+
// (bundled parse and type check). This is not needed for most users:
48+
// prefer using the free functions in `parser.h` for more flexibility.
49+
class Parser {
50+
public:
51+
virtual ~Parser() = default;
52+
53+
// Parses the given source into a CEL AST.
54+
virtual absl::StatusOr<std::unique_ptr<cel::Ast>> Parse(
55+
const cel::Source& source) const = 0;
56+
};
57+
58+
} // namespace cel
59+
60+
#endif // THIRD_PARTY_CEL_CPP_PARSER_PARSER_INTERFACE_H_

parser/parser_test.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222

2323
#include "cel/expr/syntax.pb.h"
2424
#include "absl/algorithm/container.h"
25+
#include "absl/status/status.h"
2526
#include "absl/status/status_matchers.h"
2627
#include "absl/strings/ascii.h"
2728
#include "absl/strings/str_cat.h"
2829
#include "absl/strings/str_format.h"
2930
#include "absl/strings/str_join.h"
3031
#include "absl/strings/string_view.h"
3132
#include "absl/types/optional.h"
33+
#include "base/ast_internal/ast_impl.h"
3234
#include "common/constant.h"
3335
#include "common/expr.h"
36+
#include "common/source.h"
3437
#include "internal/benchmark.h"
3538
#include "internal/testing.h"
3639
#include "parser/macro.h"
@@ -43,6 +46,7 @@ namespace google::api::expr::parser {
4346
namespace {
4447

4548
using ::absl_testing::IsOk;
49+
using ::absl_testing::StatusIs;
4650
using ::cel::ConstantKindCase;
4751
using ::cel::ExprKindCase;
4852
using ::cel::test::ExprPrinter;
@@ -1536,6 +1540,57 @@ TEST(ExpressionTest, RecursionDepthIgnoresParentheses) {
15361540
EXPECT_THAT(result, IsOk());
15371541
}
15381542

1543+
TEST(NewParserBuilderTest, Defaults) {
1544+
auto builder = cel::NewParserBuilder();
1545+
ASSERT_OK_AND_ASSIGN(auto parser, std::move(*builder).Build());
1546+
1547+
ASSERT_OK_AND_ASSIGN(auto source,
1548+
cel::NewSource("has(a.b) && [].exists(x, x > 0)"));
1549+
ASSERT_OK_AND_ASSIGN(auto ast, parser->Parse(*source));
1550+
1551+
EXPECT_FALSE(ast->IsChecked());
1552+
}
1553+
1554+
TEST(NewParserBuilderTest, CustomMacros) {
1555+
auto builder = cel::NewParserBuilder();
1556+
builder->GetOptions().disable_standard_macros = true;
1557+
ASSERT_THAT(builder->AddMacro(cel::HasMacro()), IsOk());
1558+
ASSERT_OK_AND_ASSIGN(auto parser, std::move(*builder).Build());
1559+
builder.reset();
1560+
1561+
ASSERT_OK_AND_ASSIGN(auto source, cel::NewSource("has(a.b) && [].map(x, x)"));
1562+
ASSERT_OK_AND_ASSIGN(auto ast, parser->Parse(*source));
1563+
1564+
EXPECT_FALSE(ast->IsChecked());
1565+
KindAndIdAdorner kind_and_id_adorner;
1566+
ExprPrinter w(kind_and_id_adorner);
1567+
const auto& ast_impl = cel::ast_internal::AstImpl::CastFromPublicAst(*ast);
1568+
EXPECT_EQ(w.Print(ast_impl.root_expr()),
1569+
"_&&_(\n"
1570+
" a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#,\n"
1571+
" []^#5:Expr.CreateList#.map(\n"
1572+
" x^#7:Expr.Ident#,\n"
1573+
" x^#8:Expr.Ident#\n"
1574+
" )^#6:Expr.Call#\n"
1575+
")^#9:Expr.Call#");
1576+
}
1577+
1578+
TEST(NewParserBuilderTest, ForwardsOptions) {
1579+
auto builder = cel::NewParserBuilder();
1580+
builder->GetOptions().enable_optional_syntax = true;
1581+
ASSERT_OK_AND_ASSIGN(auto parser, std::move(*builder).Build());
1582+
ASSERT_OK_AND_ASSIGN(auto source, cel::NewSource("a.?b"));
1583+
ASSERT_OK_AND_ASSIGN(auto ast, parser->Parse(*source));
1584+
EXPECT_FALSE(ast->IsChecked());
1585+
1586+
builder = cel::NewParserBuilder();
1587+
builder->GetOptions().enable_optional_syntax = false;
1588+
ASSERT_OK_AND_ASSIGN(parser, std::move(*builder).Build());
1589+
ASSERT_OK_AND_ASSIGN(source, cel::NewSource("a.?b"));
1590+
EXPECT_THAT(parser->Parse(*source),
1591+
StatusIs(absl::StatusCode::kInvalidArgument));
1592+
}
1593+
15391594
std::string TestName(const testing::TestParamInfo<TestInfo>& test_info) {
15401595
std::string name = absl::StrCat(test_info.index, "-", test_info.param.I);
15411596
absl::c_replace_if(name, [](char c) { return !absl::ascii_isalnum(c); }, '_');

0 commit comments

Comments
 (0)