From 42d29c79794941451a770e9d933a540030f6f261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jul 2019 20:54:49 +0200 Subject: [PATCH] added specialclass and keywordfunction --- .gitignore | 1 + .gitignore~ | 1 + docs/developing_micropython.ipynb | 142 +++++++++++++++++++++++++++++- keywordfunction/keywordfunction.c | 37 ++++++++ keywordfunction/micropython.mk | 8 ++ keywordfunction/micropython.mk~ | 8 ++ simplefunction/simplefunction.c | 14 +-- specialclass/micropython.mk | 8 ++ specialclass/specialclass.c | 88 ++++++++++++++++++ 9 files changed, 291 insertions(+), 16 deletions(-) create mode 100644 .gitignore create mode 100644 .gitignore~ create mode 100644 keywordfunction/keywordfunction.c create mode 100644 keywordfunction/micropython.mk create mode 100644 keywordfunction/micropython.mk~ create mode 100644 specialclass/micropython.mk create mode 100644 specialclass/specialclass.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccef25d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +docs/.ipynb_checkpoints/ \ No newline at end of file diff --git a/.gitignore~ b/.gitignore~ new file mode 100644 index 0000000..3d95d17 --- /dev/null +++ b/.gitignore~ @@ -0,0 +1 @@ +docs/.ipython_checkpoints/ \ No newline at end of file diff --git a/docs/developing_micropython.ipynb b/docs/developing_micropython.ipynb index 618c024..8d6aae8 100644 --- a/docs/developing_micropython.ipynb +++ b/docs/developing_micropython.ipynb @@ -83,6 +83,37 @@ "at least two are of particular interest. Namely, `/py/`, where the python interpreter is implemented, and `/ports/`, which contains the hardware-specific files. " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Constants\n", + "\n", + "In the implementation of micropython, everything revolves around objects of type `mp_obj_t`, which is defined in `obj.h`. Basically, `mp_obj_t` is nothing but an 8-byte segment of the memory, where all concrete objects are encoded. There can be various encodings, but the encoding is well-defined for a particular port. E.g., in the `A` encoding, integers are those objects, whose first bit in this 8-byte representation is set to 1, and the value of the integer can then be retrieved by shifting the 8-byte object by one to the right. Fortunately, we do not have to be concerned with the representations and the shifts, because there are pre-defined macros for such operations. So, if we want to find out, whether our object is an integer, we can inspect the value of the Boolean \n", + "\n", + "```c\n", + "MP_OBJ_IS_SMALL_INT(myobject)\n", + "```\n", + "We can get the integer value stored in `myobject` by calling \n", + "\n", + "```c\n", + "int a = MP_OBJ_SMALL_INT_VALUE(myobject);\n", + "```\n", + "Conversely, an integer in C can be turned into an object of type `mp_obj_t` by calling \n", + "\n", + "```c\n", + "\n", + "```\n", + "\n", + "So, if you want to find out, whether a particular object is a tuple, you could apply the `MP_OBJ_IS_TYPE` macro, \n", + "\n", + "```c\n", + "MP_OBJ_IS_TYPE(myobject, &mp_type_tuple)\n", + "```\n", + "\n", + "which returns a Boolean. " + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -604,7 +635,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Before we can actually add the `+` operator to the class, we should note that there are two kinds of special methods (TODO: THERE ARE MORE, THIS HAS TO BE EXPLAINED A BIT), namely the unary and the binary operators. \n", + "Before we can actually add the `+` operator to the class, we should note that there are two kinds of special methods, namely the unary and the binary operators. \n", "\n", "In the first group are those, whose sole argument is the class instance itself. Two frequently used cases are the length operator, `len`, and `bool`. So, e.g., if your class implements the `__len__(self)` method, and the method returns an integer, then you can call the `len` function in the console\n", "\n", @@ -612,8 +643,113 @@ "len(myclass)\n", "```\n", "\n", - "In the second category of operators are those, which require a left, as well as a right hand side. An example for this was the `__add__` method in our `Adder` class.\n", - "\n" + "In the second category of operators are those, which require a left, as well as a right hand side. An example for this was the `__add__` method in our `Adder` class. The complete list of unary, as well as binary operators can be found in `runtime.h`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The module below implements five special methods altogether. Two unary, namely, `bool`, and `len`, and three binary operators, `==`, `+`, and `*`. Since the addition and multiplication will return a new instance of `myclass`, we define a new function, `create_new_class`, that, well, creates a new instance of `myclass`, and initialises the members with the two input arguments. This function will also be called in the class initialisation function, `myclass_make_new` immediately after the argument checking. \n", + "\n", + "When implementing the operators, we have to keep a couple of things in mind. First, the `specialclass_myclass_type` has to be extended with the two methods, `.unary_op`, and `.binary_op`, where `.unary_op` is equal to the function that handles the unary operation (`specialclass_unary_op` in the example below), and `.binary_op` is equal to the function that deals with binary operations (`specialclass_binary_op` below)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```c\n", + "#include \"py/runtime.h\"\n", + "#include \"py/obj.h\"\n", + "#include \"py/binary.h\"\n", + "\n", + "typedef struct _specialclass_myclass_obj_t {\n", + "\tmp_obj_base_t base;\n", + "\tint16_t a;\n", + "\tint16_t b;\n", + "} specialclass_myclass_obj_t;\n", + "\n", + "const mp_obj_type_t specialclass_myclass_type;\n", + "\n", + "STATIC void myclass_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {\n", + "\t(void)kind;\n", + "\tspecialclass_myclass_obj_t *self = MP_OBJ_TO_PTR(self_in);\n", + "\tmp_print_str(print, \"myclass(\");\n", + "\tprintf(\"%d, \", self->a);\n", + "\tprintf(\"%d)\", self->b);\n", + "}\n", + "\n", + "mp_obj_t create_new_myclass(uint16_t a, uint16_t b) {\n", + "\tspecialclass_myclass_obj_t *out = m_new_obj(specialclass_myclass_obj_t);\n", + "\tout->base.type = &specialclass_myclass_type;\n", + "\tout->a = a;\n", + "\tout->b = b;\n", + "\treturn MP_OBJ_FROM_PTR(out);\n", + "}\n", + "\n", + "STATIC mp_obj_t myclass_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {\n", + "\tmp_arg_check_num(n_args, n_kw, 2, 2, true);\n", + "\treturn create_new_myclass(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]));\n", + "}\n", + "\n", + "STATIC const mp_rom_map_elem_t myclass_locals_dict_table[] = {\n", + "};\n", + "\n", + "STATIC MP_DEFINE_CONST_DICT(myclass_locals_dict, myclass_locals_dict_table);\n", + "\n", + "STATIC mp_obj_t specialclass_unary_op(mp_unary_op_t op, mp_obj_t self_in) {\n", + "\tspecialclass_myclass_obj_t *self = MP_OBJ_TO_PTR(self_in);\n", + " switch (op) {\n", + " case MP_UNARY_OP_BOOL: return mp_obj_new_bool((self->a > 0) && (self->b > 0));\n", + " case MP_UNARY_OP_LEN: return mp_obj_new_int(2);\n", + " default: return MP_OBJ_NULL; // op not supported\n", + " }\n", + "}\n", + "\n", + "STATIC mp_obj_t specialclass_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {\n", + " specialclass_myclass_obj_t *left_hand_side = MP_OBJ_TO_PTR(lhs);\n", + "\tspecialclass_myclass_obj_t *right_hand_size = MP_OBJ_TO_PTR(rhs);\n", + "\tswitch (op) {\n", + "\t\tcase MP_BINARY_OP_EQUAL:\n", + "\t\t\treturn mp_obj_new_bool((left_hand_side->a == right_hand_size->a) && (left_hand_side->b == right_hand_size->b));\n", + "\t\tcase MP_BINARY_OP_ADD:\n", + "\t\t\treturn create_new_myclass(left_hand_side->a + right_hand_size->a, left_hand_side->b + right_hand_size->b);\n", + "\t\tcase MP_BINARY_OP_MULTIPLY:\n", + "\t\t\treturn create_new_myclass(left_hand_side->a * right_hand_size->a, left_hand_side->b * right_hand_size->b);\n", + "\t\tdefault:\n", + "\t\t\treturn MP_OBJ_NULL; // op not supported\n", + "\t}\n", + "}\n", + "\n", + "const mp_obj_type_t specialclass_myclass_type = {\n", + "\t{ &mp_type_type },\n", + "\t.name = MP_QSTR_specialclass,\n", + "\t.print = myclass_print,\n", + "\t.make_new = myclass_make_new,\n", + "\t.unary_op = specialclass_unary_op, \n", + "\t.binary_op = specialclass_binary_op,\n", + "\t.locals_dict = (mp_obj_dict_t*)&myclass_locals_dict,\n", + "};\n", + "\n", + "STATIC const mp_map_elem_t specialclass_globals_table[] = {\n", + "\t{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_specialclass) },\n", + "\t{ MP_OBJ_NEW_QSTR(MP_QSTR_myclass), (mp_obj_t)&specialclass_myclass_type },\t\n", + "};\n", + "\n", + "STATIC MP_DEFINE_CONST_DICT (\n", + "\tmp_module_specialclass_globals,\n", + "\tspecialclass_globals_table\n", + ");\n", + "\n", + "const mp_obj_module_t specialclass_user_cmodule = {\t\n", + "\t.base = { &mp_type_module },\n", + "\t.globals = (mp_obj_dict_t*)&mp_module_specialclass_globals,\n", + "};\n", + "\n", + "MP_REGISTER_MODULE(MP_QSTR_specialclass, specialclass_user_cmodule, MODULE_SPECIALCLASS_ENABLED);\n", + "\n", + "```" ] }, { diff --git a/keywordfunction/keywordfunction.c b/keywordfunction/keywordfunction.c new file mode 100644 index 0000000..94c9f95 --- /dev/null +++ b/keywordfunction/keywordfunction.c @@ -0,0 +1,37 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" + +STATIC mp_obj_t keywordfunction_add_ints(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_a, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_b, MP_ARG_INT, {.u_int = 0}}, + { MP_QSTR_return, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if(MP_OBJ_IS_TYPE(args[2].u_rom_obj, &mp_type_str)) { + int16_t a = args[0].u_int; + int16_t b = args[1].u_int; + if(strcmp(mp_obj_get)) return mp_obj_new_int(a + b); + return mp_obj_new_float(a + b); + } +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW((keywordfunction_add_ints_obj, 1, keywordfunction_add_ints); + +STATIC const mp_rom_map_elem_t keywordfunction_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_keywordfunction) }, + { MP_ROM_QSTR(MP_QSTR_add_ints), (mp_obj_t)&keywordfunction_add_ints_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(keywordfunction_module_globals, keywordfunction_module_globals_table); + +const mp_obj_module_t keywordfunction_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&keywordfunction_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_keywordfunction, keywordfunction_user_cmodule, MODULE_KEYWORDFUNCTION_ENABLED); diff --git a/keywordfunction/micropython.mk b/keywordfunction/micropython.mk new file mode 100644 index 0000000..fbdc6b5 --- /dev/null +++ b/keywordfunction/micropython.mk @@ -0,0 +1,8 @@ +USERMODULES_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD += $(USERMODULES_DIR)/keywordfunction.c + +# We can add our module folder to include paths if needed +# This is not actually needed in this example. +CFLAGS_USERMOD += -I$(USERMODULES_DIR) \ No newline at end of file diff --git a/keywordfunction/micropython.mk~ b/keywordfunction/micropython.mk~ new file mode 100644 index 0000000..036db93 --- /dev/null +++ b/keywordfunction/micropython.mk~ @@ -0,0 +1,8 @@ +USERMODULES_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD += $(USERMODULES_DIR)/keywordclass.c + +# We can add our module folder to include paths if needed +# This is not actually needed in this example. +CFLAGS_USERMOD += -I$(USERMODULES_DIR) \ No newline at end of file diff --git a/simplefunction/simplefunction.c b/simplefunction/simplefunction.c index fd028b3..c86d2db 100644 --- a/simplefunction/simplefunction.c +++ b/simplefunction/simplefunction.c @@ -1,36 +1,24 @@ -// Include required definitions first. #include "py/obj.h" #include "py/runtime.h" #include "py/builtin.h" -// This is the function which will be called from Python as example.add_ints(a, b). STATIC mp_obj_t simplefunction_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) { - // Extract the ints from the micropython input objects int a = mp_obj_get_int(a_obj); int b = mp_obj_get_int(b_obj); - - // Calculate the addition and convert to MicroPython object. return mp_obj_new_int(a + b); } -// Define a Python reference to the function above + STATIC MP_DEFINE_CONST_FUN_OBJ_2(simplefunction_add_ints_obj, simplefunction_add_ints); -// Define all properties of the example module. -// Table entries are key/value pairs of the attribute name (a string) -// and the MicroPython object reference. -// All identifiers and strings are written as MP_QSTR_xxx and will be -// optimized to word-sized integers by the build system (interned strings). STATIC const mp_rom_map_elem_t simplefunction_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_simplefunction) }, { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&simplefunction_add_ints_obj) }, }; STATIC MP_DEFINE_CONST_DICT(simplefunction_module_globals, simplefunction_module_globals_table); -// Define module object. const mp_obj_module_t simplefunction_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&simplefunction_module_globals, }; -// Register the module to make it available in Python MP_REGISTER_MODULE(MP_QSTR_simplefunction, simplefunction_user_cmodule, MODULE_SIMPLEFUNCTION_ENABLED); diff --git a/specialclass/micropython.mk b/specialclass/micropython.mk new file mode 100644 index 0000000..9e1b0d2 --- /dev/null +++ b/specialclass/micropython.mk @@ -0,0 +1,8 @@ +USERMODULES_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD += $(USERMODULES_DIR)/specialclass.c + +# We can add our module folder to include paths if needed +# This is not actually needed in this example. +CFLAGS_USERMOD += -I$(USERMODULES_DIR) \ No newline at end of file diff --git a/specialclass/specialclass.c b/specialclass/specialclass.c new file mode 100644 index 0000000..1a780a0 --- /dev/null +++ b/specialclass/specialclass.c @@ -0,0 +1,88 @@ +#include "py/runtime.h" +#include "py/obj.h" +#include "py/binary.h" + +typedef struct _specialclass_myclass_obj_t { + mp_obj_base_t base; + int16_t a; + int16_t b; +} specialclass_myclass_obj_t; + +const mp_obj_type_t specialclass_myclass_type; + +STATIC void myclass_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + specialclass_myclass_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_print_str(print, "myclass("); + printf("%d, ", self->a); + printf("%d)", self->b); +} + +mp_obj_t create_new_myclass(uint16_t a, uint16_t b) { + specialclass_myclass_obj_t *out = m_new_obj(specialclass_myclass_obj_t); + out->base.type = &specialclass_myclass_type; + out->a = a; + out->b = b; + return MP_OBJ_FROM_PTR(out); +} + +STATIC mp_obj_t myclass_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 2, true); + return create_new_myclass(mp_obj_get_int(args[0]), mp_obj_get_int(args[1])); +} + +STATIC const mp_rom_map_elem_t myclass_locals_dict_table[] = { +}; + +STATIC MP_DEFINE_CONST_DICT(myclass_locals_dict, myclass_locals_dict_table); + +STATIC mp_obj_t specialclass_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + specialclass_myclass_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: return mp_obj_new_bool((self->a > 0) && (self->b > 0)); + case MP_UNARY_OP_LEN: return mp_obj_new_int(2); + default: return MP_OBJ_NULL; // op not supported + } +} + +STATIC mp_obj_t specialclass_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + specialclass_myclass_obj_t *left_hand_side = MP_OBJ_TO_PTR(lhs); + specialclass_myclass_obj_t *right_hand_size = MP_OBJ_TO_PTR(rhs); + switch (op) { + case MP_BINARY_OP_EQUAL: + return mp_obj_new_bool((left_hand_side->a == right_hand_size->a) && (left_hand_side->b == right_hand_size->b)); + case MP_BINARY_OP_ADD: + return create_new_myclass(left_hand_side->a + right_hand_size->a, left_hand_side->b + right_hand_size->b); + case MP_BINARY_OP_MULTIPLY: + return create_new_myclass(left_hand_side->a * right_hand_size->a, left_hand_side->b * right_hand_size->b); + default: + return MP_OBJ_NULL; // op not supported + } +} + +const mp_obj_type_t specialclass_myclass_type = { + { &mp_type_type }, + .name = MP_QSTR_specialclass, + .print = myclass_print, + .make_new = myclass_make_new, + .unary_op = specialclass_unary_op, + .binary_op = specialclass_binary_op, + .locals_dict = (mp_obj_dict_t*)&myclass_locals_dict, +}; + +STATIC const mp_map_elem_t specialclass_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_specialclass) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_myclass), (mp_obj_t)&specialclass_myclass_type }, +}; + +STATIC MP_DEFINE_CONST_DICT ( + mp_module_specialclass_globals, + specialclass_globals_table +); + +const mp_obj_module_t specialclass_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_specialclass_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_specialclass, specialclass_user_cmodule, MODULE_SPECIALCLASS_ENABLED);