Coverage Report

Created: 2026-01-12 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/parser/macro.cc
Line
Count
Source
1
// Copyright 2021 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/macro.h"
16
17
#include <cstddef>
18
#include <functional>
19
#include <memory>
20
#include <string>
21
#include <utility>
22
#include <vector>
23
24
#include "absl/base/no_destructor.h"
25
#include "absl/log/absl_check.h"
26
#include "absl/status/status.h"
27
#include "absl/status/statusor.h"
28
#include "absl/strings/str_cat.h"
29
#include "absl/strings/string_view.h"
30
#include "absl/types/optional.h"
31
#include "absl/types/span.h"
32
#include "common/expr.h"
33
#include "common/operators.h"
34
#include "internal/lexis.h"
35
#include "parser/macro_expr_factory.h"
36
37
namespace cel {
38
39
namespace {
40
41
using google::api::expr::common::CelOperator;
42
43
1
inline MacroExpander ToMacroExpander(GlobalMacroExpander expander) {
44
1
  ABSL_DCHECK(expander);
45
1
  return [expander = std::move(expander)](
46
1
             MacroExprFactory& factory,
47
1
             absl::optional<std::reference_wrapper<Expr>> target,
48
8.74k
             absl::Span<Expr> arguments) -> absl::optional<Expr> {
49
8.74k
    ABSL_DCHECK(!target.has_value());
50
8.74k
    return (expander)(factory, arguments);
51
8.74k
  };
52
1
}
53
54
6
inline MacroExpander ToMacroExpander(ReceiverMacroExpander expander) {
55
6
  ABSL_DCHECK(expander);
56
6
  return [expander = std::move(expander)](
57
6
             MacroExprFactory& factory,
58
6
             absl::optional<std::reference_wrapper<Expr>> target,
59
33.0k
             absl::Span<Expr> arguments) -> absl::optional<Expr> {
60
33.0k
    ABSL_DCHECK(target.has_value());
61
33.0k
    return (expander)(factory, *target, arguments);
62
33.0k
  };
63
6
}
64
65
absl::optional<Expr> ExpandHasMacro(MacroExprFactory& factory,
66
8.74k
                                    absl::Span<Expr> args) {
67
8.74k
  if (args.size() != 1) {
68
0
    return factory.ReportError("has() requires 1 arguments");
69
0
  }
70
8.74k
  if (!args[0].has_select_expr() || args[0].select_expr().test_only()) {
71
8.70k
    return factory.ReportErrorAt(args[0],
72
8.70k
                                 "has() argument must be a field selection");
73
8.70k
  }
74
40
  return factory.NewPresenceTest(
75
40
      args[0].mutable_select_expr().release_operand(),
76
40
      args[0].mutable_select_expr().release_field());
77
8.74k
}
78
79
1
Macro MakeHasMacro() {
80
1
  auto macro_or_status = Macro::Global(CelOperator::HAS, 1, ExpandHasMacro);
81
1
  ABSL_CHECK_OK(macro_or_status);  // Crash OK
82
1
  return std::move(*macro_or_status);
83
1
}
84
85
absl::optional<Expr> ExpandAllMacro(MacroExprFactory& factory, Expr& target,
86
684
                                    absl::Span<Expr> args) {
87
684
  if (args.size() != 2) {
88
0
    return factory.ReportError("all() requires 2 arguments");
89
0
  }
90
684
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
91
215
    return factory.ReportErrorAt(
92
215
        args[0], "all() variable name must be a simple identifier");
93
215
  }
94
469
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
95
20
    return factory.ReportErrorAt(args[1],
96
20
                                 absl::StrCat("all() variable name cannot be ",
97
20
                                              kAccumulatorVariableName));
98
20
  }
99
449
  auto init = factory.NewBoolConst(true);
100
449
  auto condition =
101
449
      factory.NewCall(CelOperator::NOT_STRICTLY_FALSE, factory.NewAccuIdent());
102
449
  auto step = factory.NewCall(CelOperator::LOGICAL_AND, factory.NewAccuIdent(),
103
449
                              std::move(args[1]));
104
449
  auto result = factory.NewAccuIdent();
105
449
  return factory.NewComprehension(args[0].ident_expr().name(),
106
449
                                  std::move(target), factory.AccuVarName(),
107
449
                                  std::move(init), std::move(condition),
108
449
                                  std::move(step), std::move(result));
109
469
}
110
111
1
Macro MakeAllMacro() {
112
1
  auto status_or_macro = Macro::Receiver(CelOperator::ALL, 2, ExpandAllMacro);
113
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
114
1
  return std::move(*status_or_macro);
115
1
}
116
117
absl::optional<Expr> ExpandExistsMacro(MacroExprFactory& factory, Expr& target,
118
492
                                       absl::Span<Expr> args) {
119
492
  if (args.size() != 2) {
120
0
    return factory.ReportError("exists() requires 2 arguments");
121
0
  }
122
492
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
123
47
    return factory.ReportErrorAt(
124
47
        args[0], "exists() variable name must be a simple identifier");
125
47
  }
