diff options
-rw-r--r-- | py/emitcpy.c | 2 | ||||
-rw-r--r-- | py/obj.c | 9 | ||||
-rw-r--r-- | py/obj.h | 1 | ||||
-rw-r--r-- | py/objcomplex.c | 38 | ||||
-rw-r--r-- | py/objgenerator.c | 23 | ||||
-rw-r--r-- | py/objstr.c | 43 | ||||
-rw-r--r-- | py/qstrdefs.h | 1 | ||||
-rw-r--r-- | py/vm.c | 11 | ||||
-rw-r--r-- | tests/basics/generator-closure.py | 26 | ||||
-rw-r--r-- | tests/basics/string-join.py | 12 | ||||
-rw-r--r-- | tests/basics/string_index.py | 78 | ||||
-rw-r--r-- | tests/basics/string_rindex.py | 78 |
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)); @@ -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 { @@ -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) @@ -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") |