forked from cel-expr/cel-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcomprehension_step.cc
More file actions
257 lines (233 loc) · 8.82 KB
/
Copy pathcomprehension_step.cc
File metadata and controls
257 lines (233 loc) · 8.82 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#include "eval/eval/comprehension_step.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "eval/eval/attribute_trail.h"
#include "eval/eval/evaluator_core.h"
#include "eval/public/cel_attribute.h"
#include "base/status_macros.h"
namespace google {
namespace api {
namespace expr {
namespace runtime {
// Stack variables during comprehension evaluation:
// 0. accu_init, then loop_step (any), available through accu_var
// 1. iter_range (list)
// 2. current index in iter_range (int64_t)
// 3. current_value from iter_range (any), available through iter_var
// 4. loop_condition (bool) OR loop_step (any)
// What to put on ExecutionPath: stack size
// 0. (dummy) 1
// 1. iter_range (dep) 2
// 2. -1 3
// 3. (dummy) 4
// 4. accu_init (dep) 5
// 5. ComprehensionNextStep 4
// 6. loop_condition (dep) 5
// 7. ComprehensionCondStep 4
// 8. loop_step (dep) 5
// 9. goto 5. 5
// 10. result (dep) 2
// 11. ComprehensionFinish 1
ComprehensionNextStep::ComprehensionNextStep(const std::string& accu_var,
const std::string& iter_var,
int64_t expr_id)
: ExpressionStepBase(expr_id, false),
accu_var_(accu_var),
iter_var_(iter_var) {}
void ComprehensionNextStep::set_jump_offset(int offset) {
jump_offset_ = offset;
}
void ComprehensionNextStep::set_error_jump_offset(int offset) {
error_jump_offset_ = offset;
}
// Stack changes of ComprehensionNextStep.
//
// Stack before:
// 0. previous accu_init or "" on the first iteration
// 1. iter_range (list)
// 2. old current_index in iter_range (int64_t)
// 3. old current_value or "" on the first iteration
// 4. loop_step or accu_init (any)
//
// Stack after:
// 0. loop_step or accu_init (any)
// 1. iter_range (list)
// 2. new current_index in iter_range (int64_t)
// 3. new current_value
//
// Stack on break:
// 0. loop_step or accu_init (any)
//
// When iter_range is not a list, this step jumps to error_jump_offset_ that is
// controlled by set_error_jump_offset. In that case the stack is cleared
// from values related to this comprehension and an error is put on the stack.
//
// Stack on error:
// 0. error
absl::Status ComprehensionNextStep::Evaluate(ExecutionFrame* frame) const {
enum {
POS_PREVIOUS_LOOP_STEP,
POS_ITER_RANGE,
POS_CURRENT_INDEX,
POS_CURRENT_VALUE,
POS_LOOP_STEP,
};
if (!frame->value_stack().HasEnough(5)) {
return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
}
auto state = frame->value_stack().GetSpan(5);
auto attr = frame->value_stack().GetAttributeSpan(5);
// Get range from the stack.
CelValue iter_range = state[POS_ITER_RANGE];
if (!iter_range.IsList()) {
frame->value_stack().Pop(5);
if (iter_range.IsError() || iter_range.IsUnknownSet()) {
frame->value_stack().Push(iter_range);
return frame->JumpTo(error_jump_offset_);
}
frame->value_stack().Push(
CreateNoMatchingOverloadError(frame->arena(), "<iter_range>"));
return frame->JumpTo(error_jump_offset_);
}
const CelList* cel_list = iter_range.ListOrDie();
const AttributeTrail iter_range_attr = attr[POS_ITER_RANGE];
// Get the current index off the stack.
CelValue current_index_value = state[POS_CURRENT_INDEX];
if (!current_index_value.IsInt64()) {
auto message = absl::StrCat(
"ComprehensionNextStep: want int64_t, got ",
CelValue::TypeName(current_index_value.type())
);
return absl::Status(absl::StatusCode::kInternal, message);
}
auto increment_status = frame->IncrementIterations();
if (!increment_status.ok()) {
return increment_status;
}
int64_t current_index = current_index_value.Int64OrDie();
if (current_index == -1) {
RETURN_IF_ERROR(frame->PushIterFrame());
}
// Update stack for breaking out of loop or next round.
CelValue loop_step = state[POS_LOOP_STEP];
frame->value_stack().Pop(5);
frame->value_stack().Push(loop_step);
RETURN_IF_ERROR(frame->SetIterVar(accu_var_, loop_step));
if (current_index >= cel_list->size() - 1) {
RETURN_IF_ERROR(frame->ClearIterVar(iter_var_));
return frame->JumpTo(jump_offset_);
}
frame->value_stack().Push(iter_range, iter_range_attr);
current_index += 1;
CelValue current_value = (*cel_list)[current_index];
frame->value_stack().Push(CelValue::CreateInt64(current_index));
auto iter_trail = iter_range_attr.Step(
CelAttributeQualifier::Create(CelValue::CreateInt64(current_index)),
frame->arena());
frame->value_stack().Push(current_value, iter_trail);
RETURN_IF_ERROR(frame->SetIterVar(iter_var_, current_value, iter_trail));
return absl::OkStatus();
}
ComprehensionCondStep::ComprehensionCondStep(const std::string&,
const std::string& iter_var,
bool shortcircuiting,
int64_t expr_id)
: ExpressionStepBase(expr_id, false),
iter_var_(iter_var),
shortcircuiting_(shortcircuiting) {}
void ComprehensionCondStep::set_jump_offset(int offset) {
jump_offset_ = offset;
}
void ComprehensionCondStep::set_error_jump_offset(int offset) {
error_jump_offset_ = offset;
}
// Stack changes by ComprehensionCondStep.
//
// Stack size before: 5.
// Stack size after: 4.
// Stack size on break: 1.
absl::Status ComprehensionCondStep::Evaluate(ExecutionFrame* frame) const {
if (!frame->value_stack().HasEnough(5)) {
return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
}
CelValue loop_condition_value = frame->value_stack().Peek();
if (!loop_condition_value.IsBool()) {
frame->value_stack().Pop(5);
if (loop_condition_value.IsError() || loop_condition_value.IsUnknownSet()) {
frame->value_stack().Push(loop_condition_value);
} else {
frame->value_stack().Push(
CreateNoMatchingOverloadError(frame->arena(), "<loop_condition>"));
}
// The error jump skips the ComprehensionFinish clean-up step, so we
// need to update the iteration variable stack here.
RETURN_IF_ERROR(frame->PopIterFrame());
return frame->JumpTo(error_jump_offset_);
}
bool loop_condition = loop_condition_value.BoolOrDie();
frame->value_stack().Pop(1); // loop_condition
if (!loop_condition && shortcircuiting_) {
frame->value_stack().Pop(3); // current_value, current_index, iter_range
return frame->JumpTo(jump_offset_);
}
return absl::OkStatus();
}
ComprehensionFinish::ComprehensionFinish(const std::string& accu_var,
const std::string&, int64_t expr_id)
: ExpressionStepBase(expr_id), accu_var_(accu_var) {}
// Stack changes of ComprehensionFinish.
//
// Stack size before: 2.
// Stack size after: 1.
absl::Status ComprehensionFinish::Evaluate(ExecutionFrame* frame) const {
if (!frame->value_stack().HasEnough(2)) {
return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
}
CelValue result = frame->value_stack().Peek();
frame->value_stack().Pop(1); // result
frame->value_stack().PopAndPush(result);
RETURN_IF_ERROR(frame->PopIterFrame());
return absl::OkStatus();
}
class ListKeysStep : public ExpressionStepBase {
public:
ListKeysStep(int64_t expr_id) : ExpressionStepBase(expr_id, false) {}
absl::Status Evaluate(ExecutionFrame* frame) const override;
private:
absl::Status ProjectKeys(ExecutionFrame* frame) const;
};
std::unique_ptr<ExpressionStep> CreateListKeysStep(int64_t expr_id) {
return absl::make_unique<ListKeysStep>(expr_id);
}
absl::Status ListKeysStep::ProjectKeys(ExecutionFrame* frame) const {
// Top of stack is map, but could be partially unknown. To tolerate cases when
// keys are not set for declared unknown values, convert to an unknown set.
if (frame->enable_unknowns()) {
const UnknownSet* unknown = frame->attribute_utility().MergeUnknowns(
frame->value_stack().GetSpan(1),
frame->value_stack().GetAttributeSpan(1), nullptr,
/*use_partial=*/true);
if (unknown) {
frame->value_stack().PopAndPush(CelValue::CreateUnknownSet(unknown));
return absl::OkStatus();
}
}
const CelValue& map = frame->value_stack().Peek();
frame->value_stack().PopAndPush(
CelValue::CreateList(map.MapOrDie()->ListKeys()));
return absl::OkStatus();
}
absl::Status ListKeysStep::Evaluate(ExecutionFrame* frame) const {
if (!frame->value_stack().HasEnough(1)) {
return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
}
const CelValue& map_value = frame->value_stack().Peek();
if (map_value.IsMap()) {
return ProjectKeys(frame);
}
return absl::OkStatus();
}
} // namespace runtime
} // namespace expr
} // namespace api
} // namespace google