Skip to content

Commit 5cf72d9

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
[Pratt Parser] Add an option to enable Pratt Parser
PiperOrigin-RevId: 953565535
1 parent 30c794b commit 5cf72d9

14 files changed

Lines changed: 502 additions & 170 deletions

File tree

conformance/BUILD

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ _TESTS_TO_SKIP_LEGACY_DASHBOARD = [
248248
]
249249

250250
# Generates a bunch of `cc_test` whose names follow the pattern
251-
# `conformance_(...)_{arena|refcount}_{optimized|unoptimized}_{recursive|iterative}`.
251+
# `conformance_(...)_{pratt|antlr}_{optimized|unoptimized}_{recursive|iterative}`.
252252
gen_conformance_tests(
253253
name = "conformance_parse_only",
254254
data = _ALL_TESTS,
@@ -317,7 +317,7 @@ gen_conformance_tests(
317317
)
318318

319319
# Generates a bunch of `cc_test` whose names follow the pattern
320-
# `conformance_dashboard_..._{arena|refcount}_{optimized|unoptimized}_{recursive|iterative}`.
320+
# `conformance_dashboard_..._{pratt|antlr}_{optimized|unoptimized}_{recursive|iterative}`.
321321
gen_conformance_tests(
322322
name = "conformance_dashboard_parse_only",
323323
dashboard = True,

conformance/run.bzl

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,17 @@ def _expand_tests_to_skip(tests_to_skip):
4747
result.append(test_to_skip[0:slash] + part)
4848
return result
4949

50-
def _conformance_test_name(name, optimize, recursive):
50+
def _conformance_test_name(name, pratt, optimize, recursive):
5151
return "_".join(
5252
[
5353
name,
54+
"pratt" if pratt else "antlr",
5455
"optimized" if optimize else "unoptimized",
5556
"recursive" if recursive else "iterative",
5657
],
5758
)
5859

59-
def _conformance_test_args(modern, optimize, recursive, select_opt, skip_check, dashboard, enable_variadic_logical_operators):
60+
def _conformance_test_args(modern, optimize, recursive, select_opt, skip_check, dashboard, enable_variadic_logical_operators, pratt):
6061
args = []
6162
if modern:
6263
args.append("--modern")
@@ -74,12 +75,16 @@ def _conformance_test_args(modern, optimize, recursive, select_opt, skip_check,
7475
args.append("--dashboard")
7576
if enable_variadic_logical_operators:
7677
args.append("--enable_variadic_logical_operators")
78+
if pratt:
79+
args.append("--enable_pratt_parser")
80+
else:
81+
args.append("--noenable_pratt_parser")
7782
return args
7883

79-
def _conformance_test(name, data, modern, optimize, recursive, select_opt, skip_check, skip_tests, tags, dashboard, enable_variadic_logical_operators):
84+
def _conformance_test(name, data, modern, optimize, recursive, select_opt, skip_check, skip_tests, tags, dashboard, enable_variadic_logical_operators, pratt):
8085
cc_test(
81-
name = _conformance_test_name(name, optimize, recursive),
82-
args = _conformance_test_args(modern, optimize, recursive, select_opt, skip_check, dashboard, enable_variadic_logical_operators) + ["$(rlocationpath {})".format(test) for test in data],
86+
name = _conformance_test_name(name, pratt, optimize, recursive),
87+
args = _conformance_test_args(modern, optimize, recursive, select_opt, skip_check, dashboard, enable_variadic_logical_operators, pratt) + ["$(rlocationpath {})".format(test) for test in data],
8388
env = select(
8489
{
8590
"@platforms//os:windows": {"CEL_SKIP_TESTS": ",".join(skip_tests + _TESTS_TO_SKIP_WINDOWS)},
@@ -108,23 +113,25 @@ def gen_conformance_tests(name, data, modern = False, checked = False, select_op
108113
"""
109114
skip_check = not checked
110115
tests = []
111-
for optimize in (True, False):
112-
for recursive in (True, False):
113-
test_name = _conformance_test_name(name, optimize, recursive)
114-
tests.append(test_name)
115-
_conformance_test(
116-
name,
117-
data,
118-
modern = modern,
119-
optimize = optimize,
120-
recursive = recursive,
121-
select_opt = select_opt,
122-
skip_check = skip_check,
123-
skip_tests = _expand_tests_to_skip(skip_tests),
124-
tags = tags,
125-
dashboard = dashboard,
126-
enable_variadic_logical_operators = enable_variadic_logical_operators,
127-
)
116+
for pratt in (True, False):
117+
for optimize in (True, False):
118+
for recursive in (True, False):
119+
test_name = _conformance_test_name(name, pratt, optimize, recursive)
120+
tests.append(test_name)
121+
_conformance_test(
122+
name,
123+
data,
124+
modern = modern,
125+
optimize = optimize,
126+
recursive = recursive,
127+
select_opt = select_opt,
128+
skip_check = skip_check,
129+
skip_tests = _expand_tests_to_skip(skip_tests),
130+
tags = tags,
131+
dashboard = dashboard,
132+
enable_variadic_logical_operators = enable_variadic_logical_operators,
133+
pratt = pratt,
134+
)
128135
native.test_suite(
129136
name = name,
130137
tests = tests,

conformance/run.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ ABSL_FLAG(bool, select_optimization, false, "Enable select optimization.");
6969
ABSL_FLAG(bool, enable_variadic_logical_operators, false,
7070
"Enable parsing logical AND & OR operators as a single flat variadic "
7171
"call.");
72+
ABSL_FLAG(bool, enable_pratt_parser, true,
73+
"Enable manual (Pratt) parser instead of ANTLR parser.");
7274

7375
namespace {
7476

@@ -266,6 +268,7 @@ NewConformanceServiceFromFlags() {
266268
.select_optimization = absl::GetFlag(FLAGS_select_optimization),
267269
.enable_variadic_logical_operators =
268270
absl::GetFlag(FLAGS_enable_variadic_logical_operators),
271+
.enable_pratt_parser = absl::GetFlag(FLAGS_enable_pratt_parser),
269272
});
270273
ABSL_CHECK_OK(status_or_service);
271274
return std::shared_ptr<cel_conformance::ConformanceServiceInterface>(

conformance/service.cc

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,16 @@ cel::expr::Expr ExtractExpr(
130130
absl::Status LegacyParse(const conformance::v1alpha1::ParseRequest& request,
131131
conformance::v1alpha1::ParseResponse& response,
132132
bool enable_optional_syntax,
133-
bool enable_variadic_logical_operators) {
133+
bool enable_variadic_logical_operators,
134+
bool enable_pratt_parser) {
134135
if (request.cel_source().empty()) {
135136
return absl::InvalidArgumentError("no source code");
136137
}
137138
cel::ParserOptions options;
138139
options.enable_optional_syntax = enable_optional_syntax;
139140
options.enable_quoted_identifiers = true;
140141
options.enable_variadic_logical_operators = enable_variadic_logical_operators;
142+
options.enable_pratt_parser = enable_pratt_parser;
141143
cel::MacroRegistry macros;
142144
CEL_RETURN_IF_ERROR(cel::RegisterStandardMacros(macros, options));
143145
CEL_RETURN_IF_ERROR(
@@ -240,7 +242,7 @@ class LegacyConformanceServiceImpl : public ConformanceServiceInterface {
240242
public:
241243
static absl::StatusOr<std::unique_ptr<LegacyConformanceServiceImpl>> Create(
242244
bool optimize, bool recursive, bool select_optimization,
243-
bool enable_variadic_logical_operators) {
245+
bool enable_variadic_logical_operators, bool enable_pratt_parser) {
244246
static auto* constant_arena = new Arena();
245247

246248
google::protobuf::LinkMessageReflection<
@@ -319,14 +321,15 @@ class LegacyConformanceServiceImpl : public ConformanceServiceInterface {
319321
builder->GetRegistry(), options));
320322

321323
return absl::WrapUnique(new LegacyConformanceServiceImpl(
322-
std::move(builder), enable_variadic_logical_operators));
324+
std::move(builder), enable_variadic_logical_operators,
325+
enable_pratt_parser));
323326
}
324327

325328
void Parse(const conformance::v1alpha1::ParseRequest& request,
326329
conformance::v1alpha1::ParseResponse& response) override {
327330
auto status =
328331
LegacyParse(request, response, /*enable_optional_syntax=*/false,
329-
enable_variadic_logical_operators_);
332+
enable_variadic_logical_operators_, enable_pratt_parser_);
330333
if (!status.ok()) {
331334
auto* issue = response.add_issues();
332335
issue->set_code(ToGrpcCode(status.code()));
@@ -425,19 +428,22 @@ class LegacyConformanceServiceImpl : public ConformanceServiceInterface {
425428

426429
private:
427430
LegacyConformanceServiceImpl(std::unique_ptr<CelExpressionBuilder> builder,
428-
bool enable_variadic_logical_operators)
431+
bool enable_variadic_logical_operators,
432+
bool enable_pratt_parser)
429433
: builder_(std::move(builder)),
430-
enable_variadic_logical_operators_(enable_variadic_logical_operators) {}
434+
enable_variadic_logical_operators_(enable_variadic_logical_operators),
435+
enable_pratt_parser_(enable_pratt_parser) {}
431436

432437
std::unique_ptr<CelExpressionBuilder> builder_;
433438
bool enable_variadic_logical_operators_;
439+
bool enable_pratt_parser_;
434440
};
435441

436442
class ModernConformanceServiceImpl : public ConformanceServiceInterface {
437443
public:
438444
static absl::StatusOr<std::unique_ptr<ModernConformanceServiceImpl>> Create(
439445
bool optimize, bool recursive, bool select_optimization,
440-
bool enable_variadic_logical_operators) {
446+
bool enable_variadic_logical_operators, bool enable_pratt_parser) {
441447
google::protobuf::LinkMessageReflection<
442448
cel::expr::conformance::proto3::TestAllTypes>();
443449
google::protobuf::LinkMessageReflection<
@@ -479,9 +485,9 @@ class ModernConformanceServiceImpl : public ConformanceServiceInterface {
479485
options.max_recursion_depth = 48;
480486
}
481487

482-
return absl::WrapUnique(
483-
new ModernConformanceServiceImpl(options, optimize, select_optimization,
484-
enable_variadic_logical_operators));
488+
return absl::WrapUnique(new ModernConformanceServiceImpl(
489+
options, optimize, select_optimization,
490+
enable_variadic_logical_operators, enable_pratt_parser));
485491
}
486492

487493
absl::StatusOr<std::unique_ptr<const cel::Runtime>> Setup(
@@ -538,7 +544,7 @@ class ModernConformanceServiceImpl : public ConformanceServiceInterface {
538544
conformance::v1alpha1::ParseResponse& response) override {
539545
auto status =
540546
LegacyParse(request, response, /*enable_optional_syntax=*/true,
541-
enable_variadic_logical_operators_);
547+
enable_variadic_logical_operators_, enable_pratt_parser_);
542548
if (!status.ok()) {
543549
auto* issue = response.add_issues();
544550
issue->set_code(ToGrpcCode(status.code()));
@@ -630,11 +636,13 @@ class ModernConformanceServiceImpl : public ConformanceServiceInterface {
630636
ModernConformanceServiceImpl(const RuntimeOptions& options,
631637
bool enable_optimizations,
632638
bool enable_select_optimization,
633-
bool enable_variadic_logical_operators)
639+
bool enable_variadic_logical_operators,
640+
bool enable_pratt_parser)
634641
: options_(options),
635642
enable_optimizations_(enable_optimizations),
636643
enable_select_optimization_(enable_select_optimization),
637-
enable_variadic_logical_operators_(enable_variadic_logical_operators) {}
644+
enable_variadic_logical_operators_(enable_variadic_logical_operators),
645+
enable_pratt_parser_(enable_pratt_parser) {}
638646

639647
static absl::StatusOr<std::unique_ptr<cel::TraceableProgram>> Plan(
640648
const cel::Runtime& runtime,
@@ -666,6 +674,7 @@ class ModernConformanceServiceImpl : public ConformanceServiceInterface {
666674
bool enable_optimizations_;
667675
bool enable_select_optimization_;
668676
bool enable_variadic_logical_operators_;
677+
bool enable_pratt_parser_;
669678
};
670679

671680
} // namespace
@@ -679,11 +688,11 @@ NewConformanceService(const ConformanceServiceOptions& options) {
679688
if (options.modern) {
680689
return google::api::expr::runtime::ModernConformanceServiceImpl::Create(
681690
options.optimize, options.recursive, options.select_optimization,
682-
options.enable_variadic_logical_operators);
691+
options.enable_variadic_logical_operators, options.enable_pratt_parser);
683692
} else {
684693
return google::api::expr::runtime::LegacyConformanceServiceImpl::Create(
685694
options.optimize, options.recursive, options.select_optimization,
686-
options.enable_variadic_logical_operators);
695+
options.enable_variadic_logical_operators, options.enable_pratt_parser);
687696
}
688697
}
689698

conformance/service.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ struct ConformanceServiceOptions {
4747
bool recursive;
4848
bool select_optimization;
4949
bool enable_variadic_logical_operators = false;
50+
bool enable_pratt_parser = true;
5051
};
5152

5253
absl::StatusOr<std::unique_ptr<ConformanceServiceInterface>>

parser/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ cc_library(
4242
":source_factory",
4343
"//common:ast",
4444
"//common:constant",
45+
"//common:expr",
4546
"//common:expr_factory",
4647
"//common:operators",
4748
"//common:source",
@@ -50,8 +51,8 @@ cc_library(
5051
"//internal:lexis",
5152
"//internal:status_macros",
5253
"//internal:strings",
53-
"//internal:utf8",
5454
"//parser/internal:cel_cc_parser",
55+
"//parser/internal:pratt_parser",
5556
"@antlr4-cpp-runtime",
5657
"@com_google_absl//absl/base:core_headers",
5758
"@com_google_absl//absl/cleanup",
@@ -60,6 +61,7 @@ cc_library(
6061
"@com_google_absl//absl/container:flat_hash_set",
6162
"@com_google_absl//absl/functional:overload",
6263
"@com_google_absl//absl/log:absl_check",
64+
"@com_google_absl//absl/log:check",
6365
"@com_google_absl//absl/memory",
6466
"@com_google_absl//absl/status",
6567
"@com_google_absl//absl/status:statusor",

parser/internal/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ cc_library(
117117
"//parser:macro_registry",
118118
"//parser:options",
119119
"//parser:parser_interface",
120+
"@com_google_absl//absl/algorithm:container",
120121
"@com_google_absl//absl/base:nullability",
121122
"@com_google_absl//absl/cleanup",
122123
"@com_google_absl//absl/container:flat_hash_map",

parser/internal/ast_factory_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,8 @@ class TestMacroExprExpanderSupport
405405
public:
406406
int64_t NextId() override { return 42; }
407407
int64_t CopyId(int64_t id) override { return id; }
408-
cel::Expr ReportError(std::string_view) override { return cel::Expr(); }
409-
cel::Expr ReportErrorAt(const cel::Expr&, std::string_view) override {
408+
cel::Expr ReportError(absl::string_view) override { return cel::Expr(); }
409+
cel::Expr ReportErrorAt(const cel::Expr&, absl::string_view) override {
410410
return cel::Expr();
411411
}
412412
};

parser/internal/pratt_parser.cc

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414

1515
#include "parser/internal/pratt_parser.h"
1616

17+
#include <algorithm>
1718
#include <cstdint>
1819
#include <memory>
1920
#include <string>
2021
#include <string_view>
2122
#include <utility>
2223
#include <vector>
2324

25+
#include "absl/algorithm/container.h"
2426
#include "absl/base/nullability.h"
2527
#include "absl/cleanup/cleanup.h"
2628
#include "absl/container/flat_hash_map.h"
@@ -158,21 +160,34 @@ template class PrattParserWorker<cel::Expr>;
158160
absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParserImpl::ParseImpl(
159161
const cel::Source& source,
160162
std::vector<cel::ParseIssue>* absl_nullable parse_issues) const {
161-
if (source.content().size() > options_.expression_size_codepoint_limit) {
163+
return PrattParseImpl(source, macro_registry_, options_, parse_issues);
164+
}
165+
166+
absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
167+
const cel::Source& source, const cel::MacroRegistry& registry,
168+
const ParserOptions& options, std::vector<cel::ParseIssue>* parse_issues) {
169+
if (source.content().size() > options.expression_size_codepoint_limit) {
162170
return absl::InvalidArgumentError(absl::StrFormat(
163-
"expression size exceeds codepoint limit: %zu > %d",
164-
source.content().size(), options_.expression_size_codepoint_limit));
171+
"expression size exceeds codepoint limit. input size: %zu, limit: %d",
172+
source.content().size(), options.expression_size_codepoint_limit));
165173
}
166174
std::vector<cel::ParseIssue> issues;
167-
AstFactory factory(&macro_registry_);
168-
PrattParserWorker<cel::Expr> worker(source, options_, &issues, factory);
175+
AstFactory factory(&registry);
176+
PrattParserWorker<cel::Expr> worker(source, options, &issues, factory);
169177
Expr expr = worker.Parse();
170178
if (worker.is_recursion_limit_exceeded()) {
171179
return absl::CancelledError(
172180
absl::StrFormat("Expression recursion limit exceeded. limit: %d",
173-
options_.max_recursion_depth));
181+
options.max_recursion_depth));
174182
}
175183
if (worker.has_errors()) {
184+
absl::c_stable_sort(
185+
issues, [](const cel::ParseIssue& lhs, const cel::ParseIssue& rhs) {
186+
if (lhs.location().line != rhs.location().line) {
187+
return lhs.location().line < rhs.location().line;
188+
}
189+
return lhs.location().column < rhs.location().column;
190+
});
176191
std::string err_msg = FormatIssues(source, issues);
177192
if (parse_issues != nullptr) {
178193
parse_issues->swap(issues);

parser/internal/pratt_parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class PrattParserImpl final : public cel::Parser {
6767
absl::flat_hash_set<std::string> library_ids_;
6868
};
6969

70+
absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
71+
const cel::Source& source, const cel::MacroRegistry& registry,
72+
const ParserOptions& options,
73+
std::vector<cel::ParseIssue>* parse_issues = nullptr);
74+
7075
class PrattParserBuilderImpl final : public cel::ParserBuilder {
7176
public:
7277
explicit PrattParserBuilderImpl(const cel::ParserOptions& options)

0 commit comments

Comments
 (0)