|
|
@@ -1,7 +1,9 @@
|
|
|
#include "pocketpy/interpreter/generator.h"
|
|
|
#include "pocketpy/interpreter/frame.h"
|
|
|
#include "pocketpy/interpreter/vm.h"
|
|
|
+#include "pocketpy/interpreter/bindings.h"
|
|
|
#include "pocketpy/objects/base.h"
|
|
|
+#include "pocketpy/objects/exception.h"
|
|
|
#include "pocketpy/pocketpy.h"
|
|
|
#include <stdbool.h>
|
|
|
#include <assert.h>
|
|
|
@@ -21,12 +23,16 @@ void Generator__dtor(Generator* ud) {
|
|
|
if(ud->frame) Frame__delete(ud->frame);
|
|
|
}
|
|
|
|
|
|
-bool generator__next__(int argc, py_Ref argv) {
|
|
|
- PY_CHECK_ARGC(1);
|
|
|
- Generator* ud = py_touserdata(argv);
|
|
|
+PK_DEFINE_NEXT_WRAPPER(generator)
|
|
|
+
|
|
|
+int generator__iternext(py_Ref self) {
|
|
|
+ Generator* ud = py_touserdata(self);
|
|
|
py_StackRef p0 = py_peek(0);
|
|
|
VM* vm = pk_current_vm;
|
|
|
- if(ud->state == 2) return StopIteration();
|
|
|
+ if(ud->state == 2) {
|
|
|
+ py_newnil(py_retval());
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
// reset frame->p0
|
|
|
assert(!ud->frame->is_locals_special);
|
|
|
@@ -35,7 +41,7 @@ bool generator__next__(int argc, py_Ref argv) {
|
|
|
ud->frame->locals = ud->frame->p0 + locals_offset;
|
|
|
|
|
|
// restore the context
|
|
|
- py_Ref backup = py_getslot(argv, 0);
|
|
|
+ py_Ref backup = py_getslot(self, 0);
|
|
|
int length = py_list_len(backup);
|
|
|
py_TValue* p = py_list_data(backup);
|
|
|
for(int i = 0; i < length; i++)
|
|
|
@@ -51,10 +57,19 @@ bool generator__next__(int argc, py_Ref argv) {
|
|
|
if(res == RES_ERROR) {
|
|
|
ud->state = 2; // end this generator immediately on error
|
|
|
if(py_matchexc(tp_StopIteration)) {
|
|
|
+ // PEP 479: a `StopIteration` escaping the body must not be mistaken
|
|
|
+ // for the generator finishing normally
|
|
|
+ py_TValue stop_iter = *py_retval(); // stashed there by py_matchexc
|
|
|
py_clearexc(p0);
|
|
|
- return true;
|
|
|
+ // root it on the stack, `RuntimeError` below allocates
|
|
|
+ py_StackRef inner = py_pushtmp();
|
|
|
+ *inner = stop_iter;
|
|
|
+ RuntimeError("generator raised StopIteration");
|
|
|
+ BaseException* exc = py_touserdata(&vm->unhandled_exc);
|
|
|
+ exc->inner_exc = *inner;
|
|
|
+ py_pop();
|
|
|
}
|
|
|
- return false;
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
if(res == RES_YIELD) {
|
|
|
@@ -67,14 +82,13 @@ bool generator__next__(int argc, py_Ref argv) {
|
|
|
vm->top_frame = vm->top_frame->f_back;
|
|
|
vm->recursion_depth--;
|
|
|
ud->state = 1;
|
|
|
- return true;
|
|
|
+ return 1;
|
|
|
} else {
|
|
|
assert(res == RES_RETURN);
|
|
|
ud->state = 2;
|
|
|
- // raise StopIteration(<retval>)
|
|
|
- bool ok = py_tpcall(tp_StopIteration, 1, py_retval());
|
|
|
- if(!ok) return false;
|
|
|
- return py_raise(py_retval());
|
|
|
+ // `py_retval()` already holds the return value, which the caller turns
|
|
|
+ // into `StopIteration(<retval>)` if it needs a real exception
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
|