126
445
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
127
85
    return factory.ReportErrorAt(
128
85
        args[1], absl::StrCat("exists() variable name cannot be ",
129
85
                              kAccumulatorVariableName));
130
85
  }
131
360
  auto init = factory.NewBoolConst(false);
132
360
  auto condition = factory.NewCall(
133
360
      CelOperator::NOT_STRICTLY_FALSE,
134
360
      factory.NewCall(CelOperator::LOGICAL_NOT, factory.NewAccuIdent()));
135
360
  auto step = factory.NewCall(CelOperator::LOGICAL_OR, factory.NewAccuIdent(),
136
360
                              std::move(args[1]));
137
360
  auto result = factory.NewAccuIdent();
138
360
  return factory.NewComprehension(args[0].ident_expr().name(),
139
360
                                  std::move(target), factory.AccuVarName(),
140
360
                                  std::move(init), std::move(condition),
141
360
                                  std::move(step), std::move(result));
142
445
}
143
144
1
Macro MakeExistsMacro() {
145
1
  auto status_or_macro =
146
1
      Macro::Receiver(CelOperator::EXISTS, 2, ExpandExistsMacro);
147
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
148
1
  return std::move(*status_or_macro);
149
1
}
150
151
absl::optional<Expr> ExpandExistsOneMacro(MacroExprFactory& factory,
152
21.7k
                                          Expr& target, absl::Span<Expr> args) {
153
21.7k
  if (args.size() != 2) {
154
0
    return factory.ReportError("exists_one() requires 2 arguments");
155
0
  }
156
21.7k
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
157
223
    return factory.ReportErrorAt(
158
223
        args[0], "exists_one() variable name must be a simple identifier");
159
223
  }
160
21.5k
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
161
59
    return factory.ReportErrorAt(
162
59
        args[1], absl::StrCat("exists_one() variable name cannot be ",
163
59
                              kAccumulatorVariableName));
164
59
  }
165
21.4k
  auto init = factory.NewIntConst(0);
166
21.4k
  auto condition = factory.NewBoolConst(true);
167
21.4k
  auto accu_ident = factory.NewAccuIdent();
168
21.4k
  auto const_1 = factory.NewIntConst(1);
169
21.4k
  auto inc_step = factory.NewCall(CelOperator::ADD, std::move(accu_ident),
170
21.4k
                                  std::move(const_1));
171
172
21.4k
  auto step = factory.NewCall(CelOperator::CONDITIONAL, std::move(args[1]),
173
21.4k
                              std::move(inc_step), factory.NewAccuIdent());
174
21.4k
  accu_ident = factory.NewAccuIdent();
175
21.4k
  auto result = factory.NewCall(CelOperator::EQUALS, std::move(accu_ident),
176
21.4k
                                factory.NewIntConst(1));
