Skip to content

Commit 13d6739

Browse files
committed
py: Generators can have their locals closed over.
1 parent 2bf7c09 commit 13d6739

2 files changed

Lines changed: 40 additions & 8 deletions

File tree

py/objgenerator.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,10 @@ mp_obj_t mp_obj_new_gen_instance(const byte *bytecode, uint n_args, const mp_obj
236236
machine_uint_t n_exc_stack = bytecode[2] | (bytecode[3] << 8);
237237
bytecode += 4;
238238

239-
// bytecode prelude: initialise closed over variables
240-
// TODO
241-
// for now we just make sure there are no cells variables
242-
// need to work out how to implement closed over variables in generators
243-
assert(bytecode[0] == 0);
244-
bytecode += 1;
245-
239+
// allocate the generator object, with room for local stack and exception stack
246240
mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t));
247241
o->base.type = &mp_type_gen_instance;
248242
o->code_info = code_info;
249-
o->ip = bytecode;
250243
o->sp = &o->state[0] - 1; // sp points to top of stack, which starts off 1 below the state
251244
o->exc_sp = (mp_exc_stack_t*)(o->state + n_state) - 1;
252245
o->n_state = n_state;
@@ -259,5 +252,18 @@ mp_obj_t mp_obj_new_gen_instance(const byte *bytecode, uint n_args, const mp_obj
259252
o->state[n_state - 1 - n_args - i] = args2[i];
260253
}
261254

255+
// bytecode prelude: initialise closed over variables
256+
for (uint n_local = *bytecode++; n_local > 0; n_local--) {
257+
uint local_num = *bytecode++;
258+
if (local_num < n_args + n_args2) {
259+
o->state[n_state - 1 - local_num] = mp_obj_new_cell(o->state[n_state - 1 - local_num]);
260+
} else {
261+
o->state[n_state - 1 - local_num] = mp_obj_new_cell(MP_OBJ_NULL);
262+
}
263+
}
264+
265+
// set ip to start of actual byte code
266+
o->ip = bytecode;
267+
262268
return o;
263269
}

tests/basics/generator-closure.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# a generator that closes over outer variables
2+
def f():
3+
x = 1 # closed over by g
4+
def g():
5+
yield x
6+
yield x + 1
7+
return g()
8+
for i in f():
9+
print(i)
10+
11+
# a generator that has its variables closed over
12+
def f():
13+
x = 1 # closed over by g
14+
def g():
15+
return x + 1
16+
yield g()
17+
x = 2
18+
yield g()
19+
for i in f():
20+
print(i)
21+
22+
# using comprehensions, the inner generator closes over y
23+
generator_of_generators = (((x, y) for x in range(2)) for y in range(3))
24+
for i in generator_of_generators:
25+
for j in i:
26+
print(j)

0 commit comments

Comments
 (0)