Coverage Report

Created: 2026-09-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(&registry);
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