177
21.4k
  return factory.NewComprehension(args[0].ident_expr().name(),
178
21.4k
                                  std::move(target), factory.AccuVarName(),
179
21.4k
                                  std::move(init), std::move(condition),
180
21.4k
                                  std::move(step), std::move(result));
181
21.5k
}
182
183
1
Macro MakeExistsOneMacro() {
184
1
  auto status_or_macro =
185
1
      Macro::Receiver(CelOperator::EXISTS_ONE, 2, ExpandExistsOneMacro);
186
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
187
1
  return std::move(*status_or_macro);
188
1
}
189
190
absl::optional<Expr> ExpandMap2Macro(MacroExprFactory& factory, Expr& target,
191
1.87k
                                     absl::Span<Expr> args) {
192
1.87k
  if (args.size() != 2) {
193
0
    return factory.ReportError("map() requires 2 arguments");
194
0
  }
195
1.87k
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
196
129
    return factory.ReportErrorAt(
197
129
        args[0], "map() variable name must be a simple identifier");
198
129
  }
199
1.74k
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
200
21
    return factory.ReportErrorAt(args[1],
201
21
                                 absl::StrCat("map() variable name cannot be ",
202
21
                                              kAccumulatorVariableName));
203
21
  }
204
1.72k
  auto init = factory.NewList();
205
1.72k
  auto condition = factory.NewBoolConst(true);
206
1.72k
  auto accu_ref = factory.NewAccuIdent();
207
1.72k
  auto accu_update =
208
1.72k
      factory.NewList(factory.NewListElement(std::move(args[1])));
209
1.72k
  auto step = factory.NewCall(CelOperator::ADD, std::move(accu_ref),
210
1.72k
                              std::move(accu_update));
211
1.72k
  return factory.NewComprehension(args[0].ident_expr().name(),
212
1.72k
                                  std::move(target), factory.AccuVarName(),
213
1.72k
                                  std::move(init), std::move(condition),
214
1.72k
                                  std::move(step), factory.NewAccuIdent());
215
1.74k
}
216
217
1
Macro MakeMap2Macro() {
218
1
  auto status_or_macro = Macro::Receiver(CelOperator::MAP, 2, ExpandMap2Macro);
219
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
220
1
  return std::move(*status_or_macro);
221
1
}
222
223
absl::optional<Expr> ExpandMap3Macro(MacroExprFactory& factory, Expr& target,
224
8.04k
                                     absl::Span<Expr> args) {
225
8.04k
  if (args.size() != 3) {
226
0
    return factory.ReportError("map() requires 3 arguments");
227
0
  }
228
8.04k
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
229
70
    return factory.ReportErrorAt(
230
70
        args[0], "map() variable name must be a simple identifier");
231
70
  }
232
7.97k
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
233
60
    return factory.ReportErrorAt(args[1],
234
60
                                 absl::StrCat("map() variable name cannot be ",
235
60
                                              kAccumulatorVariableName));
236
60
  }
237
7.91k
  auto init = factory.NewList();
238
7.91k
  auto condition = factory.NewBoolConst(true);
239
7.91k
  auto accu_ref = factory.NewAccuIdent();
240
7.91k
  auto accu_update =
241
7.91k
      factory.NewList(factory.NewListElement(std::move(args[2])));
242
7.91k
  auto step = factory.NewCall(CelOperator::ADD, std::move(accu_ref),
243
7.91k
                              std::move(accu_update));
244
7.91k
  step = factory.NewCall(CelOperator::CONDITIONAL, std::move(args[1]),
245
7.91k
                         std::move(step), factory.NewAccuIdent());
246
7.91k
  return factory.NewComprehension(args[0].ident_expr().name(),
247
7.91k
                                  std::move(target), factory.AccuVarName(),
248
7.91k
                                  std::move(init), std::move(condition),
249
7.91k
                                  std::move(step), factory.NewAccuIdent());
