forked from cel-expr/cel-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogic_step.cc
More file actions
134 lines (107 loc) · 3.74 KB
/
Copy pathlogic_step.cc
File metadata and controls
134 lines (107 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "eval/eval/logic_step.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "eval/eval/expression_step_base.h"
#include "eval/public/cel_builtins.h"
#include "eval/public/cel_value.h"
#include "eval/public/unknown_attribute_set.h"
namespace google {
namespace api {
namespace expr {
namespace runtime {
namespace {
class LogicalOpStep : public ExpressionStepBase {
public:
enum class OpType { AND, OR };
// Constructs FunctionStep that uses overloads specified.
LogicalOpStep(OpType op_type, int64_t expr_id)
: ExpressionStepBase(expr_id), op_type_(op_type) {
shortcircuit_ = (op_type_ == OpType::OR);
}
absl::Status Evaluate(ExecutionFrame* frame) const override;
private:
absl::Status Calculate(ExecutionFrame* frame, absl::Span<const CelValue> args,
CelValue* result) const {
bool bool_args[2];
bool has_bool_args[2];
for (size_t i = 0; i < args.size(); i++) {
has_bool_args[i] = args[i].GetValue(bool_args + i);
if (has_bool_args[i] && shortcircuit_ == bool_args[i]) {
*result = CelValue::CreateBool(bool_args[i]);
return absl::OkStatus();
}
}
if (has_bool_args[0] && has_bool_args[1]) {
switch (op_type_) {
case OpType::AND:
*result = CelValue::CreateBool(bool_args[0] && bool_args[1]);
return absl::OkStatus();
break;
case OpType::OR:
*result = CelValue::CreateBool(bool_args[0] || bool_args[1]);
return absl::OkStatus();
break;
}
}
// As opposed to regular function, logical operation treat Unknowns with
// higher precedence than error. This is due to the fact that after Unknown
// is resolved to actual value, it may shortcircuit and thus hide the error.
if (frame->enable_unknowns()) {
// Check if unknown?
const UnknownSet* unknown_set =
frame->attribute_utility().MergeUnknowns(args,
/*initial_set=*/nullptr);
if (unknown_set) {
*result = CelValue::CreateUnknownSet(unknown_set);
return absl::OkStatus();
}
}
if (args[0].IsError()) {
*result = args[0];
return absl::OkStatus();
} else if (args[1].IsError()) {
*result = args[1];
return absl::OkStatus();
}
// Fallback.
*result = CreateNoMatchingOverloadError(
frame->arena(),
(op_type_ == OpType::OR) ? builtin::kOr : builtin::kAnd);
return absl::OkStatus();
}
const OpType op_type_;
bool shortcircuit_;
};
absl::Status LogicalOpStep::Evaluate(ExecutionFrame* frame) const {
// Must have 2 or more values on the stack.
if (!frame->value_stack().HasEnough(2)) {
return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
}
// Create Span object that contains input arguments to the function.
auto args = frame->value_stack().GetSpan(2);
CelValue value;
auto status = Calculate(frame, args, &value);
if (!status.ok()) {
return status;
}
frame->value_stack().Pop(args.size());
frame->value_stack().Push(value);
return status;
}
} // namespace
// Factory method for "And" Execution step
absl::StatusOr<std::unique_ptr<ExpressionStep>> CreateAndStep(int64_t expr_id) {
std::unique_ptr<ExpressionStep> step =
absl::make_unique<LogicalOpStep>(LogicalOpStep::OpType::AND, expr_id);
return std::move(step);
}
// Factory method for "Or" Execution step
absl::StatusOr<std::unique_ptr<ExpressionStep>> CreateOrStep(int64_t expr_id) {
std::unique_ptr<ExpressionStep> step =
absl::make_unique<LogicalOpStep>(LogicalOpStep::OpType::OR, expr_id);
return std::move(step);
}
} // namespace runtime
} // namespace expr
} // namespace api
} // namespace google