/proc/self/cwd/parser/internal/pratt_parser.cc
Line | Count | Source |
1 | | // Copyright 2026 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 | | |
15 | | #include "parser/internal/pratt_parser.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <cstdint> |
19 | | #include <memory> |
20 | | #include <string> |
21 | | #include <string_view> |
22 | | #include <utility> |
23 | | #include <vector> |
24 | | |
25 | | #include "absl/algorithm/container.h" |
26 | | #include "absl/base/nullability.h" |
27 | | #include "absl/cleanup/cleanup.h" |
28 | | #include "absl/container/flat_hash_map.h" |
29 | | #include "absl/container/flat_hash_set.h" |
30 | | #include "absl/status/status.h" |
31 | | #include "absl/status/statusor.h" |
32 | | #include "absl/strings/str_cat.h" |
33 | | #include "absl/strings/str_format.h" |
34 | | #include "absl/strings/str_join.h" |
35 | | #include "absl/strings/string_view.h" |
36 | | #include "absl/types/span.h" |
37 | | #include "common/ast.h" |
38 | | #include "common/expr.h" |
39 | | #include "common/source.h" |
40 | | #include "internal/status_macros.h" |
41 | | #include "parser/internal/ast_factory.h" // IWYU pragma: keep |
42 | | #include "parser/internal/pratt_parser_worker.h" |
43 | | #include "parser/macro.h" |
44 | | #include "parser/macro_registry.h" |
45 | | #include "parser/options.h" |
46 | | #include "parser/parser_interface.h" |
47 | | #include "parser/source_factory.h" |
48 | | |
49 | | namespace cel::parser_internal { |
50 | | |
51 | | namespace { |
52 | | |
53 | | std::string DisplayParserError(const cel::Source& source, |
54 | | SourceLocation location, |
55 | 0 | std::string_view message) { |
56 | 0 | int32_t display_column = |
57 | 0 | location.column >= 0 ? location.column + 1 : location.column; |
58 | 0 | return absl::StrCat( |
59 | 0 | absl::StrFormat("ERROR: %s:%d:%d: %s", source.description(), |
60 | 0 | location.line, display_column, message), |
61 | 0 | source.DisplayErrorLocation(location)); |
62 | 0 | } |
63 | | |
64 | | std::string FormatIssues(const cel::Source& source, |
65 | 0 | absl::Span<const cel::ParseIssue> issues) { |
66 | 0 | return absl::StrJoin( |
67 | 0 | issues, "\n", [&source](std::string* out, const cel::ParseIssue& issue) { |
68 | 0 | absl::StrAppend( |
69 | 0 | out, DisplayParserError(source, issue.location(), issue.message())); |
70 | 0 | }); |
71 | 0 | } |
72 | | |
73 | | } // namespace |
74 | | |
75 | 0 | absl::Status PrattParserBuilderImpl::AddMacro(const cel::Macro& macro) { |
76 | 0 | for (const cel::Macro& existing_macro : macros_) { |
77 | 0 | if (existing_macro.key() == macro.key()) { |
78 | 0 | return absl::AlreadyExistsError( |
79 | 0 | absl::StrCat("macro already exists: ", macro.key())); |
80 | 0 | } |
81 | 0 | } |
82 | 0 | macros_.push_back(macro); |
83 | 0 | return absl::OkStatus(); |
84 | 0 | } |
85 | | |
86 | 0 | absl::Status PrattParserBuilderImpl::AddLibrary(cel::ParserLibrary library) { |
87 | 0 | if (!library.id.empty()) { |
88 | 0 | auto [it, inserted] = library_ids_.insert(library.id); |
89 | 0 | if (!inserted) { |
90 | 0 | return absl::AlreadyExistsError( |
91 | 0 | absl::StrCat("parser library already exists: ", library.id)); |
92 | 0 | } |
93 | 0 | } |
94 | 0 | libraries_.push_back(std::move(library)); |
95 | 0 | return absl::OkStatus(); |
96 | 0 | } |
97 | | |
98 | | absl::Status PrattParserBuilderImpl::AddLibrarySubset( |
99 | 0 | cel::ParserLibrarySubset subset) { |
100 | 0 | if (subset.library_id.empty()) { |
101 | 0 | return absl::InvalidArgumentError("subset must have a library id"); |
102 | 0 | } |
103 | 0 | std::string library_id = subset.library_id; |
104 | 0 | auto [it, inserted] = |
105 | 0 | library_subsets_.insert({library_id, std::move(subset)}); |
106 | 0 | if (!inserted) { |
107 | 0 | return absl::AlreadyExistsError( |
108 | 0 | absl::StrCat("parser library subset already exists: ", library_id)); |
109 | 0 | } |
110 | 0 | return absl::OkStatus(); |
111 | 0 | } |
112 | | |
113 | 0 | absl::StatusOr<std::unique_ptr<cel::Parser>> PrattParserBuilderImpl::Build() { |
114 | 0 | using std::swap; |
115 | 0 | std::vector<cel::Macro> individual_macros; |
116 | 0 | swap(individual_macros, macros_); |
117 | 0 | absl::Cleanup cleanup([&] { swap(macros_, individual_macros); }); |
118 | |
|
119 | 0 | cel::MacroRegistry macro_registry; |
120 | |
|
121 | 0 | for (const cel::ParserLibrary& library : libraries_) { |
122 | 0 | CEL_RETURN_IF_ERROR(library.configure(*this)); |
123 | 0 | if (!library.id.empty()) { |
124 | 0 | auto it = library_subsets_.find(library.id); |
125 | 0 | if (it != library_subsets_.end()) { |
126 | 0 | const cel::ParserLibrarySubset& subset = it->second; |
127 | 0 | for (const cel::Macro& macro : macros_) { |
128 | 0 | if (subset.should_include_macro(macro)) { |
129 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacro(macro)); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | macros_.clear(); |
133 | 0 | continue; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacros(macros_)); |
138 | 0 | macros_.clear(); |
139 | 0 | } |
140 | | |
141 | 0 | absl::flat_hash_set<std::string> library_ids(library_ids_); |
142 | |
|
143 | 0 | if (!options_.disable_standard_macros && !library_ids_.contains("stdlib")) { |
144 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacros(Macro::AllMacros())); |
145 | 0 | library_ids.insert("stdlib"); |
146 | 0 | } |
147 | | |
148 | 0 | if (options_.enable_optional_syntax && !library_ids_.contains("optional")) { |
149 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacro(cel::OptMapMacro())); |
150 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacro(cel::OptFlatMapMacro())); |
151 | 0 | library_ids.insert("optional"); |
152 | 0 | } |
153 | | |
154 | 0 | CEL_RETURN_IF_ERROR(macro_registry.RegisterMacros(individual_macros)); |
155 | 0 | return std::make_unique<PrattParserImpl>(options_, std::move(macro_registry), |
156 | 0 | std::move(library_ids)); |
157 | 0 | } |
158 | | |
159 | | template class PrattParserWorker<cel::Expr>; |
160 | | |
161 | | absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParserImpl::ParseImpl( |
162 | | const cel::Source& source, |
163 | 0 | std::vector<cel::ParseIssue>* absl_nullable parse_issues) const { |
164 | 0 | return PrattParseImpl(source, macro_registry_, options_, parse_issues); |
165 | 0 | } |
166 | | |
167 | | absl::StatusOr<std::unique_ptr<cel::Source>> PrattParserImpl::PrepareSourceImpl( |
168 | 0 | absl::string_view input, absl::string_view description) const { |
169 | 0 | return cel::NewSource( |
170 | 0 | input, std::string(description), |
171 | 0 | cel::SourceOptions{.max_codepoint_size = |
172 | 0 | options_.expression_size_codepoint_limit}); |
173 | 0 | } |
174 | | |
175 | | absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl( |
176 | | const cel::Source& source, const cel::MacroRegistry& registry, |
177 | | const ParserOptions& options, std::vector<cel::ParseIssue>* parse_issues, |
178 | 0 | cel::EnrichedSourceInfo* enriched_source_info) { |
179 | 0 | if (source.content().size() > options.expression_size_codepoint_limit) { |
180 | 0 | return absl::InvalidArgumentError(absl::StrFormat( |
181 | 0 | "expression size exceeds codepoint limit. input size: %zu, limit: %d", |
182 | 0 | source.content().size(), options.expression_size_codepoint_limit)); |
183 | 0 | } |
184 | 0 | std::vector<cel::ParseIssue> issues; |
185 | 0 | AstFactory factory(®istry); |
186 | 0 | PrattParserWorker<cel::Expr> worker( |
187 | 0 | source, options, &issues, factory, |
188 | 0 | /*track_node_ranges=*/enriched_source_info != nullptr); |
189 | 0 | Expr expr = worker.Parse(); |
190 | 0 | if (worker.is_recursion_limit_exceeded()) { |
191 | 0 | return absl::CancelledError( |
192 | 0 | absl::StrFormat("Expression recursion limit exceeded. limit: %d", |
193 | 0 | options.max_recursion_depth)); |
194 | 0 | } |
195 | 0 | if (worker.has_errors()) { |
196 | 0 | absl::c_stable_sort( |
197 | 0 | issues, [](const cel::ParseIssue& lhs, const cel::ParseIssue& rhs) { |
198 | 0 | if (lhs.location().line != rhs.location().line) { |
199 | 0 | return lhs.location().line < rhs.location().line; |
200 | 0 | } |
201 | 0 | return lhs.location().column < rhs.location().column; |
202 | 0 | }); |
203 | 0 | std::string err_msg = FormatIssues(source, issues); |
204 | 0 | if (parse_issues != nullptr) { |
205 | 0 | parse_issues->swap(issues); |
206 | 0 | } |
207 | 0 | return absl::InvalidArgumentError(err_msg); |
208 | 0 | } |
209 | | |
210 | 0 | if (enriched_source_info != nullptr) { |
211 | 0 | *enriched_source_info = cel::EnrichedSourceInfo(worker.GetNodeRanges()); |
212 | 0 | } |
213 | |
|
214 | 0 | cel::SourceInfo source_info; |
215 | 0 | source_info.set_location(std::string(source.description())); |
216 | 0 | for (const auto& [id, pos] : worker.GetNodePositions()) { |
217 | 0 | source_info.mutable_positions().insert({id, pos}); |
218 | 0 | } |
219 | 0 | source_info.mutable_line_offsets().reserve(source.line_offsets().size()); |
220 | 0 | for (int32_t offset : source.line_offsets()) { |
221 | 0 | source_info.mutable_line_offsets().push_back(offset); |
222 | 0 | } |
223 | 0 | source_info.mutable_macro_calls() = worker.ReleaseMacroCalls(); |
224 | 0 | return std::make_unique<cel::Ast>(std::move(expr), std::move(source_info)); |
225 | 0 | } |
226 | | |
227 | 0 | std::unique_ptr<cel::ParserBuilder> PrattParserImpl::ToBuilder() const { |
228 | 0 | auto ins = std::make_unique<PrattParserBuilderImpl>(options_); |
229 | 0 | ins->library_ids_ = library_ids_; |
230 | 0 | ins->macros_ = macro_registry_.ListMacros(); |
231 | 0 | return ins; |
232 | 0 | } |
233 | | |
234 | | } // namespace cel::parser_internal |