250
7.97k
}
251
252
1
Macro MakeMap3Macro() {
253
1
  auto status_or_macro = Macro::Receiver(CelOperator::MAP, 3, ExpandMap3Macro);
254
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
255
1
  return std::move(*status_or_macro);
256
1
}
257
258
absl::optional<Expr> ExpandFilterMacro(MacroExprFactory& factory, Expr& target,
259
205
                                       absl::Span<Expr> args) {
260
205
  if (args.size() != 2) {
261
0
    return factory.ReportError("filter() requires 2 arguments");
262
0
  }
263
205
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
264
69
    return factory.ReportErrorAt(
265
69
        args[0], "filter() variable name must be a simple identifier");
266
69
  }
267
136
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
268
13
    return factory.ReportErrorAt(
269
13
        args[1], absl::StrCat("filter() variable name cannot be ",
270
13
                              kAccumulatorVariableName));
271
13
  }
272
123
  auto name = args[0].ident_expr().name();
273
274
123
  auto init = factory.NewList();
275
123
  auto condition = factory.NewBoolConst(true);
276
123
  auto accu_ref = factory.NewAccuIdent();
277
123
  auto accu_update =
278
123
      factory.NewList(factory.NewListElement(std::move(args[0])));
279
123
  auto step = factory.NewCall(CelOperator::ADD, std::move(accu_ref),
280
123
                              std::move(accu_update));
281
123
  step = factory.NewCall(CelOperator::CONDITIONAL, std::move(args[1]),
282
123
                         std::move(step), factory.NewAccuIdent());
283
123
  return factory.NewComprehension(std::move(name), std::move(target),
284
123
                                  factory.AccuVarName(), std::move(init),
285
123
                                  std::move(condition), std::move(step),
286
123
                                  factory.NewAccuIdent());
287
136
}
288
289
1
Macro MakeFilterMacro() {
290
1
  auto status_or_macro =
291
1
      Macro::Receiver(CelOperator::FILTER, 2, ExpandFilterMacro);
292
1
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
293
1
  return std::move(*status_or_macro);
294
1
}
295
296
absl::optional<Expr> ExpandOptMapMacro(MacroExprFactory& factory, Expr& target,
297
0
                                       absl::Span<Expr> args) {
298
0
  if (args.size() != 2) {
299
0
    return factory.ReportError("optMap() requires 2 arguments");
300
0
  }
301
0
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
302
0
    return factory.ReportErrorAt(
303
0
        args[0], "optMap() variable name must be a simple identifier");
304
0
  }
305
0
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
306
0
    return factory.ReportErrorAt(
307
0
        args[1], absl::StrCat("optMap() variable name cannot be ",
308
0
                              kAccumulatorVariableName));
309
0
  }
310
0
  auto var_name = args[0].ident_expr().name();
311
312
0
  auto target_copy = factory.Copy(target);
313
0
  std::vector<Expr> call_args;
314
0
  call_args.reserve(3);
315
0
  call_args.push_back(factory.NewMemberCall("hasValue", std::move(target)));
316
0
  auto iter_range = factory.NewList();
317
0
  auto accu_init = factory.NewMemberCall("value", std::move(target_copy));
318
0
  auto condition = factory.NewBoolConst(false);
319
0
  auto fold = factory.NewComprehension(
320
0
      "#unused", std::move(iter_range), std::move(var_name),
321
0
      std::move(accu_init), std::move(condition), std::move(args[0]),
322
0
      std::move(args[1]));
323
0
  call_args.push_back(factory.NewCall("optional.of", std::move(fold)));
324
0
  call_args.push_back(factory.NewCall("optional.none"));
325
0
  return factory.NewCall(CelOperator::CONDITIONAL, std::move(call_args));
