summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/emitbc.c26
-rw-r--r--py/obj.h3
-rw-r--r--py/objgetitemiter.c53
-rw-r--r--py/objstr.c4
-rw-r--r--py/objtype.c17
-rw-r--r--py/py.mk1
-rw-r--r--py/qstrdefs.h4
-rw-r--r--py/runtime.c33
-rw-r--r--py/vm.c9
-rw-r--r--stm/Makefile1
-rw-r--r--stm/gccollect.c39
-rw-r--r--stm/gccollect.h9
-rw-r--r--stm/main.c38
-rw-r--r--stm/stm32f405.ld1
-rw-r--r--tests/basics/getitem.py22
15 files changed, 185 insertions, 75 deletions
diff --git a/py/emitbc.c b/py/emitbc.c
index 10a95fbcfa..9fa2880ecb 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -71,10 +71,14 @@ static void emit_write_code_info_qstr(emit_t* emit, qstr qstr) {
c[3] = (qstr >> 24) & 0xff;
}
-static void emit_write_code_info_byte_byte(emit_t* emit, byte b1, uint b2) {
- byte* c = emit_get_cur_to_write_code_info(emit, 2);
- c[0] = b1;
- c[1] = b2;
+static void emit_write_code_info_bytes_lines(emit_t* emit, uint bytes_to_skip, uint lines_to_skip) {
+ for (; bytes_to_skip > 31; bytes_to_skip -= 31) {
+ *emit_get_cur_to_write_code_info(emit, 1) = 31;
+ }
+ for (; lines_to_skip > 7; lines_to_skip -= 7) {
+ *emit_get_cur_to_write_code_info(emit, 1) = 7 << 5;
+ }
+ *emit_get_cur_to_write_code_info(emit, 1) = bytes_to_skip | (lines_to_skip << 5);
}
// all functions must go through this one to emit byte code
@@ -218,7 +222,7 @@ static void emit_bc_end_pass(emit_t *emit) {
printf("ERROR: stack size not back to zero; got %d\n", emit->stack_size);
}
- emit_write_code_info_byte_byte(emit, 0, 0); // end of line number info
+ emit_write_code_info_bytes_lines(emit, 0, 0); // end of line number info
if (emit->pass == PASS_2) {
// calculate size of code in bytes
@@ -246,15 +250,9 @@ static void emit_bc_set_stack_size(emit_t *emit, int size) {
static void emit_bc_set_source_line(emit_t *emit, int source_line) {
//printf("source: line %d -> %d offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->byte_code_offset);
if (source_line > emit->last_source_line) {
- int bytes_to_skip = emit->byte_code_offset - emit->last_source_line_offset;
- for (; bytes_to_skip > 255; bytes_to_skip -= 255) {
- emit_write_code_info_byte_byte(emit, 255, 0);
- }
- int lines_to_skip = source_line - emit->last_source_line;
- for (; lines_to_skip > 255; lines_to_skip -= 255) {
- emit_write_code_info_byte_byte(emit, 0, 255);
- }
- emit_write_code_info_byte_byte(emit, bytes_to_skip, lines_to_skip);
+ uint bytes_to_skip = emit->byte_code_offset - emit->last_source_line_offset;
+ uint lines_to_skip = source_line - emit->last_source_line;
+ emit_write_code_info_bytes_lines(emit, bytes_to_skip, lines_to_skip);
//printf(" %d %d\n", bytes_to_skip, lines_to_skip);
emit->last_source_line_offset = emit->byte_code_offset;
emit->last_source_line = source_line;
diff --git a/py/obj.h b/py/obj.h
index 68d9588b4d..e122f5a2bf 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -39,7 +39,7 @@ typedef struct _mp_obj_base_t mp_obj_base_t;
#define MP_OBJ_IS_SMALL_INT(o) ((((mp_small_int_t)(o)) & 1) != 0)
#define MP_OBJ_IS_QSTR(o) ((((mp_small_int_t)(o)) & 3) == 2)
#define MP_OBJ_IS_OBJ(o) ((((mp_small_int_t)(o)) & 3) == 0)
-#define MP_OBJ_IS_TYPE(o, t) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)(o))->type == (t)))
+#define MP_OBJ_IS_TYPE(o, t) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)(o))->type == (t))) // this does not work for checking a string, use below macro for that
#define MP_OBJ_IS_STR(o) (MP_OBJ_IS_QSTR(o) || MP_OBJ_IS_TYPE(o, &str_type))
#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_small_int_t)(o)) >> 1)
@@ -231,6 +231,7 @@ mp_obj_t mp_obj_new_dict(int n_args);
mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items);
mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step);
mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self);
+mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args);
mp_obj_t mp_obj_new_module(qstr module_name);
mp_obj_type_t *mp_obj_get_type(mp_obj_t o_in);
diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c
new file mode 100644
index 0000000000..40ed1a1520
--- /dev/null
+++ b/py/objgetitemiter.c
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "runtime.h"
+
+// this is a wrapper object that is turns something that has a __getitem__ method into an iterator
+
+typedef struct _mp_obj_getitem_iter_t {
+ mp_obj_base_t base;
+ mp_obj_t args[3];
+} mp_obj_getitem_iter_t;
+
+static mp_obj_t it_iternext(mp_obj_t self_in) {
+ mp_obj_getitem_iter_t *self = self_in;
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ // try to get next item
+ mp_obj_t value = rt_call_method_n_kw(1, 0, self->args);
+ self->args[2] = MP_OBJ_NEW_SMALL_INT(MP_OBJ_SMALL_INT_VALUE(self->args[2]) + 1);
+ nlr_pop();
+ return value;
+ } else {
+ // an exception was raised
+ if (MP_OBJ_IS_TYPE(nlr.ret_val, &exception_type) && mp_obj_exception_get_type(nlr.ret_val) == MP_QSTR_StopIteration) {
+ // return mp_const_stop_iteration instead of raising StopIteration
+ return mp_const_stop_iteration;
+ } else {
+ // re-raise exception
+ nlr_jump(nlr.ret_val);
+ }
+ }
+}
+
+static const mp_obj_type_t it_type = {
+ { &mp_const_type },
+ "iterator",
+ .iternext = it_iternext
+};
+
+// args are those returned from rt_load_method_maybe (ie either an attribute or a method)
+mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args) {
+ mp_obj_getitem_iter_t *o = m_new_obj(mp_obj_getitem_iter_t);
+ o->base.type = &it_type;
+ o->args[0] = args[0];
+ o->args[1] = args[1];
+ o->args[2] = MP_OBJ_NEW_SMALL_INT(0);
+ return o;
+}
diff --git a/py/objstr.c b/py/objstr.c
index 723eebd614..3a4d69cfcc 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -77,7 +77,7 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
uint index = mp_get_index(mp_obj_get_type(lhs_in), lhs_len, rhs_in);
if (MP_OBJ_IS_TYPE(lhs_in, &bytes_type)) {
- return MP_OBJ_NEW_SMALL_INT(lhs_data[index]);
+ return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)lhs_data[index]);
} else {
return mp_obj_new_str(lhs_data + index, 1, true);
}
@@ -549,7 +549,7 @@ mp_obj_t bytes_it_iternext(mp_obj_t self_in) {
mp_obj_str_it_t *self = self_in;
GET_STR_DATA_LEN(self->str, str, len);
if (self->cur < len) {
- mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(str[self->cur]);
+ mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT((mp_small_int_t)str[self->cur]);
self->cur += 1;
return o_out;
} else {
diff --git a/py/objtype.c b/py/objtype.c
index 75755f4fb9..9cb9b8654d 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -116,9 +116,8 @@ static mp_obj_t class_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const m
return o;
}
-// TODO somehow replace const char * with a qstr
-static const char *binary_op_method_name[] = {
- [RT_BINARY_OP_SUBSCR] = "__getitem__",
+static const qstr binary_op_method_name[] = {
+ [RT_BINARY_OP_SUBSCR] = MP_QSTR___getitem__,
/*
RT_BINARY_OP_OR,
RT_BINARY_OP_XOR,
@@ -126,8 +125,8 @@ static const char *binary_op_method_name[] = {
RT_BINARY_OP_LSHIFT,
RT_BINARY_OP_RSHIFT,
*/
- [RT_BINARY_OP_ADD] = "__add__",
- [RT_BINARY_OP_SUBTRACT] = "__sub__",
+ [RT_BINARY_OP_ADD] = MP_QSTR___add__,
+ [RT_BINARY_OP_SUBTRACT] = MP_QSTR___sub__,
/*
RT_BINARY_OP_MULTIPLY,
RT_BINARY_OP_FLOOR_DIVIDE,
@@ -157,16 +156,16 @@ static const char *binary_op_method_name[] = {
RT_COMPARE_OP_IS,
RT_COMPARE_OP_IS_NOT,
*/
- [RT_COMPARE_OP_EXCEPTION_MATCH] = "__not_implemented__",
+ [RT_COMPARE_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size
};
static mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
mp_obj_class_t *lhs = lhs_in;
- const char *op_name = binary_op_method_name[op];
- if (op_name == NULL) {
+ qstr op_name = binary_op_method_name[op];
+ if (op_name == 0) {
return MP_OBJ_NULL;
}
- mp_obj_t member = mp_obj_class_lookup(lhs->base.type, QSTR_FROM_STR_STATIC(op_name));
+ mp_obj_t member = mp_obj_class_lookup(lhs->base.type, op_name);
if (member != MP_OBJ_NULL) {
return rt_call_function_2(member, lhs_in, rhs_in);
} else {
diff --git a/py/py.mk b/py/py.mk
index d4946e2838..742e6e398e 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -47,6 +47,7 @@ PY_O_BASENAME = \
objfloat.o \
objfun.o \
objgenerator.o \
+ objgetitemiter.o \
objint.o \
objint_longlong.o \
objlist.o \
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index f2c4dfd97f..e76efaf0e0 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -13,6 +13,10 @@ Q(__next__)
Q(__qualname__)
Q(__repl_print__)
+Q(__getitem__)
+Q(__add__)
+Q(__sub__)
+
Q(micropython)
Q(byte_code)
Q(native)
diff --git a/py/runtime.c b/py/runtime.c
index 25992a52c0..8ad98d5f12 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -87,7 +87,7 @@ void rt_init(void) {
// init loaded modules table
mp_map_init(&map_loaded_modules, 3);
- // built-in exceptions (TODO, make these proper classes)
+ // built-in exceptions (TODO, make these proper classes, and const if possible)
mp_map_add_qstr(&map_builtins, MP_QSTR_AttributeError, mp_obj_new_exception(MP_QSTR_AttributeError));
mp_map_add_qstr(&map_builtins, MP_QSTR_IndexError, mp_obj_new_exception(MP_QSTR_IndexError));
mp_map_add_qstr(&map_builtins, MP_QSTR_KeyError, mp_obj_new_exception(MP_QSTR_KeyError));
@@ -100,6 +100,7 @@ void rt_init(void) {
mp_map_add_qstr(&map_builtins, MP_QSTR_OverflowError, mp_obj_new_exception(MP_QSTR_OverflowError));
mp_map_add_qstr(&map_builtins, MP_QSTR_OSError, mp_obj_new_exception(MP_QSTR_OSError));
mp_map_add_qstr(&map_builtins, MP_QSTR_AssertionError, mp_obj_new_exception(MP_QSTR_AssertionError));
+ mp_map_add_qstr(&map_builtins, MP_QSTR_StopIteration, mp_obj_new_exception(MP_QSTR_StopIteration));
// built-in objects
mp_map_add_qstr(&map_builtins, MP_QSTR_Ellipsis, mp_const_ellipsis);
@@ -809,7 +810,7 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
// use load_method
mp_obj_t dest[2];
rt_load_method(base, attr, dest);
- if (dest[1] == NULL) {
+ if (dest[1] == MP_OBJ_NULL) {
// load_method returned just a normal attribute
return dest[0];
} else {
@@ -818,9 +819,10 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
}
}
-void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
- DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr));
-
+// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL
+// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
+// method attribute found, returns: dest[0] == <method>, dest[1] == <self>
+static void rt_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
// clear output to indicate no attribute/method found yet
dest[0] = MP_OBJ_NULL;
dest[1] = MP_OBJ_NULL;
@@ -834,7 +836,7 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
}
// if nothing found yet, look for built-in and generic names
- if (dest[0] == NULL) {
+ if (dest[0] == MP_OBJ_NULL) {
if (attr == MP_QSTR___next__ && type->iternext != NULL) {
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
dest[1] = base;
@@ -865,8 +867,14 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
}
}
}
+}
+
+void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
+ DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr));
+
+ rt_load_method_maybe(base, attr, dest);
- if (dest[0] == NULL) {
+ if (dest[0] == MP_OBJ_NULL) {
// no attribute/method called attr
// following CPython, we give a more detailed error message for type objects
if (MP_OBJ_IS_TYPE(base, &mp_const_type)) {
@@ -914,7 +922,16 @@ mp_obj_t rt_getiter(mp_obj_t o_in) {
if (type->getiter != NULL) {
return type->getiter(o_in);
} else {
- nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", type->name));
+ // check for __getitem__ method
+ mp_obj_t dest[2];
+ rt_load_method_maybe(o_in, qstr_from_str("__getitem__"), dest);
+ if (dest[0] != MP_OBJ_NULL) {
+ // __getitem__ exists, create an iterator
+ return mp_obj_new_getitem_iter(dest);
+ } else {
+ // object not iterable
+ nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", type->name));
+ }
}
}
diff --git a/py/vm.c b/py/vm.c
index affa5943bd..82a9f893f3 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -550,12 +550,9 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
machine_uint_t source_line = 1;
machine_uint_t bc = save_ip - code_info - code_info_size;
//printf("find %lu %d %d\n", bc, code_info[12], code_info[13]);
- for (const byte* ci = code_info + 12; bc >= ci[0]; ci += 2) {
- bc -= ci[0];
- source_line += ci[1];
- if (ci[0] == 0 && ci[1] == 0) {
- break;
- }
+ for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) {
+ bc -= *ci & 31;
+ source_line += *ci >> 5;
}
mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name);
}
diff --git a/stm/Makefile b/stm/Makefile
index b3483e089d..c0ae6a89c0 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -40,6 +40,7 @@ SRC_C = \
string0.c \
malloc0.c \
systick.c \
+ gccollect.c \
lexerfatfs.c \
led.c \
lcd.c \
diff --git a/stm/gccollect.c b/stm/gccollect.c
new file mode 100644
index 0000000000..c0f67ac0d5
--- /dev/null
+++ b/stm/gccollect.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "gc.h"
+#include "gccollect.h"
+#include "systick.h"
+
+void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end);
+
+void gc_collect(void) {
+ uint32_t start = sys_tick_counter;
+ gc_collect_start();
+ gc_collect_root((void**)&_ram_start, ((uint32_t)&_heap_start - (uint32_t)&_ram_start) / 4);
+ machine_uint_t regs[10];
+ gc_helper_get_regs_and_clean_stack(regs, HEAP_END);
+ gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack
+ gc_collect_end();
+ uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
+
+ if (0) {
+ // print GC info
+ gc_info_t info;
+ gc_info(&info);
+ printf("GC@%lu %lums\n", start, ticks);
+ printf(" %lu total\n", info.total);
+ printf(" %lu : %lu\n", info.used, info.free);
+ printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
+ }
+}
+
+static mp_obj_t pyb_gc(void) {
+ gc_collect();
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_gc_obj, pyb_gc);
diff --git a/stm/gccollect.h b/stm/gccollect.h
new file mode 100644
index 0000000000..6467ec7d21
--- /dev/null
+++ b/stm/gccollect.h
@@ -0,0 +1,9 @@
+#define HEAP_END (0x2001c000) // tunable
+#define RAM_END (0x20020000) // fixed for chip
+
+extern uint32_t _ram_start;
+extern uint32_t _heap_start;
+
+void gc_collect(void);
+
+MP_DECLARE_CONST_FUN_OBJ(pyb_gc_obj);
diff --git a/stm/main.c b/stm/main.c
index 2ce2b6dc61..4114ec1979 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -28,6 +28,7 @@
#include "runtime.h"
#include "repl.h"
#include "gc.h"
+#include "gccollect.h"
#include "systick.h"
#include "led.h"
#include "servo.h"
@@ -47,8 +48,6 @@
int errno;
-extern uint32_t _heap_start;
-
static FATFS fatfs0;
void flash_error(int n) {
@@ -180,6 +179,7 @@ static mp_obj_t pyb_info(void) {
printf("_ebss=%p\n", &_ebss);
printf("_estack=%p\n", &_estack);
printf("_etext=%p\n", &_etext);
+ printf("_ram_start=%p\n", &_ram_start);
printf("_heap_start=%p\n", &_heap_start);
}
@@ -455,38 +455,6 @@ bool do_file(const char *filename) {
}
}
-#define RAM_START (0x20000000) // fixed for chip
-#define HEAP_END (0x2001c000) // tunable
-#define RAM_END (0x20020000) // fixed for chip
-
-void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end);
-
-void gc_collect(void) {
- uint32_t start = sys_tick_counter;
- gc_collect_start();
- gc_collect_root((void**)RAM_START, (((uint32_t)&_heap_start) - RAM_START) / 4);
- machine_uint_t regs[10];
- gc_helper_get_regs_and_clean_stack(regs, HEAP_END);
- gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack
- gc_collect_end();
- uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
-
- if (0) {
- // print GC info
- gc_info_t info;
- gc_info(&info);
- printf("GC@%lu %lums\n", start, ticks);
- printf(" %lu total\n", info.total);
- printf(" %lu : %lu\n", info.used, info.free);
- printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
- }
-}
-
-mp_obj_t pyb_gc(void) {
- gc_collect();
- return mp_const_none;
-}
-
mp_obj_t pyb_gpio(uint n_args, mp_obj_t *args) {
//assert(1 <= n_args && n_args <= 2);
@@ -655,7 +623,7 @@ soft_reset:
rt_store_attr(m, MP_QSTR_source_dir, rt_make_function_n(1, pyb_source_dir));
rt_store_attr(m, MP_QSTR_main, rt_make_function_n(1, pyb_main));
rt_store_attr(m, MP_QSTR_sync, rt_make_function_n(0, pyb_sync));
- rt_store_attr(m, MP_QSTR_gc, rt_make_function_n(0, pyb_gc));
+ rt_store_attr(m, MP_QSTR_gc, (mp_obj_t)&pyb_gc_obj);
rt_store_attr(m, MP_QSTR_delay, rt_make_function_n(1, pyb_delay));
#if MICROPY_HW_HAS_SWITCH
rt_store_attr(m, MP_QSTR_switch, (mp_obj_t)&pyb_switch_obj);
diff --git a/stm/stm32f405.ld b/stm/stm32f405.ld
index c19e6a1c19..fbfc584f9d 100644
--- a/stm/stm32f405.ld
+++ b/stm/stm32f405.ld
@@ -69,6 +69,7 @@ SECTIONS
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
+ _ram_start = .; /* create a global symbol at ram start for garbage collector */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
diff --git a/tests/basics/getitem.py b/tests/basics/getitem.py
new file mode 100644
index 0000000000..f39296aca3
--- /dev/null
+++ b/tests/basics/getitem.py
@@ -0,0 +1,22 @@
+# create a class that has a __getitem__ method
+class A:
+ def __getitem__(self, index):
+ print('getitem', index)
+ if index > 10:
+ raise StopIteration
+
+# test __getitem__
+A()[0]
+A()[1]
+
+# iterate using a for loop
+for i in A():
+ pass
+
+# iterate manually
+it = iter(A())
+try:
+ while True:
+ next(it)
+except StopIteration:
+ pass