From a3dcd9e80ce8bf7abae3daa8da82f1b085499989 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 17 Dec 2013 21:35:38 +0000 Subject: [PATCH] py: add more Python built-in functions. --- py/builtin.c | 338 +++++++++++++++++++++++++++++++++++++++++++-------- py/builtin.h | 28 ++++- py/obj.c | 11 ++ py/obj.h | 1 + py/runtime.c | 59 ++++++--- py/runtime.h | 2 + 6 files changed, 363 insertions(+), 76 deletions(-) diff --git a/py/builtin.c b/py/builtin.c index 8b380b2310..e45cdc98bc 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -16,6 +16,35 @@ #include "objprivate.h" #include "builtin.h" +py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name) { + // we differ from CPython: we set the new __locals__ object here + py_map_t *old_locals = rt_get_map_locals(); + py_map_t *class_locals = py_map_new(MAP_QSTR, 0); + rt_set_map_locals(class_locals); + + // call the class code + rt_call_function_1(o_class_fun, (py_obj_t)0xdeadbeef); + + // restore old __locals__ object + rt_set_map_locals(old_locals); + + // create and return the new class + py_obj_base_t *o = m_new(py_obj_base_t, 1); + o->kind = O_CLASS; + o->u_class.locals = class_locals; + return o; +} + +py_obj_t py_builtin___import__(int n, py_obj_t *args) { + printf("import:\n"); + for (int i = 0; i < n; i++) { + printf(" "); + py_obj_print(args[i]); + printf("\n"); + } + return py_const_none; +} + py_obj_t py_builtin___repl_print__(py_obj_t o) { if (o != py_const_none) { py_obj_print(o); @@ -24,40 +53,6 @@ py_obj_t py_builtin___repl_print__(py_obj_t o) { return py_const_none; } -py_obj_t py_builtin_print(int n_args, const py_obj_t* args) { - for (int i = 0; i < n_args; i++) { - if (i > 0) { - printf(" "); - } - if (IS_O(args[i], O_STR)) { - // special case, print string raw - printf("%s", qstr_str(((py_obj_base_t*)args[i])->u_str)); - } else { - // print the object Python style - py_obj_print(args[i]); - } - } - printf("\n"); - return py_const_none; -} - -py_obj_t py_builtin_len(py_obj_t o_in) { - py_small_int_t len = 0; - if (IS_O(o_in, O_STR)) { - py_obj_base_t *o = o_in; - len = strlen(qstr_str(o->u_str)); - } else if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) { - py_obj_base_t *o = o_in; - len = o->u_tuple_list.len; - } else if (IS_O(o_in, O_MAP)) { - py_obj_base_t *o = o_in; - len = o->u_map.used; - } else { - assert(0); - } - return TO_SMALL_INT(len); -} - py_obj_t py_builtin_abs(py_obj_t o_in) { if (IS_SMALL_INT(o_in)) { py_small_int_t val = FROM_SMALL_INT(o_in); @@ -84,26 +79,250 @@ py_obj_t py_builtin_abs(py_obj_t o_in) { } } -py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name) { - // we differ from CPython: we set the new __locals__ object here - py_map_t *old_locals = rt_get_map_locals(); - py_map_t *class_locals = py_map_new(MAP_QSTR, 0); - rt_set_map_locals(class_locals); - - // call the class code - rt_call_function_1(o_class_fun, (py_obj_t)0xdeadbeef); - - // restore old __locals__ object - rt_set_map_locals(old_locals); - - // create and return the new class - py_obj_base_t *o = m_new(py_obj_base_t, 1); - o->kind = O_CLASS; - o->u_class.locals = class_locals; - return o; +py_obj_t py_builtin_all(py_obj_t o_in) { + py_obj_t iterable = rt_getiter(o_in); + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + if (!rt_is_true(item)) { + return py_const_false; + } + } + return py_const_true; } -py_obj_t py_builtin_range(int n_args, const py_obj_t* args) { +py_obj_t py_builtin_any(py_obj_t o_in) { + py_obj_t iterable = rt_getiter(o_in); + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + if (rt_is_true(item)) { + return py_const_true; + } + } + return py_const_false; +} + +py_obj_t py_builtin_bool(int n_args, const py_obj_t *args) { + switch (n_args) { + case 0: return py_const_false; + case 1: if (rt_is_true(args[0])) { return py_const_true; } else { return py_const_false; } + default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "bool() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args, NULL)); + } +} + +py_obj_t py_builtin_callable(py_obj_t o_in) { + if (py_obj_is_callable(o_in)) { + return py_const_true; + } else { + return py_const_false; + } +} + +#if MICROPY_ENABLE_FLOAT +py_obj_t py_builtin_complex(int n_args, const py_obj_t *args) { + switch (n_args) { + case 0: + return py_obj_new_complex(0, 0); + + case 1: + // TODO allow string as first arg + if (IS_O(args[0], O_COMPLEX)) { + return args[0]; + } else { + return py_obj_new_complex(py_obj_get_float(args[0]), 0); + } + + case 2: + { + py_float_t real, imag; + if (IS_O(args[0], O_COMPLEX)) { + py_obj_get_complex(args[0], &real, &imag); + } else { + real = py_obj_get_float(args[0]); + imag = 0; + } + if (IS_O(args[1], O_COMPLEX)) { + py_float_t real2, imag2; + py_obj_get_complex(args[1], &real2, &imag2); + real -= imag2; + imag += real2; + } else { + imag += py_obj_get_float(args[1]); + } + return py_obj_new_complex(real, imag); + } + + default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "comlpex() takes at most 2 arguments (%d given)", (void*)(machine_int_t)n_args, NULL)); + } +} +#endif + +py_obj_t py_builtin_chr(py_obj_t o_in) { + int ord = py_obj_get_int(o_in); + if (0 <= ord && ord <= 0x10ffff) { + char *str = m_new(char, 2); + str[0] = ord; + str[1] = '\0'; + return py_obj_new_str(qstr_from_str_take(str)); + } else { + nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "chr() arg not in range(0x110000)", NULL, NULL)); + } +} + +py_obj_t py_builtin_dict(void) { + // TODO create from an iterable! + return rt_build_map(0); +} + +py_obj_t py_builtin_divmod(py_obj_t o1_in, py_obj_t o2_in) { + if (IS_SMALL_INT(o1_in) && IS_SMALL_INT(o2_in)) { + py_small_int_t i1 = FROM_SMALL_INT(o1_in); + py_small_int_t i2 = FROM_SMALL_INT(o2_in); + py_obj_t revs_args[2]; + revs_args[1] = TO_SMALL_INT(i1 / i2); + revs_args[0] = TO_SMALL_INT(i1 % i2); + return rt_build_tuple(2, revs_args); + } else { + nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "unsupported operand type(s) for divmod(): '%s' and '%s'", py_obj_get_type_str(o1_in), py_obj_get_type_str(o2_in))); + } +} + +py_obj_t py_builtin_hash(py_obj_t o_in) { + // TODO hash will generally overflow small integer; can we safely truncate it? + return py_obj_new_int(py_obj_hash(o_in)); +} + +py_obj_t py_builtin_iter(py_obj_t o_in) { + return rt_getiter(o_in); +} + +py_obj_t py_builtin_next(py_obj_t o_in) { + return rt_gen_instance_next(o_in); +} + +py_obj_t py_builtin_len(py_obj_t o_in) { + py_small_int_t len = 0; + if (IS_O(o_in, O_STR)) { + py_obj_base_t *o = o_in; + len = strlen(qstr_str(o->u_str)); + } else if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) { + py_obj_base_t *o = o_in; + len = o->u_tuple_list.len; + } else if (IS_O(o_in, O_MAP)) { + py_obj_base_t *o = o_in; + len = o->u_map.used; + } else { + nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "object of type '%s' has no len()", py_obj_get_type_str(o_in), NULL)); + } + return TO_SMALL_INT(len); +} + +py_obj_t py_builtin_list(int n_args, const py_obj_t *args) { + switch (n_args) { + case 0: return rt_build_list(0, NULL); + case 1: + { + // make list from iterable + py_obj_t iterable = rt_getiter(args[0]); + py_obj_t list = rt_build_list(0, NULL); + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + rt_list_append(list, item); + } + return list; + } + default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "list() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args, NULL)); + } +} + +py_obj_t py_builtin_max(int n_args, const py_obj_t *args) { + if (n_args == 1) { + // given an iterable + py_obj_t iterable = rt_getiter(args[0]); + py_obj_t max_obj = NULL; + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + if (max_obj == NULL || py_obj_less(max_obj, item)) { + max_obj = item; + } + } + if (max_obj == NULL) { + nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "max() arg is an empty sequence", NULL, NULL)); + } + return max_obj; + } else { + // given many args + py_obj_t max_obj = args[0]; + for (int i = 1; i < n_args; i++) { + if (py_obj_less(max_obj, args[i])) { + max_obj = args[i]; + } + } + return max_obj; + } +} + +py_obj_t py_builtin_min(int n_args, const py_obj_t *args) { + if (n_args == 1) { + // given an iterable + py_obj_t iterable = rt_getiter(args[0]); + py_obj_t min_obj = NULL; + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + if (min_obj == NULL || py_obj_less(item, min_obj)) { + min_obj = item; + } + } + if (min_obj == NULL) { + nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "min() arg is an empty sequence", NULL, NULL)); + } + return min_obj; + } else { + // given many args + py_obj_t min_obj = args[0]; + for (int i = 1; i < n_args; i++) { + if (py_obj_less(args[i], min_obj)) { + min_obj = args[i]; + } + } + return min_obj; + } +} + +py_obj_t py_builtin_ord(py_obj_t o_in) { + const char *str = qstr_str(py_obj_get_qstr(o_in)); + if (strlen(str) == 1) { + return py_obj_new_int(str[0]); + } else { + nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "ord() expected a character, but string of length %d found", (void*)(machine_int_t)strlen(str), NULL)); + } +} + +py_obj_t py_builtin_pow(int n_args, const py_obj_t *args) { + switch (n_args) { + case 2: return rt_binary_op(RT_BINARY_OP_POWER, args[0], args[1]); + case 3: return rt_binary_op(RT_BINARY_OP_MODULO, rt_binary_op(RT_BINARY_OP_POWER, args[0], args[1]), args[2]); // TODO optimise... + default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "pow expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args, NULL)); + } +} + +py_obj_t py_builtin_print(int n_args, const py_obj_t *args) { + for (int i = 0; i < n_args; i++) { + if (i > 0) { + printf(" "); + } + if (IS_O(args[i], O_STR)) { + // special case, print string raw + printf("%s", qstr_str(((py_obj_base_t*)args[i])->u_str)); + } else { + // print the object Python style + py_obj_print(args[i]); + } + } + printf("\n"); + return py_const_none; +} + +py_obj_t py_builtin_range(int n_args, const py_obj_t *args) { switch (n_args) { case 1: return py_obj_new_range(0, py_obj_get_int(args[0]), 1); case 2: return py_obj_new_range(py_obj_get_int(args[0]), py_obj_get_int(args[1]), 1); @@ -111,3 +330,18 @@ py_obj_t py_builtin_range(int n_args, const py_obj_t* args) { default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "range expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args, NULL)); } } + +py_obj_t py_builtin_sum(int n_args, const py_obj_t *args) { + py_obj_t value; + switch (n_args) { + case 1: value = py_obj_new_int(0); break; + case 2: value = args[1]; break; + default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "sum expected at most 2 arguments, got %d", (void*)(machine_int_t)n_args, NULL)); + } + py_obj_t iterable = rt_getiter(args[0]); + py_obj_t item; + while ((item = rt_iternext(iterable)) != py_const_stop_iteration) { + value = rt_binary_op(RT_BINARY_OP_ADD, value, item); + } + return value; +} diff --git a/py/builtin.h b/py/builtin.h index 6ca1752bda..0a0470a842 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -1,6 +1,24 @@ -py_obj_t py_builtin___repl_print__(py_obj_t o); -py_obj_t py_builtin_print(int n_args, const py_obj_t* args); -py_obj_t py_builtin_len(py_obj_t o_in); -py_obj_t py_builtin_abs(py_obj_t o_in); py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name); -py_obj_t py_builtin_range(int n_args, const py_obj_t* args); +py_obj_t py_builtin___import__(int n, py_obj_t *args); +py_obj_t py_builtin___repl_print__(py_obj_t o); +py_obj_t py_builtin_abs(py_obj_t o_in); +py_obj_t py_builtin_all(py_obj_t o_in); +py_obj_t py_builtin_any(py_obj_t o_in); +py_obj_t py_builtin_bool(int n_args, const py_obj_t *args); +py_obj_t py_builtin_callable(py_obj_t o_in); +py_obj_t py_builtin_complex(int n_args, const py_obj_t *args); +py_obj_t py_builtin_chr(py_obj_t o_in); +py_obj_t py_builtin_dict(void); +py_obj_t py_builtin_divmod(py_obj_t o1_in, py_obj_t o2_in); +py_obj_t py_builtin_hash(py_obj_t o_in); +py_obj_t py_builtin_iter(py_obj_t o_in); +py_obj_t py_builtin_len(py_obj_t o_in); +py_obj_t py_builtin_list(int n_args, const py_obj_t *args); +py_obj_t py_builtin_max(int n_args, const py_obj_t *args); +py_obj_t py_builtin_min(int n_args, const py_obj_t *args); +py_obj_t py_builtin_next(py_obj_t o_in); +py_obj_t py_builtin_ord(py_obj_t o_in); +py_obj_t py_builtin_pow(int n_args, const py_obj_t *args); +py_obj_t py_builtin_print(int n_args, const py_obj_t *args); +py_obj_t py_builtin_range(int n_args, const py_obj_t *args); +py_obj_t py_builtin_sum(int n_args, const py_obj_t *args); diff --git a/py/obj.c b/py/obj.c index d678f41146..0699d4cde7 100644 --- a/py/obj.c +++ b/py/obj.c @@ -358,6 +358,17 @@ bool py_obj_equal(py_obj_t o1, py_obj_t o2) { } } +bool py_obj_less(py_obj_t o1, py_obj_t o2) { + if (IS_SMALL_INT(o1) && IS_SMALL_INT(o2)) { + py_small_int_t i1 = FROM_SMALL_INT(o1); + py_small_int_t i2 = FROM_SMALL_INT(o2); + return i1 < i2; + } else { + assert(0); + return false; + } +} + machine_int_t py_obj_get_int(py_obj_t arg) { if (arg == py_const_false) { return 0; diff --git a/py/obj.h b/py/obj.h index cbb885503e..746c92bbad 100644 --- a/py/obj.h +++ b/py/obj.h @@ -39,6 +39,7 @@ void py_obj_print(py_obj_t o); bool py_obj_is_callable(py_obj_t o_in); machine_int_t py_obj_hash(py_obj_t o_in); bool py_obj_equal(py_obj_t o1, py_obj_t o2); +bool py_obj_less(py_obj_t o1, py_obj_t o2); machine_int_t py_obj_get_int(py_obj_t arg); #if MICROPY_ENABLE_FLOAT diff --git a/py/runtime.c b/py/runtime.c index f1938d6ec8..35f4f2af29 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -128,7 +128,7 @@ bad_arg: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "?str.join expecting a list of str's", NULL, NULL)); } -py_obj_t rt_str_format(int n_args, const py_obj_t* args) { +py_obj_t rt_str_format(int n_args, const py_obj_t *args) { assert(IS_O(args[0], O_STR)); py_obj_base_t *self = args[0]; @@ -302,12 +302,29 @@ void rt_init(void) { py_qstr_map_lookup(map_globals, qstr_from_str_static("__name__"), true)->value = py_obj_new_str(qstr_from_str_static("__main__")); py_map_init(&map_builtins, MAP_QSTR, 3); - py_qstr_map_lookup(&map_builtins, qstr_from_str_static("__repl_print__"), true)->value = rt_make_function_1(py_builtin___repl_print__); - py_qstr_map_lookup(&map_builtins, qstr_from_str_static("print"), true)->value = rt_make_function_var(0, py_builtin_print); - py_qstr_map_lookup(&map_builtins, qstr_from_str_static("len"), true)->value = rt_make_function_1(py_builtin_len); - py_qstr_map_lookup(&map_builtins, qstr_from_str_static("abs"), true)->value = rt_make_function_1(py_builtin_abs); py_qstr_map_lookup(&map_builtins, rt_q___build_class__, true)->value = rt_make_function_2(py_builtin___build_class__); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("__repl_print__"), true)->value = rt_make_function_1(py_builtin___repl_print__); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("abs"), true)->value = rt_make_function_1(py_builtin_abs); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("all"), true)->value = rt_make_function_1(py_builtin_all); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("any"), true)->value = rt_make_function_1(py_builtin_any); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("bool"), true)->value = rt_make_function_var(0, py_builtin_bool); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("callable"), true)->value = rt_make_function_1(py_builtin_callable); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("complex"), true)->value = rt_make_function_var(0, py_builtin_complex); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("chr"), true)->value = rt_make_function_1(py_builtin_chr); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("dict"), true)->value = rt_make_function_0(py_builtin_dict); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("divmod"), true)->value = rt_make_function_2(py_builtin_divmod); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("hash"), true)->value = rt_make_function_1(py_builtin_hash); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("iter"), true)->value = rt_make_function_1(py_builtin_iter); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("len"), true)->value = rt_make_function_1(py_builtin_len); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("list"), true)->value = rt_make_function_var(0, py_builtin_list); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("max"), true)->value = rt_make_function_var(1, py_builtin_max); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("min"), true)->value = rt_make_function_var(1, py_builtin_min); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("next"), true)->value = rt_make_function_1(py_builtin_next); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("ord"), true)->value = rt_make_function_1(py_builtin_ord); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("pow"), true)->value = rt_make_function_var(2, py_builtin_pow); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("print"), true)->value = rt_make_function_var(0, py_builtin_print); py_qstr_map_lookup(&map_builtins, qstr_from_str_static("range"), true)->value = rt_make_function_var(1, py_builtin_range); + py_qstr_map_lookup(&map_builtins, qstr_from_str_static("sum"), true)->value = rt_make_function_var(1, py_builtin_sum); next_unique_code_id = 2; // 1 is reserved for the __main__ module scope unique_codes = NULL; @@ -676,13 +693,27 @@ py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs) { case RT_BINARY_OP_TRUE_DIVIDE: case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return py_obj_new_float((py_float_t)lhs_val / (py_float_t)rhs_val); #endif + + // TODO implement modulo as specified by Python + case RT_BINARY_OP_MODULO: + case RT_BINARY_OP_INPLACE_MODULO: lhs_val %= rhs_val; break; + + // TODO check for negative power, and overflow case RT_BINARY_OP_POWER: case RT_BINARY_OP_INPLACE_POWER: - // TODO - if (rhs_val == 2) { - lhs_val = lhs_val * lhs_val; - break; + { + int ans = 1; + while (rhs_val > 0) { + if (rhs_val & 1) { + ans *= lhs_val; + } + lhs_val *= lhs_val; + rhs_val /= 2; } + lhs_val = ans; + break; + } + default: printf("%d\n", op); assert(0); } if (fit_small_int(lhs_val)) { @@ -1456,16 +1487,6 @@ py_obj_t rt_iternext(py_obj_t o_in) { } } -py_obj_t py_builtin___import__(int n, py_obj_t *args) { - printf("import:\n"); - for (int i = 0; i < n; i++) { - printf(" "); - py_obj_print(args[i]); - printf("\n"); - } - return py_const_none; -} - py_obj_t rt_import_name(qstr name, py_obj_t fromlist, py_obj_t level) { // build args array py_obj_t args[5]; diff --git a/py/runtime.h b/py/runtime.h index c4cd094389..d5d160535f 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -157,3 +157,5 @@ py_obj_t rt_getiter(py_obj_t o); py_obj_t rt_iternext(py_obj_t o); py_obj_t rt_import_name(qstr name, py_obj_t fromlist, py_obj_t level); py_obj_t rt_import_from(py_obj_t module, qstr name); + +py_obj_t rt_gen_instance_next(py_obj_t self_in);