326
0
}
327
328
0
Macro MakeOptMapMacro() {
329
0
  auto status_or_macro = Macro::Receiver("optMap", 2, ExpandOptMapMacro);
330
0
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
331
0
  return std::move(*status_or_macro);
332
0
}
333
334
absl::optional<Expr> ExpandOptFlatMapMacro(MacroExprFactory& factory,
335
                                           Expr& target,
336
0
                                           absl::Span<Expr> args) {
337
0
  if (args.size() != 2) {
338
0
    return factory.ReportError("optFlatMap() requires 2 arguments");
339
0
  }
340
0
  if (!args[0].has_ident_expr() || args[0].ident_expr().name().empty()) {
341
0
    return factory.ReportErrorAt(
342
0
        args[0], "optFlatMap() variable name must be a simple identifier");
343
0
  }
344
0
  if (args[0].ident_expr().name() == kAccumulatorVariableName) {
345
0
    return factory.ReportErrorAt(
346
0
        args[1], absl::StrCat("optFlatMap() variable name cannot be ",
347
0
                              kAccumulatorVariableName));
348
0
  }
349
0
  auto var_name = args[0].ident_expr().name();
350
351
0
  auto target_copy = factory.Copy(target);
352
0
  std::vector<Expr> call_args;
353
0
  call_args.reserve(3);
354
0
  call_args.push_back(factory.NewMemberCall("hasValue", std::move(target)));
355
0
  auto iter_range = factory.NewList();
356
0
  auto accu_init = factory.NewMemberCall("value", std::move(target_copy));
357
0
  auto condition = factory.NewBoolConst(false);
358
0
  call_args.push_back(factory.NewComprehension(
359
0
      "#unused", std::move(iter_range), std::move(var_name),
360
0
      std::move(accu_init), std::move(condition), std::move(args[0]),
361
0
      std::move(args[1])));
362
0
  call_args.push_back(factory.NewCall("optional.none"));
363
0
  return factory.NewCall(CelOperator::CONDITIONAL, std::move(call_args));
364
0
}
365
366
0
Macro MakeOptFlatMapMacro() {
367
0
  auto status_or_macro =
368
0
      Macro::Receiver("optFlatMap", 2, ExpandOptFlatMapMacro);
369
0
  ABSL_CHECK_OK(status_or_macro);  // Crash OK
370
0
  return std::move(*status_or_macro);
371
0
}
372
373
}  // namespace
374
375
absl::StatusOr<Macro> Macro::Global(absl::string_view name,
376
                                    size_t argument_count,
377
1
                                    GlobalMacroExpander expander) {
378
1
  if (!expander) {
379
0
    return absl::InvalidArgumentError(
380
0
        absl::StrCat("macro expander for `", name, "` cannot be empty"));
381
0
  }
382
1
  return Make(name, argument_count, ToMacroExpander(std::move(expander)),
383
1
              /*receiver_style=*/false, /*var_arg_style=*/false);
384
1
}
385
386
absl::StatusOr<Macro> Macro::GlobalVarArg(absl::string_view name,
387
0
                                          GlobalMacroExpander expander) {
388
0
  if (!expander) {
389
0
    return absl::InvalidArgumentError(
390
0
        absl::StrCat("macro expander for `", name, "` cannot be empty"));
391
0
  }
392
0
  return Make(name, 0, ToMacroExpander(std::move(expander)),
393
0
              /*receiver_style=*/false,
394
0
              /*var_arg_style=*/true);
395
0
}
396
397
absl::StatusOr<Macro> Macro::Receiver(absl::string_view name,
398
                                      size_t argument_count,
399
6
                                      ReceiverMacroExpander expander) {
400
6
  if (!expander) {
401
0
    return absl::InvalidArgumentError(
402
0
        absl::StrCat("macro expander for `", name, "` cannot be empty"));
403
0
  }
404
6
  return Make(name, argument_count, ToMacroExpander(std::move(expander)),
405
6
              /*receiver_style=*/true, /*var_arg_style=*/false);
406
6
}
407
408
absl::StatusOr<Macro> Macro::ReceiverVarArg(absl::string_view name,
409
0
                                            ReceiverMacroExpander expander) {
410
0
  if (!expander) {
411
0
    return absl::InvalidArgumentError(
412
0
        absl::StrCat("macro expander for `", name, "` cannot be empty"));
413
0
  }
414
0
  return Make(name, 0, ToMacroExpander(std::move(expander)),
415
0
              /*receiver_style=*/true,
416
0
              /*var_arg_style=*/true);
417
0
}
418
419
6.54k
std::vector<Macro> Macro::AllMacros() {
420
6.54k
  return {HasMacro(),  AllMacro(),  ExistsMacro(), ExistsOneMacro(),
421
6.54k
          Map2Macro(), Map3Macro(), FilterMacro()};
422
6.54k
}
423
424
std::string Macro::Key(absl::string_view name, size_t argument_count,
425
7
                       bool receiver_style, bool var_arg_style) {
426
7
  if (var_arg_style) {
427
0
    return absl::StrCat(name, ":*:", receiver_style ? "true" : "false");
428
0
  }
429
7
  return absl::StrCat(name, ":", argument_count, ":",
430
7
                      receiver_style ? "true" : "false");
431
7
}
432
433
absl::StatusOr<Macro> Macro::Make(absl::string_view name, size_t argument_count,
434
                                  MacroExpander expander, bool receiver_style,
435
7
                                  bool var_arg_style) {
436
7
  if (!internal::LexisIsIdentifier(name)) {
437
0
    return absl::InvalidArgumentError(absl::StrCat(
438
0
        "macro function name `", name, "` is not a valid identifier"));
439
0
  }
440
7
  if (!expander) {
441
0
    return absl::InvalidArgumentError(
442
0
        absl::StrCat("macro expander for `", name, "` cannot be empty"));
443
0
  }
444
7
  return Macro(std::make_shared<Rep>(
445
7
      std::string(name),
446
7
      Key(name, argument_count, receiver_style, var_arg_style), argument_count,
447
7
      std::move(expander), receiver_style, var_arg_style));
448
7
}
449
450
6.54k
const Macro& HasMacro() {
451
6.54k
  static const absl::NoDestructor<Macro> macro(MakeHasMacro());
452
6.54k
  return *macro;
453
6.54k
}
454
455
6.54k
const Macro& AllMacro() {
456
6.54k
  static const absl::NoDestructor<Macro> macro(MakeAllMacro());
457
6.54k
  return *macro;
458
6.54k
}
459
460
6.54k
const Macro& ExistsMacro() {
461
6.54k
  static const absl::NoDestructor<Macro> macro(MakeExistsMacro());
462
6.54k
  return *macro;
463
6.54k
}
464
465
6.54k
const Macro& ExistsOneMacro() {
466
6.54k
  static const absl::NoDestructor<Macro> macro(MakeExistsOneMacro());
467
6.54k
  return *macro;
468
6.54k
}
469
470
6.54k
const Macro& Map2Macro() {
471
6.54k
  static const absl::NoDestructor<Macro> macro(MakeMap2Macro());
472
6.54k
  return *macro;
473
6.54k
}
474
475
6.54k
const Macro& Map3Macro() {
476
6.54k
  static const absl::NoDestructor<Macro> macro(MakeMap3Macro());
477
6.54k
  return *macro;
478
6.54k
}
479
480
6.54k
const Macro& FilterMacro() {
481
6.54k
  static const absl::NoDestructor<Macro> macro(MakeFilterMacro());
482
6.54k
  return *macro;
483
6.54k
}
484
485
0
const Macro& OptMapMacro() {
486
0
  static const absl::NoDestructor<Macro> macro(MakeOptMapMacro());
487
0
  return *macro;
488
0
}
489
490
0
const Macro& OptFlatMapMacro() {
491
0
  static const absl::NoDestructor<Macro> macro(MakeOptFlatMapMacro());
492
0
  return *macro;
493
0
}
494
495
}  // namespace cel