Skip to content

Commit 9bcb00a

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
[Pratt Parser] Add support for enriched source info
PiperOrigin-RevId: 963760554
1 parent ad9ebbe commit 9bcb00a

9 files changed

Lines changed: 114 additions & 34 deletions

File tree

parser/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ cc_library(
164164
hdrs = [
165165
"source_factory.h",
166166
],
167+
deps = [
168+
"@com_google_absl//absl/container:flat_hash_map",
169+
],
167170
)
168171

169172
cc_library(

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+
"//parser:source_factory",
120121
"@com_google_absl//absl/algorithm:container",
121122
"@com_google_absl//absl/base:nullability",
122123
"@com_google_absl//absl/cleanup",

parser/internal/pratt_parser.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "parser/macro_registry.h"
4545
#include "parser/options.h"
4646
#include "parser/parser_interface.h"
47+
#include "parser/source_factory.h"
4748

4849
namespace cel::parser_internal {
4950

@@ -173,15 +174,18 @@ absl::StatusOr<std::unique_ptr<cel::Source>> PrattParserImpl::PrepareSourceImpl(
173174

174175
absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
175176
const cel::Source& source, const cel::MacroRegistry& registry,
176-
const ParserOptions& options, std::vector<cel::ParseIssue>* parse_issues) {
177+
const ParserOptions& options, std::vector<cel::ParseIssue>* parse_issues,
178+
cel::EnrichedSourceInfo* enriched_source_info) {
177179
if (source.content().size() > options.expression_size_codepoint_limit) {
178180
return absl::InvalidArgumentError(absl::StrFormat(
179181
"expression size exceeds codepoint limit. input size: %zu, limit: %d",
180182
source.content().size(), options.expression_size_codepoint_limit));
181183
}
182184
std::vector<cel::ParseIssue> issues;
183185
AstFactory factory(&registry);
184-
PrattParserWorker<cel::Expr> worker(source, options, &issues, factory);
186+
PrattParserWorker<cel::Expr> worker(
187+
source, options, &issues, factory,
188+
/*track_node_ranges=*/enriched_source_info != nullptr);
185189
Expr expr = worker.Parse();
186190
if (worker.is_recursion_limit_exceeded()) {
187191
return absl::CancelledError(
@@ -203,6 +207,10 @@ absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
203207
return absl::InvalidArgumentError(err_msg);
204208
}
205209

210+
if (enriched_source_info != nullptr) {
211+
*enriched_source_info = cel::EnrichedSourceInfo(worker.GetNodeRanges());
212+
}
213+
206214
cel::SourceInfo source_info;
207215
source_info.set_location(std::string(source.description()));
208216
for (const auto& [id, pos] : worker.GetNodePositions()) {

parser/internal/pratt_parser.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@
2525
#include "absl/container/flat_hash_set.h"
2626
#include "absl/status/status.h"
2727
#include "absl/status/statusor.h"
28+
#include "absl/strings/string_view.h"
2829
#include "common/ast.h"
2930
#include "common/source.h"
3031
#include "parser/macro.h"
3132
#include "parser/macro_registry.h"
3233
#include "parser/options.h"
3334
#include "parser/parser_interface.h"
3435

36+
namespace cel {
37+
class EnrichedSourceInfo;
38+
} // namespace cel
39+
3540
namespace cel::parser_internal {
3641

3742
// PrattParserImpl implements the Pratt parsing algorithm for CEL expressions.
@@ -73,7 +78,8 @@ class PrattParserImpl final : public cel::Parser {
7378
absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
7479
const cel::Source& source, const cel::MacroRegistry& registry,
7580
const ParserOptions& options,
76-
std::vector<cel::ParseIssue>* parse_issues = nullptr);
81+
std::vector<cel::ParseIssue>* parse_issues = nullptr,
82+
cel::EnrichedSourceInfo* enriched_source_info = nullptr);
7783

7884
class PrattParserBuilderImpl final : public cel::ParserBuilder {
7985
public:

parser/internal/pratt_parser_worker.cc

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <vector>
2020

2121
#include "absl/base/nullability.h"
22+
#include "absl/base/optimization.h"
2223
#include "absl/strings/str_cat.h"
2324
#include "absl/strings/str_format.h"
2425
#include "absl/strings/string_view.h"
@@ -101,11 +102,13 @@ const BinaryOpInfo& GetBinaryOpInfo(TokenType type) {
101102

102103
ParserWorker::ParserWorker(
103104
const cel::Source& source, const cel::ParserOptions& options,
104-
std::vector<cel::ParseIssue>* absl_nullable parse_issues)
105+
std::vector<cel::ParseIssue>* absl_nullable parse_issues,
106+
bool track_node_ranges)
105107
: source_(source),
106108
options_(options),
107109
lexer_(source_),
108-
parse_issues_(parse_issues) {}
110+
parse_issues_(parse_issues),
111+
track_node_ranges_(track_node_ranges) {}
109112

110113
void ParserWorker::InitTokenStream() {
111114
current_token_ = Token{.type = TokenType::kError, .start = 0, .end = 0};
@@ -204,14 +207,15 @@ int64_t ParserWorker::NextId(int32_t position) {
204207
}
205208
if (position >= 0) {
206209
positions_.insert({id, position});
210+
if (ABSL_PREDICT_FALSE(track_node_ranges_)) {
211+
node_ranges_.insert({id, {position, position}});
212+
}
207213
}
208214
return id;
209215
}
210216

211217
int64_t ParserWorker::NextId() { return NextId(-1); }
212218

213-
bool ParserWorker::NodeLimitExceeded() { return node_limit_exceeded_; }
214-
215219
int64_t ParserWorker::CopyId(int64_t id) {
216220
if (id == 0) {
217221
return 0;
@@ -220,11 +224,20 @@ int64_t ParserWorker::CopyId(int64_t id) {
220224
if (auto it = positions_.find(id); it != positions_.end()) {
221225
pos = it->second;
222226
}
223-
return NextId(pos);
227+
int64_t new_id = NextId(pos);
228+
if (ABSL_PREDICT_FALSE(track_node_ranges_)) {
229+
if (auto it = node_ranges_.find(id); it != node_ranges_.end()) {
230+
node_ranges_[new_id] = it->second;
231+
}
232+
}
233+
return new_id;
224234
}
225235

226236
void ParserWorker::EraseId(int64_t id) {
227237
positions_.erase(id);
238+
if (ABSL_PREDICT_FALSE(track_node_ranges_)) {
239+
node_ranges_.erase(id);
240+
}
228241
if (next_id_ == id + 1) {
229242
--next_id_;
230243
}

parser/internal/pratt_parser_worker.h

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,16 @@ namespace cel::parser_internal {
5151
class ParserWorker {
5252
public:
5353
ParserWorker(const cel::Source& source, const cel::ParserOptions& options,
54-
std::vector<cel::ParseIssue>* absl_nullable parse_issues);
54+
std::vector<cel::ParseIssue>* absl_nullable parse_issues,
55+
bool track_node_ranges = false);
5556

5657
const absl::flat_hash_map<int64_t, int32_t>& GetNodePositions() const {
5758
return positions_;
5859
}
60+
const absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>>&
61+
GetNodeRanges() const {
62+
return node_ranges_;
63+
}
5964
absl::Span<const int32_t> GetLineOffsets() const {
6065
return source_.line_offsets();
6166
}
@@ -75,11 +80,25 @@ class ParserWorker {
7580

7681
// ID and Position tracking
7782
int64_t NextId(int32_t position);
78-
int64_t NextId(const Token& token) { return NextId(token.start); }
83+
int64_t NextId(const Token& token) {
84+
int64_t id = NextId(token.start);
85+
if (ABSL_PREDICT_FALSE(track_node_ranges_)) {
86+
if (token.start >= 0 && token.end > token.start) {
87+
node_ranges_[id] = {token.start, token.end - 1};
88+
}
89+
}
90+
return id;
91+
}
7992
int64_t NextId();
80-
bool NodeLimitExceeded();
8193
int64_t CopyId(int64_t id);
8294
void EraseId(int64_t id);
95+
void SetNodeRange(int64_t id, int32_t begin, int32_t end) {
96+
if (ABSL_PREDICT_FALSE(track_node_ranges_)) {
97+
if (id != 0 && begin >= 0 && end >= begin) {
98+
node_ranges_[id] = {begin, end};
99+
}
100+
}
101+
}
83102

84103
// Error reporting and recovery
85104
bool is_recovery_limit_exceeded() const {
@@ -100,10 +119,12 @@ class ParserWorker {
100119
int64_t next_id_ = 1;
101120
bool node_limit_exceeded_ = false;
102121
absl::flat_hash_map<int64_t, int32_t> positions_;
122+
absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>> node_ranges_;
103123
std::vector<cel::ParseIssue>* absl_nullable parse_issues_;
104124
int error_count_ = 0;
105125
bool lexer_error_reported_ = false;
106126
bool recursion_limit_exceeded_ = false;
127+
bool track_node_ranges_ = false;
107128
};
108129

109130
struct BinaryOpInfo {
@@ -131,8 +152,10 @@ class PrattParserWorker : public ParserWorker {
131152
explicit PrattParserWorker(
132153
const cel::Source& source, const cel::ParserOptions& options,
133154
std::vector<cel::ParseIssue>* absl_nullable parse_issues,
134-
AstFactoryInterface<ExprNode>& ast_factory)
135-
: ParserWorker(source, options, parse_issues), ast_factory_(ast_factory) {
155+
AstFactoryInterface<ExprNode>& ast_factory,
156+
bool track_node_ranges = false)
157+
: ParserWorker(source, options, parse_issues, track_node_ranges),
158+
ast_factory_(ast_factory) {
136159
this->InitTokenStream();
137160
}
138161

@@ -696,7 +719,9 @@ ExprNode PrattParserWorker<ExprNode>::ParseList() {
696719
break;
697720
}
698721
}
699-
Expect(TokenType::kRightBracket, "expected ']'");
722+
if (Expect(TokenType::kRightBracket, "expected ']'")) {
723+
SetNodeRange(list_id, open_tok.start, current_token_.end - 1);
724+
}
700725
return builder.Build();
701726
}
702727

@@ -732,7 +757,9 @@ ExprNode PrattParserWorker<ExprNode>::ParseMap() {
732757
break;
733758
}
734759
}
735-
Expect(TokenType::kRightBrace, "expected '}'");
760+
if (Expect(TokenType::kRightBrace, "expected '}'")) {
761+
SetNodeRange(map_id, open_tok.start, current_token_.end - 1);
762+
}
736763
return builder.Build();
737764
}
738765

@@ -774,7 +801,14 @@ ExprNode PrattParserWorker<ExprNode>::ParseStruct(
774801
break;
775802
}
776803
}
777-
Expect(TokenType::kRightBrace, "expected '}'");
804+
if (Expect(TokenType::kRightBrace, "expected '}'")) {
805+
int32_t start_pos = open_tok.start;
806+
auto it = positions_.find(obj_id);
807+
if (it != positions_.end()) {
808+
start_pos = it->second;
809+
}
810+
SetNodeRange(obj_id, start_pos, current_token_.end - 1);
811+
}
778812
return builder.Build();
779813
}
780814

@@ -1052,7 +1086,7 @@ std::optional<ExprNode> PrattParserWorker<ExprNode>::TryExpandMacro(
10521086
if (!expander) {
10531087
return std::nullopt;
10541088
}
1055-
if (NodeLimitExceeded()) {
1089+
if (node_limit_exceeded_) {
10561090
ReportError(expr_id,
10571091
"could not expand macro: expression node limit exceeded");
10581092
return std::nullopt;

parser/parser.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include <functional>
2424
#include <iterator>
2525
#include <limits>
26-
#include <map>
2726
#include <memory>
2827
#include <optional>
2928
#include <string>
@@ -1445,7 +1444,8 @@ cel::SourceInfo ParserVisitor::GetSourceInfo() {
14451444
}
14461445

14471446
EnrichedSourceInfo ParserVisitor::enriched_source_info() const {
1448-
std::map<int64_t, std::pair<int32_t, int32_t>> offsets;
1447+
absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>> offsets;
1448+
offsets.reserve(factory_.positions().size());
14491449
for (const auto& positions : factory_.positions()) {
14501450
offsets.insert(
14511451
std::pair{positions.first,
@@ -1951,14 +1951,17 @@ absl::StatusOr<VerboseParsedExpr> EnrichedParse(
19511951
const ParserOptions& options) {
19521952
ParsedExpr parsed_expr;
19531953
if (options.enable_pratt_parser) {
1954-
CEL_ASSIGN_OR_RETURN(
1955-
std::unique_ptr<cel::Ast> ast,
1956-
cel::parser_internal::PrattParseImpl(source, registry, options));
1954+
EnrichedSourceInfo enriched_source_info;
1955+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Ast> ast,
1956+
cel::parser_internal::PrattParseImpl(
1957+
source, registry, options,
1958+
/*parse_issues=*/nullptr, &enriched_source_info));
19571959
CEL_RETURN_IF_ERROR(cel::ast_internal::ExprToProto(
19581960
ast->root_expr(), parsed_expr.mutable_expr()));
19591961
CEL_RETURN_IF_ERROR(cel::ast_internal::SourceInfoToProto(
19601962
ast->source_info(), parsed_expr.mutable_source_info()));
1961-
return VerboseParsedExpr(std::move(parsed_expr), EnrichedSourceInfo());
1963+
return VerboseParsedExpr(std::move(parsed_expr),
1964+
std::move(enriched_source_info));
19621965
}
19631966
CEL_ASSIGN_OR_RETURN(ParseResult parse_result,
19641967
ParseImpl(source, registry, options));

parser/parser_test.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,8 +1650,13 @@ class LocationAdorner : public cel::ExpressionAdorner {
16501650

16511651
std::string ConvertEnrichedSourceInfoToString(
16521652
const EnrichedSourceInfo& enriched_source_info) {
1653+
std::vector<std::pair<int64_t, std::pair<int32_t, int32_t>>> sorted_offsets(
1654+
enriched_source_info.offsets().begin(),
1655+
enriched_source_info.offsets().end());
1656+
absl::c_sort(sorted_offsets);
16531657
std::vector<std::string> offsets;
1654-
for (const auto& offset : enriched_source_info.offsets()) {
1658+
offsets.reserve(sorted_offsets.size());
1659+
for (const auto& offset : sorted_offsets) {
16551660
offsets.push_back(absl::StrFormat(
16561661
"[%d,%d,%d]", offset.first, offset.second.first, offset.second.second));
16571662
}
@@ -1747,24 +1752,23 @@ TEST_P(ExpressionTest, Parse) {
17471752
}
17481753
}
17491754

1750-
TEST(ExpressionTest, CompositeExpressionOffsets) {
1751-
ParserOptions options;
1755+
TEST_P(ExpressionTest, CompositeExpressionOffsets) {
17521756
std::vector<Macro> macros = Macro::AllMacros();
17531757

17541758
std::string list_expr = "[1, 2]";
1755-
auto list_result = EnrichedParse(list_expr, macros, "<input>", options);
1759+
auto list_result = EnrichedParse(list_expr, macros, "<input>", options_);
17561760
ASSERT_THAT(list_result, IsOk());
17571761
auto list_offsets = list_result->enriched_source_info().offsets();
17581762
EXPECT_EQ(list_offsets.at(1), std::make_pair(0, 5));
17591763

17601764
std::string map_expr = "{'a': 1}";
1761-
auto map_result = EnrichedParse(map_expr, macros, "<input>", options);
1765+
auto map_result = EnrichedParse(map_expr, macros, "<input>", options_);
17621766
ASSERT_THAT(map_result, IsOk());
17631767
auto map_offsets = map_result->enriched_source_info().offsets();
17641768
EXPECT_EQ(map_offsets.at(1), std::make_pair(0, 7));
17651769

17661770
std::string msg_expr = "Msg{f: 1}";
1767-
auto msg_result = EnrichedParse(msg_expr, macros, "<input>", options);
1771+
auto msg_result = EnrichedParse(msg_expr, macros, "<input>", options_);
17681772
ASSERT_THAT(msg_result, IsOk());
17691773
auto msg_offsets = msg_result->enriched_source_info().offsets();
17701774
EXPECT_EQ(msg_offsets.at(1), std::make_pair(0, 8));

parser/source_factory.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616
#define THIRD_PARTY_CEL_CPP_PARSER_SOURCE_FACTORY_H_
1717

1818
#include <cstdint>
19-
#include <map>
2019
#include <utility>
2120

22-
namespace google::api::expr::parser {
21+
#include "absl/container/flat_hash_map.h"
22+
23+
namespace cel {
2324

2425
class EnrichedSourceInfo {
2526
public:
2627
explicit EnrichedSourceInfo(
27-
std::map<int64_t, std::pair<int32_t, int32_t>> offsets)
28+
absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>> offsets)
2829
: offsets_(std::move(offsets)) {}
2930

3031
EnrichedSourceInfo() = default;
@@ -33,15 +34,22 @@ class EnrichedSourceInfo {
3334
EnrichedSourceInfo(EnrichedSourceInfo&& other) = default;
3435
EnrichedSourceInfo& operator=(EnrichedSourceInfo&& other) = default;
3536

36-
const std::map<int64_t, std::pair<int32_t, int32_t>>& offsets() const {
37+
const absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>>& offsets()
38+
const {
3739
return offsets_;
3840
}
3941

4042
private:
4143
// A map between node_id and pair of start position and end position
42-
std::map<int64_t, std::pair<int32_t, int32_t>> offsets_;
44+
absl::flat_hash_map<int64_t, std::pair<int32_t, int32_t>> offsets_;
4345
};
4446

47+
} // namespace cel
48+
49+
namespace google::api::expr::parser {
50+
51+
using EnrichedSourceInfo = ::cel::EnrichedSourceInfo;
52+
4553
} // namespace google::api::expr::parser
4654

4755
#endif // THIRD_PARTY_CEL_CPP_PARSER_SOURCE_FACTORY_H_

0 commit comments

Comments
 (0)