summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/emitcpy.c2
-rw-r--r--py/obj.c9
-rw-r--r--py/obj.h1
-rw-r--r--py/objcomplex.c38
-rw-r--r--py/objgenerator.c23
-rw-r--r--py/objstr.c43
-rw-r--r--py/qstrdefs.h1
-rw-r--r--py/vm.c11
-rw-r--r--tests/basics/generator-closure.py26
-rw-r--r--tests/basics/string-join.py12
-rw-r--r--tests/basics/string_index.py78
-rw-r--r--tests/basics/string_rindex.py78
12 files changed, 281 insertions, 41 deletions
diff --git a/py/emitcpy.c b/py/emitcpy.c
index fe40c4145e..8c608d6a2b 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -230,7 +230,7 @@ STATIC void emit_cpy_load_const_verbatim_str(emit_t *emit, const char *str) {
}
}
-STATIC void emit_cpy_load_fast(emit_t *emit, qstr qstr, int local_num) {
+STATIC void emit_cpy_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
emit_pre(emit, 1, 3);
if (emit->pass == PASS_3) {
printf("LOAD_FAST %d %s\n", local_num, qstr_str(qstr));
diff --git a/py/obj.c b/py/obj.c
index d1db53690f..844ec41216 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -99,6 +99,11 @@ int mp_obj_is_true(mp_obj_t arg) {
}
}
+// returns true if o_in is bool, small int, or long int
+bool mp_obj_is_integer(mp_obj_t o_in) {
+ return MP_OBJ_IS_INT(o_in) || MP_OBJ_IS_TYPE(o_in, &mp_type_bool);
+}
+
bool mp_obj_is_callable(mp_obj_t o_in) {
return mp_obj_get_type(o_in)->call != NULL;
}
@@ -285,8 +290,8 @@ void mp_obj_get_array_fixed_n(mp_obj_t o, uint len, mp_obj_t **items) {
// is_slice determines whether the index is a slice index
uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index, bool is_slice) {
int i;
- if (MP_OBJ_IS_SMALL_INT(index)) {
- i = MP_OBJ_SMALL_INT_VALUE(index);
+ if (MP_OBJ_IS_INT(index)) {
+ i = mp_obj_int_get_checked(index);
} else if (MP_OBJ_IS_TYPE(index, &mp_type_bool)) {
i = (index == mp_const_true ? 1 : 0);
} else {
diff --git a/py/obj.h b/py/obj.h
index ab1685d4d0..b944d2fcb5 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -369,6 +369,7 @@ void mp_obj_print(mp_obj_t o, mp_print_kind_t kind);
void mp_obj_print_exception(mp_obj_t exc);
int mp_obj_is_true(mp_obj_t arg);
+bool mp_obj_is_integer(mp_obj_t o_in); // returns true if o_in is bool, small int, or long int
bool mp_obj_is_callable(mp_obj_t o_in);
machine_int_t mp_obj_hash(mp_obj_t o_in);
bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2);
diff --git a/py/objcomplex.c b/py/objcomplex.c
index 769977ad80..66f971da0e 100644
--- a/py/objcomplex.c
+++ b/py/objcomplex.c
@@ -144,22 +144,40 @@ mp_obj_t mp_obj_complex_binary_op(int op, mp_float_t lhs_real, mp_float_t lhs_im
lhs_imag -= rhs_imag;
break;
case MP_BINARY_OP_MULTIPLY:
- case MP_BINARY_OP_INPLACE_MULTIPLY:
- {
- mp_float_t real = lhs_real * rhs_real - lhs_imag * rhs_imag;
+ case MP_BINARY_OP_INPLACE_MULTIPLY: {
+ mp_float_t real;
+ multiply:
+ real = lhs_real * rhs_real - lhs_imag * rhs_imag;
lhs_imag = lhs_real * rhs_imag + lhs_imag * rhs_real;
lhs_real = real;
break;
}
- /* TODO floor(?) the value
case MP_BINARY_OP_FLOOR_DIVIDE:
- case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break;
- */
- /* TODO
+ case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "can't do truncated division of a complex number"));
+
case MP_BINARY_OP_TRUE_DIVIDE:
- case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: val = lhs_val / rhs_val; break;
- */
- return NULL; // op not supported
+ case MP_BINARY_OP_INPLACE_TRUE_DIVIDE:
+ if (rhs_imag == 0) {
+ if (rhs_real == 0) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "complex division by zero"));
+ }
+ lhs_real /= rhs_real;
+ lhs_imag /= rhs_real;
+ } else if (rhs_real == 0) {
+ mp_float_t real = lhs_imag / rhs_imag;
+ lhs_imag = -lhs_real / rhs_imag;
+ lhs_real = real;
+ } else {
+ mp_float_t rhs_len_sq = rhs_real*rhs_real + rhs_imag*rhs_imag;
+ rhs_real /= rhs_len_sq;
+ rhs_imag /= -rhs_len_sq;
+ goto multiply;
+ }
+ break;
+
+ default:
+ return MP_OBJ_NULL; // op not supported
}
return mp_obj_new_complex(lhs_real, lhs_imag);
}
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 975681af88..6468aa20db 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -236,17 +236,10 @@ mp_obj_t mp_obj_new_gen_instance(const byte *bytecode, uint n_args, const mp_obj
machine_uint_t n_exc_stack = bytecode[2] | (bytecode[3] << 8);
bytecode += 4;
- // bytecode prelude: initialise closed over variables
- // TODO
- // for now we just make sure there are no cells variables
- // need to work out how to implement closed over variables in generators
- assert(bytecode[0] == 0);
- bytecode += 1;
-
+ // allocate the generator object, with room for local stack and exception stack
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));
o->base.type = &mp_type_gen_instance;
o->code_info = code_info;
- o->ip = bytecode;
o->sp = &o->state[0] - 1; // sp points to top of stack, which starts off 1 below the state
o->exc_sp = (mp_exc_stack_t*)(o->state + n_state) - 1;
o->n_state = n_state;
@@ -259,5 +252,19 @@ mp_obj_t mp_obj_new_gen_instance(const byte *bytecode, uint n_args, const mp_obj
o->state[n_state - 1 - n_args - i] = args2[i];
}
+ // set rest of state to MP_OBJ_NULL
+ for (uint i = 0; i < n_state - n_args - n_args2; i++) {
+ o->state[i] = MP_OBJ_NULL;
+ }
+
+ // bytecode prelude: initialise closed over variables
+ for (uint n_local = *bytecode++; n_local > 0; n_local--) {
+ uint local_num = *bytecode++;
+ o->state[n_state - 1 - local_num] = mp_obj_new_cell(o->state[n_state - 1 - local_num]);
+ }
+
+ // set ip to start of actual byte code
+ o->ip = bytecode;
+
return o;
}
diff --git a/py/objstr.c b/py/objstr.c
index 5e9f0c76b6..519ff464d6 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -221,9 +221,7 @@ STATIC mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
GET_STR_DATA_LEN(lhs_in, lhs_data, lhs_len);
switch (op) {
case MP_BINARY_OP_SUBSCR:
- // TODO: need predicate to check for int-like type (bools are such for example)
- // ["no", "yes"][1 == 2] is common idiom
- if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
+ if (mp_obj_is_integer(rhs_in)) {
uint index = mp_get_index(mp_obj_get_type(lhs_in), lhs_len, rhs_in, false);
if (MP_OBJ_IS_TYPE(lhs_in, &mp_type_bytes)) {
return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)lhs_data[index]);
@@ -329,17 +327,19 @@ STATIC mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_t *seq_items;
if (MP_OBJ_IS_TYPE(arg, &mp_type_tuple)) {
mp_obj_tuple_get(arg, &seq_len, &seq_items);
- } else if (MP_OBJ_IS_TYPE(arg, &mp_type_list)) {
- mp_obj_list_get(arg, &seq_len, &seq_items);
} else {
- goto bad_arg;
+ if (!MP_OBJ_IS_TYPE(arg, &mp_type_list)) {
+ // arg is not a list, try to convert it to one
+ arg = mp_type_list.make_new((mp_obj_t)&mp_type_list, 1, 0, &arg);
+ }
+ mp_obj_list_get(arg, &seq_len, &seq_items);
}
// count required length
int required_len = 0;
for (int i = 0; i < seq_len; i++) {
if (!MP_OBJ_IS_STR(seq_items[i])) {
- goto bad_arg;
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "join expected a list of str's"));
}
if (i > 0) {
required_len += sep_len;
@@ -363,9 +363,6 @@ STATIC mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
// return joined string
return mp_obj_str_builder_end(joined_str);
-
-bad_arg:
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "?str.join expecting a list of str's"));
}
#define is_ws(c) ((c) == ' ' || (c) == '\t')
@@ -441,7 +438,7 @@ STATIC mp_obj_t str_split(uint n_args, const mp_obj_t *args) {
return res;
}
-STATIC mp_obj_t str_finder(uint n_args, const mp_obj_t *args, machine_int_t direction) {
+STATIC mp_obj_t str_finder(uint n_args, const mp_obj_t *args, machine_int_t direction, bool is_index) {
assert(2 <= n_args && n_args <= 4);
assert(MP_OBJ_IS_STR(args[0]));
assert(MP_OBJ_IS_STR(args[1]));
@@ -461,7 +458,11 @@ STATIC mp_obj_t str_finder(uint n_args, const mp_obj_t *args, machine_int_t dire
const byte *p = find_subbytes(haystack + start, end - start, needle, needle_len, direction);
if (p == NULL) {
// not found
- return MP_OBJ_NEW_SMALL_INT(-1);
+ if (is_index) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "substring not found"));
+ } else {
+ return MP_OBJ_NEW_SMALL_INT(-1);
+ }
} else {
// found
return MP_OBJ_NEW_SMALL_INT(p - haystack);
@@ -469,11 +470,19 @@ STATIC mp_obj_t str_finder(uint n_args, const mp_obj_t *args, machine_int_t dire
}
STATIC mp_obj_t str_find(uint n_args, const mp_obj_t *args) {
- return str_finder(n_args, args, 1);
+ return str_finder(n_args, args, 1, false);
}
STATIC mp_obj_t str_rfind(uint n_args, const mp_obj_t *args) {
- return str_finder(n_args, args, -1);
+ return str_finder(n_args, args, -1, false);
+}
+
+STATIC mp_obj_t str_index(uint n_args, const mp_obj_t *args) {
+ return str_finder(n_args, args, 1, true);
+}
+
+STATIC mp_obj_t str_rindex(uint n_args, const mp_obj_t *args) {
+ return str_finder(n_args, args, -1, true);
}
// TODO: (Much) more variety in args
@@ -1128,7 +1137,7 @@ STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
}
}
- // if max_rep is still 0 by this point we will need to do all possible replacements
+ // if max_rep is still -1 by this point we will need to do all possible replacements
// check argument types
@@ -1307,6 +1316,8 @@ STATIC machine_int_t str_get_buffer(mp_obj_t self_in, buffer_info_t *bufinfo, in
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj, 2, 4, str_find);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj, 2, 4, str_rfind);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_index_obj, 2, 4, str_index);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rindex_obj, 2, 4, str_rindex);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj, 1, 3, str_split);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(str_startswith_obj, str_startswith);
@@ -1320,6 +1331,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(str_rpartition_obj, str_rpartition);
STATIC const mp_map_elem_t str_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_find), (mp_obj_t)&str_find_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_rfind), (mp_obj_t)&str_rfind_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_index), (mp_obj_t)&str_index_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_rindex), (mp_obj_t)&str_rindex_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_join), (mp_obj_t)&str_join_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_split), (mp_obj_t)&str_split_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_startswith), (mp_obj_t)&str_startswith_obj },
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 0c2c032f29..342160bbc7 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -183,6 +183,7 @@ Q(union)
Q(update)
Q(find)
Q(rfind)
+Q(rindex)
Q(split)
Q(startswith)
Q(replace)
diff --git a/py/vm.c b/py/vm.c
index f8b60acd22..b9147d1d32 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -95,14 +95,15 @@ mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args,
state[n_state - 1 - n_args - i] = args2[i];
}
+ // set rest of state to MP_OBJ_NULL
+ for (uint i = 0; i < n_state - n_args - n_args2; i++) {
+ state[i] = MP_OBJ_NULL;
+ }
+
// bytecode prelude: initialise closed over variables
for (uint n_local = *ip++; n_local > 0; n_local--) {
uint local_num = *ip++;
- if (local_num < n_args + n_args2) {
- state[n_state - 1 - local_num] = mp_obj_new_cell(state[n_state - 1 - local_num]);
- } else {
- state[n_state - 1 - local_num] = mp_obj_new_cell(MP_OBJ_NULL);
- }
+ state[n_state - 1 - local_num] = mp_obj_new_cell(state[n_state - 1 - local_num]);
}
// execute the byte code
diff --git a/tests/basics/generator-closure.py b/tests/basics/generator-closure.py
new file mode 100644
index 0000000000..d8a517edeb
--- /dev/null
+++ b/tests/basics/generator-closure.py
@@ -0,0 +1,26 @@
+# a generator that closes over outer variables
+def f():
+ x = 1 # closed over by g
+ def g():
+ yield x
+ yield x + 1
+ return g()
+for i in f():
+ print(i)
+
+# a generator that has its variables closed over
+def f():
+ x = 1 # closed over by g
+ def g():
+ return x + 1
+ yield g()
+ x = 2
+ yield g()
+for i in f():
+ print(i)
+
+# using comprehensions, the inner generator closes over y
+generator_of_generators = (((x, y) for x in range(2)) for y in range(3))
+for i in generator_of_generators:
+ for j in i:
+ print(j)
diff --git a/tests/basics/string-join.py b/tests/basics/string-join.py
new file mode 100644
index 0000000000..275a804c64
--- /dev/null
+++ b/tests/basics/string-join.py
@@ -0,0 +1,12 @@
+print(','.join(()))
+print(','.join(('a',)))
+print(','.join(('a', 'b')))
+
+print(','.join([]))
+print(','.join(['a']))
+print(','.join(['a', 'b']))
+
+print(''.join(''))
+print(''.join('abc'))
+print(','.join('abc'))
+print(','.join('abc' for i in range(5)))
diff --git a/tests/basics/string_index.py b/tests/basics/string_index.py
new file mode 100644
index 0000000000..31f6900e6c
--- /dev/null
+++ b/tests/basics/string_index.py
@@ -0,0 +1,78 @@
+print("hello world".index("ll"))
+print("hello world".index("ll", None))
+print("hello world".index("ll", 1))
+print("hello world".index("ll", 1, None))
+print("hello world".index("ll", None, None))
+print("hello world".index("ll", 1, -1))
+
+try:
+ print("hello world".index("ll", 1, 1))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("hello world".index("ll", 1, 2))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("hello world".index("ll", 1, 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+print("hello world".index("ll", 1, 4))
+print("hello world".index("ll", 1, 5))
+print("hello world".index("ll", -100))
+print("0000".index('0'))
+print("0000".index('0', 0))
+print("0000".index('0', 1))
+print("0000".index('0', 2))
+print("0000".index('0', 3))
+
+try:
+ print("0000".index('0', 4))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".index('0', 5))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".index('-1', 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".index('1', 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".index('1', 4))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".index('1', 5))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
diff --git a/tests/basics/string_rindex.py b/tests/basics/string_rindex.py
new file mode 100644
index 0000000000..25acd37f6f
--- /dev/null
+++ b/tests/basics/string_rindex.py
@@ -0,0 +1,78 @@
+print("hello world".rindex("ll"))
+print("hello world".rindex("ll", None))
+print("hello world".rindex("ll", 1))
+print("hello world".rindex("ll", 1, None))
+print("hello world".rindex("ll", None, None))
+print("hello world".rindex("ll", 1, -1))
+
+try:
+ print("hello world".rindex("ll", 1, 1))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("hello world".rindex("ll", 1, 2))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("hello world".rindex("ll", 1, 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+print("hello world".rindex("ll", 1, 4))
+print("hello world".rindex("ll", 1, 5))
+print("hello world".rindex("ll", -100))
+print("0000".rindex('0'))
+print("0000".rindex('0', 0))
+print("0000".rindex('0', 1))
+print("0000".rindex('0', 2))
+print("0000".rindex('0', 3))
+
+try:
+ print("0000".rindex('0', 4))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".rindex('0', 5))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".rindex('-1', 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".rindex('1', 3))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".rindex('1', 4))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")
+
+try:
+ print("0000".rindex('1', 5))
+except ValueError:
+ print("Raised ValueError")
+else:
+ print("Did not raise ValueError")