GCC Code Coverage Report


Directory: ./
File: src/vm/tree.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 254 514 49.4%
Functions: 39 97 40.2%
Branches: 202 645 31.3%

Line Branch Exec Source
1
2 #include "../dtypes.h"
3 #include "ast/values/exception.h"
4 #include "ast/values/generator.h"
5 #include "ast/values/object.h"
6 #include "logging/logging.h"
7 #include "parser/parsing_error.h"
8 #include "utilities/guard.h"
9
10 #include "vm/tree.h"
11
12 #define EXEC_BODY(body) \
13 for (StmtNode * stmt: (body)) { \
14 exec(stmt, depth); \
15 \
16 if (has_exceptions()) { \
17 return None(); \
18 } \
19 \
20 if (return_value != nullptr) { \
21 return return_value; \
22 } \
23 }
24
25 namespace lython {
26
27 void TreeEvaluator::raise_exception(PartialResult* exception, PartialResult* cause) {
28 // Create the exception object
29
30 // Constant* cause_value = cast<Constant>(cause);
31 // // cause should be an exception
32 // NativeObject* cause_except = cause_value->value.get<NativeObject*>();
33
34 // if (cause != nullptr && cause_except) {
35 // // TODO:
36 // }
37
38
1/1
✓ Branch 1 taken 3 times.
3 lyException* except = root.new_object<lyException>(traces);
39 // Constant* except_value = root.new_object<Constant>(except);
40
41
1/1
✓ Branch 1 taken 3 times.
3 exceptions.push_back(except);
42 3 }
43
44 PartialResult* TreeEvaluator::compare(Compare_t* n, int depth) {
45
46 // a and b and c and d
47 //
48
1/1
✓ Branch 1 taken 52 times.
52 PartialResult* left = exec(n->left, depth);
49
1/1
✓ Branch 1 taken 52 times.
52 Constant* left_const = cast<Constant>(left);
50
51 52 Array<PartialResult*> partials;
52
1/1
✓ Branch 2 taken 52 times.
52 partials.reserve(n->comparators.size());
53
54 52 bool bnative = !n->native_operator.empty();
55 52 bool full_eval = true;
56 52 bool result = true;
57
58
2/2
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 22 times.
77 for (int i = 0; i < n->comparators.size(); i++) {
59
1/1
✓ Branch 2 taken 55 times.
55 PartialResult* right = exec(n->comparators[i], depth);
60
1/1
✓ Branch 1 taken 55 times.
55 partials.push_back(right);
61
62
1/1
✓ Branch 1 taken 55 times.
55 Constant* right_const = cast<Constant>(right);
63
64
2/4
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
55 if (left_const != nullptr && right_const != nullptr) {
65 55 Constant* value = nullptr;
66
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 if (!bnative) {
68 Scope scope(bindings);
69
70 bindings.add(StringRef(), left_const, nullptr);
71 bindings.add(StringRef(), right_const, nullptr);
72
73 value = cast<Constant>(exec(n->resolved_operator[i], depth));
74
75
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 } else if (bnative) {
76 55 auto native = n->native_operator[i];
77 assert(native, "Operator needs to be set");
78
79
1/1
✓ Branch 1 taken 55 times.
55 ConstantValue v = native(left_const->value, right_const->value);
80
3/4
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 30 times.
55 result = result && v.get<bool>();
81 55 }
82
83 // One comparison is false so the entire thing does not work
84
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 25 times.
55 if (!result) {
85
1/1
✓ Branch 1 taken 30 times.
30 return False();
86 }
87
88 25 left = right;
89 25 left_const = right_const;
90
91 25 } else {
92 full_eval = false;
93 }
94 }
95
96
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (full_eval) {
97
1/1
✓ Branch 1 taken 22 times.
22 return True();
98 }
99
100 Compare* comp = root.new_object<Compare>();
101 comp->left = n->left;
102 comp->ops = n->ops;
103 comp->comparators.reserve(partials.size());
104
105 comp->resolved_operator = n->resolved_operator;
106 comp->native_operator = n->native_operator;
107
108 for (auto* p: partials) {
109 comp->comparators.push_back((ExprNode*)p);
110 }
111 return comp;
112 52 }
113
114 PartialResult* TreeEvaluator::boolop(BoolOp_t* n, int depth) {
115 // a and b or c and d
116 //
117
1/1
✓ Branch 2 taken 2 times.
2 PartialResult* first_value = exec(n->values[0], depth);
118
1/1
✓ Branch 1 taken 2 times.
2 Constant* first = cast<Constant>(first_value);
119
120 2 Array<PartialResult*> partials;
121
1/1
✓ Branch 2 taken 2 times.
2 partials.reserve(n->values.size());
122
1/1
✓ Branch 1 taken 2 times.
2 partials.push_back(first_value);
123
124 2 bool full_eval = true;
125 2 bool result = false;
126 std::function<bool(bool, bool)> reduce = [](bool a, bool b) -> bool { return a || b; };
127
128
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (n->op == BoolOperator::And) {
129 2 result = true;
130 reduce = [](bool a, bool b) -> bool { return a && b; };
131 }
132
133
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 for (int i = 1; i < n->values.size(); i++) {
134
1/1
✓ Branch 2 taken 2 times.
2 PartialResult* second_value = exec(n->values[i], depth);
135
1/1
✓ Branch 1 taken 2 times.
2 partials.push_back(second_value);
136
137
1/1
✓ Branch 1 taken 2 times.
2 Constant* second = cast<Constant>(second_value);
138
139
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (first != nullptr && second != nullptr) {
140 2 Constant* value = nullptr;
141
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (n->resolved_operator != nullptr) {
143 Scope scope(bindings);
144
145 bindings.add(StringRef(), first_value, nullptr);
146 bindings.add(StringRef(), second_value, nullptr);
147
148 value = cast<Constant>(exec(n->resolved_operator, depth));
149
150 result = reduce(result, value->value.get<bool>());
151
152
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 } else if (n->native_operator != nullptr) {
153
1/1
✓ Branch 1 taken 2 times.
2 ConstantValue v = n->native_operator(first->value, second->value);
154
1/1
✓ Branch 2 taken 2 times.
2 result = reduce(result, v.get<bool>());
155 2 }
156
157 // Shortcut
158
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (n->op == BoolOperator::And && !result) {
159
1/1
✓ Branch 1 taken 1 times.
1 return False();
160 }
161
162
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (n->op == BoolOperator::Or && result) {
163 return True();
164 }
165
166 1 first = second;
167 1 first_value = second_value;
168
169 1 } else {
170 full_eval = false;
171 }
172 }
173
174
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (result) {
175
1/1
✓ Branch 1 taken 1 times.
1 return True();
176 } else {
177 return False();
178 }
179
180 BoolOp* boolop = root.new_object<BoolOp>();
181 boolop->op = n->op;
182 boolop->values.reserve(partials.size());
183 boolop->resolved_operator = n->resolved_operator;
184 boolop->native_operator = n->native_operator;
185
186 for (auto* p: partials) {
187 boolop->values.push_back((ExprNode*)p);
188 }
189 return boolop;
190 2 }
191
192 PartialResult* TreeEvaluator::binop(BinOp_t* n, int depth) {
193
194 61 auto* lhs = exec(n->left, depth);
195 61 auto* rhs = exec(n->right, depth);
196
197 // TODO: if they evaluate to constant that belong to the value root
198 // we can free them as soon as we finish combining the values
199
200 // We can execute the function because both arguments got resolved
201
4/8
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 61 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 61 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 61 times.
✗ Branch 8 not taken.
122 if (lhs != nullptr && lhs->is_instance<Constant>() && rhs != nullptr &&
202
1/2
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
61 rhs->is_instance<Constant>()) {
203 61 PartialResult* result = nullptr;
204
205 // Execute function
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (n->resolved_operator != nullptr) {
207 Scope scope(bindings);
208
209 bindings.add(StringRef(), lhs, nullptr);
210 bindings.add(StringRef(), rhs, nullptr);
211
212 result = exec(n->resolved_operator, depth);
213 }
214
215
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 else if (n->native_operator != nullptr) {
216 61 Constant* lhsc = static_cast<Constant*>(lhs);
217 61 Constant* rhsc = static_cast<Constant*>(rhs);
218
219
2/2
✓ Branch 1 taken 61 times.
✓ Branch 4 taken 61 times.
61 result = root.new_object<Constant>(n->native_operator(lhsc->value, rhsc->value));
220 }
221
222
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 if (result != nullptr) {
223 // Free the temporary values because we were able to combine them into a result
224 // lhs/rhs could be constant created by the parser, in that case their parent are not
225 // the root node and they should not be destroyed
226
227 // This is not valid, the constant could have been allocated by a function call
228 // they need to be freed when the call ends
229 // root.remove_child_if_parent(lhs, true);
230 // root.remove_child_if_parent(rhs, true);
231
232 61 return result;
233 }
234 }
235
236 // We could not execute, just return what we could execute so far
237 BinOp* binary = root.new_object<BinOp>();
238
239 binary->op = n->op;
240 binary->left = (ExprNode*)lhs;
241 binary->right = (ExprNode*)rhs;
242
243 binary->resolved_operator = n->resolved_operator;
244 binary->native_operator = n->native_operator;
245
246 // The partial operator becomes the owner of the partial results
247 lhs->move(binary);
248 rhs->move(binary);
249
250 return binary;
251 }
252
253 PartialResult* TreeEvaluator::unaryop(UnaryOp_t* n, int depth) {
254 3 auto* operand = exec(n->operand, depth);
255
256 // We can execute the function because both arguments got resolved
257
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 if (operand != nullptr && operand->is_instance<Constant>()) {
258
259 // Execute function
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (n->resolved_operator != nullptr) {
261 Scope scope(bindings);
262 bindings.add(StringRef(), operand, nullptr);
263 return exec(n->resolved_operator, depth);
264 }
265
266
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (n->native_operator != nullptr) {
267 3 Constant* operandc = static_cast<Constant*>(operand);
268
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 return root.new_object<Constant>(n->native_operator(operandc->value));
269 }
270 }
271
272 // We could not execute, just return what we could execute so far
273 UnaryOp* unary = root.new_object<UnaryOp>();
274 unary->op = n->op;
275 unary->operand = (ExprNode*)operand;
276 unary->resolved_operator = n->resolved_operator;
277 unary->native_operator = n->native_operator;
278 return unary;
279 }
280
281 PartialResult* TreeEvaluator::namedexpr(NamedExpr_t* n, int depth) {
282 1 PartialResult* value = exec(n->value, depth);
283
284
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (value->is_instance<Constant>()) {
285
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 bindings.add(StringRef(), value, nullptr);
286 1 return value;
287 }
288
289 // TODO: do i put the evaluated expression or the partial expression ?
290 NamedExpr* expr = root.new_object<NamedExpr>();
291 expr->target = n->target;
292 expr->value = (ExprNode*)value;
293 bindings.add(StringRef(), value, nullptr);
294 return expr;
295 }
296
297 PartialResult* TreeEvaluator::lambda(Lambda_t* n, int depth) {
298 auto* result = exec(n->body, depth);
299
300 if (result != nullptr && result->is_instance<Constant>())
301 return result;
302
303 // Here we should build a new lambda
304 // but we have to know which args were defined and which were not
305 // we can check n->args vardid and fetch them from the context
306 // if they are undefined we need to forward them
307 return None();
308 }
309
310 PartialResult* TreeEvaluator::ifexp(IfExp_t* n, int depth) {
311 2 Constant* value = cast<Constant>(exec(n->test, depth));
312
313
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (value == nullptr) {
314 // Could not evaluate the if test
315 // the entire expression cannot be evaluated
316 return n;
317 }
318
319 2 bool btrue = value->value.get<bool>();
320
321
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (btrue) {
322 1 return exec(n->body, depth);
323 }
324
325 1 return exec(n->orelse, depth);
326 }
327
328 PartialResult* TreeEvaluator::call_native(Call_t* call, BuiltinType_t* function, int depth) {
329 Array<PartialResult*> args;
330 Array<Constant*> value_args;
331 args.reserve(call->args.size());
332 value_args.reserve(call->args.size());
333
334 bool compile_time = true;
335
336 for (int i = 0; i < call->args.size(); i++) {
337 PartialResult* arg = exec(call->args[i], depth);
338 args.push_back(arg);
339
340 Constant* value = cast<Constant>(arg);
341 if (value != nullptr) {
342 value_args.push_back(value);
343 }
344 compile_time = compile_time && value != nullptr;
345 }
346
347 PartialResult* ret_result = nullptr;
348
349 if (compile_time) {
350 ConstantValue result = function->native_function(value_args);
351 ret_result = root.new_object<Constant>(result);
352 } else {
353 // FIXME: we probably need the context here
354 ret_result = function->native_macro(args);
355 }
356
357 for (PartialResult* arg: args) {
358 root.remove_child_if_parent(arg, true);
359 }
360
361 return ret_result;
362 }
363 PartialResult* TreeEvaluator::call_script(Call_t* call, FunctionDef_t* function, int depth) {
364 50 Scope scope(bindings);
365
366 // TODO: free the references held by the binding to save sapce
367 50 Array<PartialResult*> to_be_freed;
368
1/1
✓ Branch 2 taken 50 times.
50 to_be_freed.reserve(call->args.size());
369
370 // insert arguments to the context
371
2/2
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 50 times.
96 for (int i = 0; i < call->args.size(); i++) {
372
1/1
✓ Branch 2 taken 46 times.
46 PartialResult* arg = exec(call->args[i], depth);
373
1/1
✓ Branch 1 taken 46 times.
46 to_be_freed.push_back(arg);
374
2/2
✓ Branch 1 taken 46 times.
✓ Branch 4 taken 46 times.
46 bindings.add(StringRef(), arg, nullptr);
375 }
376
377
7/8
✓ Branch 4 taken 98 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 93 times.
✓ Branch 10 taken 5 times.
✓ Branch 12 taken 45 times.
✓ Branch 13 taken 48 times.
✓ Branch 16 taken 98 times.
✗ Branch 17 not taken.
98 EXEC_BODY(function->body);
378
379 // TODO: check if the execution was partial or full
380 // Actually; if we take ownership of the arguments
381 // when we generate partial nodes then we can always try to free regardless
382 for (PartialResult* arg: to_be_freed) {
383 root.remove_child_if_parent(arg, true);
384 }
385
386 return return_value;
387 50 }
388
389 Constant* object__new__(GCObject* parent, ClassDef* class_t) {
390 2 Constant* value = parent->new_object<Constant>();
391
392 2 Object* obj = value->new_object<Object>();
393 2 obj->attributes.resize(class_t->attributes.size());
394
395
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 for (int i = 0; i < class_t->attributes.size(); i++) {
396 4 obj->attributes[i] = obj->new_object<Constant>();
397 }
398
399
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 value->value = ConstantValue(obj);
400 2 return value;
401 }
402
403 PartialResult* TreeEvaluator::call_constructor(Call_t* call, ClassDef_t* cls, int depth) {
404 // Create the object
405
1/1
✓ Branch 1 taken 2 times.
2 Constant* obj = object__new__(&root, cls);
406
407 // Fetch construtor
408 // TODO: this lookup should not exist
409
3/3
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 String ctor_name = String(cls->name) + String(".__init__");
410
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 int ctorvarid = bindings.get_varid(ctor_name);
411
1/1
✓ Branch 2 taken 2 times.
2 FunctionDef* ctor = cast<FunctionDef>(bindings.get_value(ctorvarid));
412
413 2 Scope scope(bindings);
414
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ctor != nullptr) {
415
3/3
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
2 bindings.add(StringRef("self"), obj, nullptr);
416
417
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
6 for (auto& arg: call->args) {
418
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 bindings.add(StringRef(), arg, nullptr);
419 }
420
421
2/2
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 2 times.
6 for (auto& stmt: ctor->body) {
422
1/1
✓ Branch 1 taken 4 times.
4 exec(stmt, depth);
423
424
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (has_exceptions()) {
425 break;
426 }
427 }
428 }
429
430 2 return obj;
431 2 }
432
433 Constant* TreeEvaluator::make(ClassDef* class_t, Array<Constant*> args, int depth) {
434 // TODO: this should be generated inside the SEMA
435 //
436 String __new__ = class_t->cls_namespace + "." + "__new__";
437 String __init__ = class_t->cls_namespace + "." + "__init__";
438
439 int varid_new_fun = bindings.get_varid(__new__);
440 int varid_init_fun = bindings.get_varid(__init__);
441
442 FunctionDef* new_fun = cast<FunctionDef>(bindings.get_value(varid_new_fun));
443 FunctionDef* init_fun = cast<FunctionDef>(bindings.get_value(varid_init_fun));
444
445 Constant* self = nullptr;
446
447 if (new_fun != nullptr) {
448 Scope _(bindings);
449 bindings.add(StringRef(), class_t, nullptr);
450 for (auto& arg: args) {
451 bindings.add(StringRef(), arg, nullptr);
452 }
453
454 for (auto& stmt: new_fun->body) {
455 exec(stmt, depth);
456
457 if (return_value != nullptr) {
458 self = cast<Constant>(return_value);
459 break;
460 }
461
462 if (has_exceptions()) {
463 break;
464 }
465 }
466 }
467
468 if (init_fun != nullptr) {
469 Scope _(bindings);
470
471 bindings.add(StringRef(), self, nullptr);
472 for (auto& arg: args) {
473 bindings.add(StringRef(), arg, nullptr);
474 }
475
476 for (auto& stmt: init_fun->body) {
477 exec(stmt, depth);
478
479 if (has_exceptions()) {
480 break;
481 }
482 }
483 }
484
485 return self;
486 }
487
488 PartialResult* TreeEvaluator::make_generator(Call_t* call, FunctionDef_t* n, int depth) {
489
490 Generator* gen = root.new_object<Generator>();
491 gen->scope = bindings;
492
493 for (int i = 0; i < call->args.size(); i++) {
494 PartialResult* arg = exec(call->args[i], depth);
495 gen->scope.add(StringRef(), arg, nullptr);
496 }
497
498 Constant* val = root.new_object<Constant>();
499 val->value = ConstantValue(gen);
500
501 return val;
502 }
503
504 PartialResult* TreeEvaluator::call(Call_t* n, int depth) {
505
506 using TraceGuard = PopGuard<Array<StackTrace>, StackTrace>;
507
508 // Populate current stack with the expression that will branch out
509 52 get_kwtrace().expr = n;
510
511 52 TraceGuard _(traces);
512
1/1
✓ Branch 1 taken 52 times.
52 StackTrace& trace = traces.emplace_back();
513
514 // fetch the function we need to call
515
1/1
✓ Branch 1 taken 52 times.
52 auto* function = exec(n->func, depth);
516 assert(function, "Function should be found");
517
518
3/3
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 2 times.
52 if (FunctionDef_t* fun = cast<FunctionDef>(function)) {
519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (fun->generator) {
520 return make_generator(n, fun, depth);
521 }
522
1/1
✓ Branch 1 taken 50 times.
50 return call_script(n, fun, depth);
523 }
524
525
2/3
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (ClassDef_t* cls = cast<ClassDef_t>(function)) {
526
1/1
✓ Branch 1 taken 2 times.
2 return call_constructor(n, cls, depth);
527 }
528
529 if (BuiltinType_t* fun = cast<BuiltinType>(function)) {
530 return call_native(n, fun, depth);
531 }
532
533 /*
534 if (Coroutine_t* fun = cast<Coroutine>(function)) {
535 return call_coroutine(n, fun, depth);
536 }
537 */
538
539 // function could not be resolved at compile time
540 // return self ?
541 return nullptr;
542 52 }
543
544 PartialResult* TreeEvaluator::constant(Constant_t* n, int depth) {
545 188 Constant* cpy = root.copy(n);
546 188 return cpy;
547 }
548
549 PartialResult* TreeEvaluator::comment(Comment_t* n, int depth) { return nullptr; }
550
551 PartialResult* TreeEvaluator::name(Name_t* n, int depth) {
552
553 203 Node* result = nullptr;
554 203 int varid = -1;
555
556
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 186 times.
203 if (n->ctx == ExprContext::Store) {
557
1/1
✓ Branch 1 taken 17 times.
17 Constant* variable = root.new_object<Constant>();
558
1/1
✓ Branch 1 taken 17 times.
17 bindings.add(n->id, variable, nullptr);
559 17 return variable;
560 }
561
562
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 52 times.
186 if (n->dynamic) {
563 // Local variables | Arguments
564 kwassert(n->offset != -1, "Reference should have a reverse lookup offset");
565 134 varid = int(bindings.bindings.size()) - n->offset;
566 134 result = bindings.get_value(varid);
567 } else {
568 // Global variables
569 52 result = bindings.get_value(n->varid);
570 }
571
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186 times.
186 if (result == nullptr) {
573 bindings.dump(std::cout);
574 }
575 kwassert(result != nullptr, "Could not find variable");
576
577 186 String kindstr;
578
1/2
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
186 if (result != nullptr) {
579
1/1
✓ Branch 1 taken 186 times.
186 kindstr = str(result->kind);
580 }
581
582 186 String strresult;
583
3/4
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 134 times.
✓ Branch 3 taken 52 times.
186 if (result != nullptr && result->kind == NodeKind::Constant) {
584
1/1
✓ Branch 1 taken 134 times.
134 strresult = str(result);
585 }
586 kwdebug("Name (varid: {}), (size: {}) (offset: {}) resolved: {}",
587 n->varid,
588 n->size,
589 n->offset,
590 varid);
591 kwdebug("Looked for {} (id: {}) found {}: {}", n->id, n->varid, kindstr, strresult);
592 186 return result;
593 186 }
594
595 PartialResult* TreeEvaluator::functiondef(FunctionDef_t* n, int depth) {
596 // this should not be called
597 return_value = nullptr;
598 EXEC_BODY(n->body);
599 return return_value;
600 }
601
602 PartialResult* TreeEvaluator::invalidstmt(InvalidStatement_t* n, int depth) {
603 // FIXME: raise exception here
604 raise_exception(nullptr, nullptr);
605 return nullptr;
606 }
607
608 PartialResult* TreeEvaluator::returnstmt(Return_t* n, int depth) {
609 kwdebug("Compute return {}", str(n));
610
611
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 if (n->value.has_value()) {
612 47 set_return_value(exec(n->value.value(), depth));
613 kwdebug("Returning {}", str(return_value));
614 47 return return_value;
615 }
616
617 set_return_value(None());
618 return return_value;
619 }
620
621 PartialResult* TreeEvaluator::assign(Assign_t* n, int depth) {
622 21 PartialResult* value = exec(n->value, depth);
623
624 // Unpacking
625 21 TupleExpr* targets = cast<TupleExpr>(n->targets[0]);
626 21 TupleExpr* values = cast<TupleExpr>(value);
627
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
21 if (values != nullptr && targets != nullptr) {
628 assert(values->elts.size() == targets->elts.size(), "Size must match");
629
630 // this probably does not work quite right in some cases
631 for (int i = 0; i < values->elts.size(); i++) {
632 bindings.add(StringRef(), values->elts[i], nullptr);
633 }
634 return None();
635 }
636
637 21 Constant* new_value = cast<Constant>(value);
638
639
3/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
21 if (!n->targets.empty() && new_value != nullptr) {
640
641 // resolve the target
642 // the target should always be a reference to a constant/value
643 21 auto* gtarg = exec(n->targets[0], depth);
644 21 auto* target = cast<Constant>(gtarg);
645
646
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (target != nullptr) {
647 21 target->value = new_value->value;
648 }
649 21 return None();
650 }
651
652 bindings.add(StringRef(), value, nullptr);
653 return None();
654 }
655 PartialResult* TreeEvaluator::augassign(AugAssign_t* n, int depth) {
656
657 // This should a Name
658 28 auto* name = cast<Name>(n->target);
659
660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (name == nullptr) {
661 kwerror("Assign to {}", str(n->target->kind));
662 return None();
663 }
664
665 28 auto* left = exec(n->target, depth); // load a
666 28 auto* right = exec(n->value, depth); // load b
667
668 28 auto* left_v = cast<Constant>(left);
669 28 auto* right_v = cast<Constant>(right);
670
671
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 if (left_v != nullptr && right_v != nullptr) {
672 28 PartialResult* value = nullptr;
673
674 // Execute function
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (n->resolved_operator != nullptr) {
676 Scope scope(bindings);
677
678 bindings.add(StringRef(), left_v, nullptr);
679 bindings.add(StringRef(), right_v, nullptr);
680
681 value = exec(n->resolved_operator, depth);
682 }
683
684
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 else if (n->native_operator != nullptr) {
685
1/1
✓ Branch 1 taken 28 times.
28 auto result = n->native_operator(left_v->value, right_v->value);
686
1/1
✓ Branch 1 taken 28 times.
28 value = root.new_object<Constant>(result);
687 28 } else {
688 kwerror("Operator does not have implementation!");
689 }
690
691 28 bindings.set_value(name->varid, value); // store a
692 28 return None();
693 }
694
695 AugAssign* expr = root.new_object<AugAssign>();
696 // Do not use the evaluted target here
697 expr->target = n->target;
698 expr->op = n->op;
699 expr->value = (ExprNode*)right;
700 return expr;
701 }
702
703 PartialResult* TreeEvaluator::annassign(AnnAssign_t* n, int depth) {
704 2 PartialResult* value = None();
705
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (n->value.has_value()) {
706 2 value = exec(n->value.value(), depth);
707 }
708
709
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 bindings.add(StringRef(), value, nullptr);
710 2 return None();
711 }
712
713 PartialResult* TreeEvaluator::forstmt(For_t* n, int depth) {
714
715 // insert target into the context
716 // exec(n->target, depth);
717 int targetid = bindings.add(StringRef(), None(), nullptr);
718
719 PartialResult* iterator = exec(n->iter, depth);
720
721 bool broke = false;
722 loop_break = false;
723 loop_continue = false;
724
725 while (true) {
726 // Python does not create a new scope for `for`
727 // Scope scope(bindings);
728
729 // Get the value of the iterator
730 PartialResult* value = get_next(iterator, depth);
731
732 // or value == StopIteration
733 if (value == nullptr) {
734 break;
735 }
736
737 bindings.set_value(targetid, value);
738
739 for (StmtNode* stmt: n->body) {
740 exec(stmt, depth);
741
742 if (has_exceptions()) {
743 return None();
744 }
745
746 if (return_value != nullptr) {
747 return return_value;
748 }
749
750 if (loop_break) {
751 broke = true;
752 break;
753 }
754
755 if (loop_continue) {
756 break;
757 }
758 }
759
760 loop_break = false;
761 loop_continue = false;
762
763 if (broke) {
764 break;
765 }
766 }
767
768 EXEC_BODY(n->orelse);
769 return None();
770 }
771 PartialResult* TreeEvaluator::whilestmt(While_t* n, int depth) {
772
773 5 bool broke = false;
774 5 loop_break = false;
775 5 loop_continue = false;
776
777 while (true) {
778 17 Constant* value = cast<Constant>(exec(n->test, depth));
779 // TODO if it is not a value that means we could not evaluate it so we should return the
780 // node itself
781 assert(value, "While test should return a boolean");
782
783
3/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 3 times.
17 bool bcontinue = value != nullptr && value->value.get<bool>();
784
785
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
17 if (!bcontinue || broke) {
786 break;
787 }
788
789
2/2
✓ Branch 5 taken 38 times.
✓ Branch 6 taken 2 times.
40 for (StmtNode* stmt: n->body) {
790
1/1
✓ Branch 1 taken 38 times.
38 exec(stmt, depth);
791
792
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 37 times.
38 if (has_exceptions()) {
793
1/1
✓ Branch 1 taken 1 times.
1 return None();
794 }
795
796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (return_value != nullptr) {
797 return return_value;
798 }
799
800
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 36 times.
37 if (loop_break) {
801 1 broke = true;
802 1 break;
803 }
804
805
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 26 times.
36 if (loop_continue) {
806 10 break;
807 }
808 }
809
810 // reset
811 13 loop_break = false;
812 13 loop_continue = false;
813
814
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
13 if (broke) {
815 1 break;
816 }
817 12 }
818
819
1/10
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 4 times.
4 EXEC_BODY(n->orelse);
820 4 return None();
821 }
822
823 PartialResult* TreeEvaluator::ifstmt(If_t* n, int depth) {
824
825 29 Array<StmtNode*>& body = n->orelse;
826 // Chained
827
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
29 if (!n->tests.empty()) {
828 for (int i = 0; i < n->tests.size(); i++) {
829 Constant* value = cast<Constant>(exec(n->test, depth));
830 assert(value, "If test should return a boolean");
831
832 bool btrue = value->value.get<bool>();
833 if (btrue) {
834 body = n->bodies[i];
835 break;
836 }
837 }
838
839 EXEC_BODY(body);
840
841 return None();
842 }
843
844 // Simple
845 29 PartialResult* test = exec(n->test, depth);
846 29 Constant* value = cast<Constant>(test);
847 assert(value, "If test should return a boolean");
848
849 29 bool btrue = value->value.get<bool>();
850
851
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 24 times.
29 if (btrue) {
852 5 body = n->body;
853 }
854
855
7/8
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 5 times.
✓ Branch 10 taken 1 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 6 times.
✓ Branch 17 taken 23 times.
29 EXEC_BODY(body);
856 23 return None();
857 }
858
859 PartialResult* TreeEvaluator::assertstmt(Assert_t* n, int depth) {
860 4 PartialResult* btest = exec(n->test, depth);
861 4 Constant* value = cast<Constant>(btest);
862
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (value != nullptr) {
863
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
4 if (!value->value.get<bool>()) {
864 // make_ref(n, "AssertionError") n->msg
865 3 raise_exception(nullptr, nullptr);
866 3 return None();
867 }
868
869 // All Good
870 1 return None();
871 }
872
873 Assert* expr = root.new_object<Assert>();
874 expr->test = (ExprNode*)btest;
875 expr->msg = n->msg;
876 return expr;
877 }
878
879 PartialResult* TreeEvaluator::exprstmt(Expr_t* n, int depth) { return exec(n->value, depth); }
880 PartialResult* TreeEvaluator::pass(Pass_t* n, int depth) {
881 // return itself in case the pass is required for correctness
882 return n;
883 }
884 PartialResult* TreeEvaluator::breakstmt(Break_t* n, int depth) {
885 1 loop_break = true;
886 1 return n;
887 }
888 PartialResult* TreeEvaluator::continuestmt(Continue_t* n, int depth) {
889 10 loop_continue = true;
890 10 return n;
891 }
892
893 PartialResult* TreeEvaluator::inlinestmt(Inline_t* n, int depth) {
894
895
5/9
✓ Branch 4 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
2 EXEC_BODY(n->body);
896
897 return None();
898 }
899
900 PartialResult* TreeEvaluator::raise(Raise_t* n, int depth) {
901
902 if (n->exc.has_value()) {
903 // create a new exceptins
904 auto* obj = exec(n->exc.value(), depth);
905
906 if (n->cause.has_value()) {
907 cause = exec(n->cause.value(), depth);
908 }
909
910 raise_exception(obj, cause);
911 }
912
913 // FIXME: this re-reraise current exception
914 // check what happens if no exception exists
915 exceptions.push_back(nullptr);
916 return nullptr;
917 }
918
919 PartialResult* TreeEvaluator::trystmt(Try_t* n, int depth) {
920
921 EXEC_BODY(n->body);
922
923 if (has_exceptions()) {
924 // start handling all the exceptions we received
925 auto _ = HandleException(this);
926
927 ExceptHandler const* matched = nullptr;
928 lyException* latest_exception = exceptions[exceptions.size() - 1];
929
930 for (ExceptHandler const& handler: n->handlers) {
931 // match the exception type to the one we received
932
933 bool found_matcher = false;
934
935 // catch all
936 if (!handler.type.has_value()) {
937 matched = &handler;
938 found_matcher = true;
939 }
940
941 // FIXME: we do not have the type at runtime!!!
942 else if (equal(handler.type.value(), latest_exception->type)) {
943 matched = &handler;
944 found_matcher = true;
945 }
946
947 if (found_matcher) {
948 break;
949 }
950 }
951
952 if (matched != nullptr) {
953 Scope _(bindings);
954 Constant exception;
955
956 // Execute Handler
957 if (matched->name.has_value()) {
958 exception.value = latest_exception->custom;
959 bindings.add(matched->name.value(), &exception, nullptr);
960 }
961
962 EXEC_BODY(matched->body);
963
964 // Exception was handled!
965 exceptions.pop_back();
966 cause = nullptr;
967 }
968 // Exception was NOT handled
969 // leave the exception as is so we continue moving back
970
971 } else {
972 EXEC_BODY(n->orelse);
973 }
974
975 auto _ = HandleException(this);
976
977 EXEC_BODY(n->finalbody);
978
979 // we are not handling exception anymore
980 handling_exceptions = 0;
981
982 return None();
983 }
984
985 /*
986 https://stackoverflow.com/questions/60926323/can-i-raise-an-exception-in-exit
987
988 manager = (EXPRESSION)
989 enter = type(manager).__enter__
990 exit = type(manager).__exit__
991 value = enter(manager)
992 hit_except = False
993
994 try:
995 TARGET = value
996 SUITE
997 except:
998 hit_except = True
999 if not exit(manager, *sys.exc_kwinfo()):
1000 raise
1001 finally:
1002 if not hit_except:
1003 exit(manager, None, None, None)
1004
1005 */
1006 PartialResult* TreeEvaluator::with(With_t* n, int depth) {
1007 // Call enter
1008 for (auto& item: n->items) {
1009 auto* ctx = exec(item.context_expr, depth);
1010 auto* result = call_enter(ctx, depth);
1011
1012 if (item.optional_vars.has_value()) {
1013 bindings.add(StringRef(""), result, nullptr);
1014 }
1015 }
1016
1017 EXEC_BODY(n->body);
1018
1019 // Call exit regardless of exception status
1020 auto _ = HandleException(this);
1021
1022 for (auto& item: n->items) {
1023 auto* ctx = exec(item.context_expr, depth);
1024 call_exit(ctx, depth);
1025 }
1026
1027 return nullptr;
1028 }
1029
1030 PartialResult* TreeEvaluator::match(Match_t* n, int depth) { return nullptr; }
1031 PartialResult* TreeEvaluator::import(Import_t* n, int depth) { return nullptr; }
1032 PartialResult* TreeEvaluator::importfrom(ImportFrom_t* n, int depth) { return nullptr; }
1033
1034 PartialResult* TreeEvaluator::dictexpr(DictExpr_t* n, int depth) { return nullptr; }
1035 PartialResult* TreeEvaluator::setexpr(SetExpr_t* n, int depth) { return nullptr; }
1036 PartialResult* TreeEvaluator::listcomp(ListComp_t* n, int depth) { return nullptr; }
1037 PartialResult* TreeEvaluator::generateexpr(GeneratorExp_t* n, int depth) { return nullptr; }
1038 PartialResult* TreeEvaluator::setcomp(SetComp_t* n, int depth) { return nullptr; }
1039 PartialResult* TreeEvaluator::dictcomp(DictComp_t* n, int depth) { return nullptr; }
1040
1041 PartialResult* TreeEvaluator::yield(Yield_t* n, int depth) {
1042 if (n->value.has_value()) {
1043 auto* value = exec(n->value.value(), depth);
1044 // Get the top level functions and create a lambda
1045 yielding = true;
1046 return_value = value;
1047 return value;
1048 }
1049 return None();
1050 }
1051 PartialResult* TreeEvaluator::yieldfrom(YieldFrom_t* n, int depth) { return nullptr; }
1052 PartialResult* TreeEvaluator::joinedstr(JoinedStr_t* n, int depth) { return nullptr; }
1053 PartialResult* TreeEvaluator::formattedvalue(FormattedValue_t* n, int depth) { return nullptr; }
1054
1055 PartialResult* TreeEvaluator::starred(Starred_t* n, int depth) { return nullptr; }
1056 PartialResult* TreeEvaluator::listexpr(ListExpr_t* n, int depth) { return nullptr; }
1057 PartialResult* TreeEvaluator::tupleexpr(TupleExpr_t* n, int depth) { return nullptr; }
1058 PartialResult* TreeEvaluator::deletestmt(Delete_t* n, int depth) { return nullptr; }
1059
1060 PartialResult* TreeEvaluator::await(Await_t* n, int depth) { return nullptr; }
1061
1062 // Objects
1063 PartialResult* TreeEvaluator::slice(Slice_t* n, int depth) { return nullptr; }
1064 PartialResult* TreeEvaluator::attribute(Attribute_t* n, int depth) {
1065 // a.b
1066 6 Constant* obj = cast<Constant>(exec(n->value, depth));
1067
1068
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (obj != nullptr) {
1069 6 NativeObject* nv = obj->value.get<NativeObject*>();
1070
1071
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (nv->class_id == meta::type_id<Object>()) {
1072 6 Object* nvobj = reinterpret_cast<Object*>(nv);
1073
1074 // If Object held ConstantValue
1075 // we would pass down a copy of the attribute not a reference to them
1076 // return root.new_object<Constant>(nvobj->attributes[n->attrid]);
1077
1078 // Object is holding a Constant Node
1079 // we pass a reference to it not a copy
1080 6 return nvobj->attributes[n->attrid];
1081 }
1082 }
1083
1084 return nullptr;
1085 }
1086 PartialResult* TreeEvaluator::subscript(Subscript_t* n, int depth) { return nullptr; }
1087
1088 // Call __next__ for a given object
1089 PartialResult* TreeEvaluator::get_next(Node* iterator, int depth) {
1090 // FIXME: implement me
1091 return nullptr;
1092 }
1093
1094 PartialResult* TreeEvaluator::call_enter(Node* ctx, int depth) {
1095 // Call __enter__
1096 return nullptr;
1097 }
1098 PartialResult* TreeEvaluator::call_exit(Node* ctx, int depth) {
1099 // Call __exit__
1100 return nullptr;
1101 }
1102
1103 // Types
1104 // -----
1105 PartialResult* TreeEvaluator::classdef(ClassDef_t* n, int depth) { return nullptr; }
1106
1107 PartialResult* TreeEvaluator::dicttype(DictType_t* n, int depth) { return nullptr; }
1108 PartialResult* TreeEvaluator::arraytype(ArrayType_t* n, int depth) { return nullptr; }
1109 PartialResult* TreeEvaluator::tupletype(TupleType_t* n, int depth) { return nullptr; }
1110 PartialResult* TreeEvaluator::arrow(Arrow_t* n, int depth) { return nullptr; }
1111 PartialResult* TreeEvaluator::classtype(ClassType_t* n, int depth) { return nullptr; }
1112 PartialResult* TreeEvaluator::settype(SetType_t* n, int depth) { return nullptr; }
1113 PartialResult* TreeEvaluator::builtintype(BuiltinType_t* n, int depth) {
1114 // return self because it also holds the native function to use
1115 return n;
1116 }
1117
1118 // Match
1119 // -----
1120 PartialResult* TreeEvaluator::matchvalue(MatchValue_t* n, int depth) { return nullptr; }
1121 PartialResult* TreeEvaluator::matchsingleton(MatchSingleton_t* n, int depth) { return nullptr; }
1122 PartialResult* TreeEvaluator::matchsequence(MatchSequence_t* n, int depth) { return nullptr; }
1123 PartialResult* TreeEvaluator::matchmapping(MatchMapping_t* n, int depth) { return nullptr; }
1124 PartialResult* TreeEvaluator::matchclass(MatchClass_t* n, int depth) { return nullptr; }
1125 PartialResult* TreeEvaluator::matchstar(MatchStar_t* n, int depth) { return nullptr; }
1126 PartialResult* TreeEvaluator::matchas(MatchAs_t* n, int depth) { return nullptr; }
1127 PartialResult* TreeEvaluator::matchor(MatchOr_t* n, int depth) { return nullptr; }
1128
1129 PartialResult* TreeEvaluator::global(Global_t* n, int depth) {
1130 // we don't really need it right now, we are not enforcing this
1131 // might be sema business anyway
1132 return nullptr;
1133 }
1134 PartialResult* TreeEvaluator::nonlocal(Nonlocal_t* n, int depth) {
1135 // we don't really need it right now, we are not enforcing this
1136 // might be sema business anyway
1137 return nullptr;
1138 }
1139
1140 #define PRINT(msg) std::cout << (msg) << "\n";
1141
1142 void printkwtrace(StackTrace& trace) {
1143
1/1
✓ Branch 2 taken 8 times.
8 String file = "<input>";
1144 8 int line = -1;
1145
1/1
✓ Branch 2 taken 8 times.
8 String parent = "<module>";
1146 8 String expr;
1147
1148
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (trace.stmt != nullptr) {
1149 8 line = trace.stmt->lineno;
1150
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 parent = shortprint(get_parent(trace.stmt));
1151
1/1
✓ Branch 1 taken 8 times.
8 expr = shortprint(trace.stmt);
1152 } else if (trace.expr != nullptr) {
1153 line = trace.expr->lineno;
1154 parent = shortprint(trace.stmt);
1155 expr = shortprint(trace.stmt);
1156 }
1157
1158
1/1
✓ Branch 1 taken 8 times.
8 fmt::print(" File \"{}\", line {}, in {}\n", file, line, parent);
1159
1/1
✓ Branch 1 taken 8 times.
8 fmt::print(" {}\n", expr);
1160 8 }
1161
1162 PartialResult* TreeEvaluator::eval(StmtNode_t* stmt) {
1163 28 auto* result = exec(stmt, 0);
1164
1165
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 25 times.
28 if (has_exceptions()) {
1166 3 lyException* except = exceptions[exceptions.size() - 1];
1167
1168 assert(except != nullptr, "Exception is null");
1169
1170
1/1
✓ Branch 1 taken 3 times.
3 fmt::print("Traceback (most recent call last):\n");
1171
2/2
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 3 times.
11 for (StackTrace& st: except->traces) {
1172
1/1
✓ Branch 1 taken 8 times.
8 printkwtrace(st);
1173 }
1174
1175
1/1
✓ Branch 2 taken 3 times.
3 String exception_type = "AssertionError";
1176
1/1
✓ Branch 2 taken 3 times.
3 String exception_msg = "Very bad";
1177
1/1
✓ Branch 1 taken 3 times.
3 fmt::print("{}: {}\n", exception_type, exception_msg);
1178 3 }
1179
1180 28 return result;
1181 }
1182
1183 } // namespace lython
1184