解释器是能够执行用其他计算机语言编写的程序的系统软件,是一种翻译程序。它的执行方式是一边翻译一边执行,因此其执行效率一般偏低,但是解释器的实现较为简单。
解释器执行时,每次从程序的源代码中读入一个标识符。如果读入的是关键字,解释器就按照该关键字的要求执行规定的操作。不同的关键字一般在程序中对应不同的数据结构。在到达程序的结尾之前,这个过程将反复进行。
以一个Scheme语句为例
(+ (- 2 1) 3)
首先遇到(
创建一个栈帧。
遇到+
表明需要对后两个操作数进行加法运算。
又遇到(
创建一个栈帧。
遇到-
,需要对后面两个操作数进行减法运算。
然后相继遇到2和1,减法运算得到第一个操作数结果为1
遇到)
,带着结果执行pop操作回到上一个栈帧
然后遇到3,第一个操作数的结果为1,两个操作数进行加法运算得到结果为4
遇到)
,返回上级栈帧,最终结果为4
Micropython技术是依赖Byte Code的执行,在编译阶段就将py文件先转换成mpy文件,在通过mpy-tool.py生成Byte Code,Byte Code在执行时会依赖Virtual Machine入口表,找到对应的Module入口,最终找到对应的Funcion binary code执行。其中所有的Function都通过Dictionary的形式存储,而每一个Dictionary都有自己的QSTR,Micropython有buildin的QSTR和用户扩展的QSTR。具体流程可参考如下图。
每次编译前makemodulesdefs.py
, makeqstrdefs.py
, makeqstrdata.py
脚本会收集和生成所有的QSTR,以QDEF(MP_QSTR_sizeof, (const byte*)"\x49\x73\x06" "sizeof")
的形式写入genhdr/qstrdefs.generated.h
文件。其中\x49\x73
是字符串哈希值,\x06
是字符串长度。MicroPython通过哈希值和长度进行字符串比较从而尽可能地保证性能。
qstrdefs.generated.h
主要在qstr.h
和qstr.c
中被include。
/* qstr.h */
enum {
#ifndef NO_QSTR
#define QDEF(id, str) id,
#include "genhdr/qstrdefs.generated.h"
#undef QDEF
#endif
MP_QSTRnumber_of, // no underscore so it can't clash with any of the above
};
typedef struct _qstr_pool_t {
struct _qstr_pool_t *prev;
size_t total_prev_len;
size_t alloc;
size_t len;
const byte *qstrs[];
} qstr_pool_t;
/* qstr.c */
const qstr_pool_t mp_qstr_const_pool = {
NULL, // no previous pool
0, // no previous pool
MICROPY_ALLOC_QSTR_ENTRIES_INIT,
MP_QSTRnumber_of, // corresponds to number of strings in array just below
{
#ifndef NO_QSTR
#define QDEF(id, str) str,
#include "genhdr/qstrdefs.generated.h"
#undef QDEF
#endif
},
};
不同文件里QDEF的意义是不同的,qstr.h
里是取了前半部分也就是MP_QSTR_xx
形式的操作符,加入到enum中作为index,qstr.c
中则提取了由哈希值长度以及实际字符串组成的字符串,即MicroPython字节码,将其加入到qstr_pool这个数据结构中。qstr_pool中都是预定义的qstr,包括MicroPython内置qstr和用户通过c语言定义的qstr。这个pool称为interned pool。
mpy-cross将py文件编译成mpy文件,mpy文件就是由字节码组成,字节码输入MicroPython虚拟机后就会查找qstr pool执行对应的函数。
我们通过C语言扩展MicroPython时也需要将关键字注册到qstr pool中。
阅读前需要对Python面向对象由初步认识。
直接看代码注释应该就能明白。C语言的相关API主要在py/obj.h
和py/obj.c
中。
#include <math.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
const mp_obj_type_t vector_vector3D_type;
typedef struct _vector_vector3D_obj_t {
mp_obj_base_t base;
float x, y, z;
} vector_vector3D_obj_t;
// helper function
mp_obj_t create_new_vector3D(float _x, float _y, float _z) {
vector_vector3D_obj_t *vector3D = m_new_obj(vector_vector3D_obj_t);
vector3D->base.type = &vector_vector3D_type;
vector3D->x = _x;
vector3D->y = _y;
vector3D->z = _z;
return MP_OBJ_FROM_PTR(vector3D);
}
STATIC void vector3D_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_print_str(print, "vector3D(");
mp_obj_print_helper(print, mp_obj_new_float(self->x), PRINT_REPR);
mp_print_str(print, ", ");
mp_obj_print_helper(print, mp_obj_new_float(self->y), PRINT_REPR);
mp_print_str(print, ", ");
mp_obj_print_helper(print, mp_obj_new_float(self->z), PRINT_REPR);
mp_print_str(print, ")");
}
/*
* class vector3D:
* def __init__(self, _x, _y, _z):
* self.x = _x
* self.y = _y
* self.z = _z
*/
STATIC mp_obj_t vector3D_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, 3, 3, true);
vector_vector3D_obj_t *self = m_new_obj(vector_vector3D_obj_t);
self->base.type = &vector_vector3D_type;
self->x = mp_obj_get_float(args[0]);
self->y = mp_obj_get_float(args[1]);
self->z = mp_obj_get_float(args[2]);
return MP_OBJ_FROM_PTR(self);
}
/*
* class vector3D:
*
* @property
* def x(self):
* return self.x
*
* @property
* def y(self):
* return self.y
*
* @property
* def z(self):
* return self.z
*/
STATIC mp_obj_t vector3D_x(mp_obj_t self_in) {
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(self->x);
}
STATIC mp_obj_t vector3D_y(mp_obj_t self_in) {
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(self->y);
}
STATIC mp_obj_t vector3D_z(mp_obj_t self_in) {
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(self->z);
}
/*
* class vector3D:
* def normalize(self):
* length = self.__abs__()
* if length == 0:
* raise ZeroDivisionError("vector's norm is zero")
* self.x /= length
* self.y /= length
* self.z /= length
*/
STATIC mp_obj_t vector3D_normalize(mp_obj_t self_in) {
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
float length = sqrtf(self->x * self->x + self->y * self->y + self->z * self->z);
if (length == 0) {
mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("vector's norm is zero"));
return mp_const_none;
}
self->x /= length;
self->y /= length;
self->z /= length;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(vector3D_normalize_obj, vector3D_normalize);
STATIC void vector3D_attr(mp_obj_t self, qstr attribute, mp_obj_t *destination) {
if (attribute == MP_QSTR_x) {
destination[0] = vector3D_x(self);
} else if (attribute == MP_QSTR_y) {
destination[0] = vector3D_y(self);
} else if (attribute == MP_QSTR_z) {
destination[0] = vector3D_z(self);
} else if (attribute == MP_QSTR_normalize) {
destination[0] = (mp_obj_t)&vector3D_normalize_obj;
destination[1] = self;
}
}
/*
* class vector3D:
* def __bool__(self):
* return self.x != 0 or self.y != 0 or self.z != 0
* def __abs__(self):
* return sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
* def __eq__(self, other):
* return self.x == other.x and self.y = other.y and self.z = other.z
* def __add__(self, other):
* return vector3D(self.x + other.x, self.y + other.y, self.z + other.z)
* def __sub__(self, other):
* return vector3D(self.x - other.x, self.y - other.y, self.z - other.z)
* def __mul__(self, other):
* return sqrt(self.x * other.x + self.y * other.y + self.z * other.z)
*/
STATIC mp_obj_t vector3D_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
vector_vector3D_obj_t *self = MP_OBJ_TO_PTR(self_in);
switch (op) {
case MP_UNARY_OP_BOOL: return mp_obj_new_bool((self->x != 0) || (self->y != 0) || (self->z != 0));
case MP_UNARY_OP_ABS:
return mp_obj_new_float(sqrtf(self->x * self->x + self->y * self->y + self->z * self->z));
default: return MP_OBJ_NULL; // operator not supported
}
}
STATIC mp_obj_t vector3D_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
vector_vector3D_obj_t *left_hand_side = MP_OBJ_TO_PTR(lhs);
vector_vector3D_obj_t *right_hand_side = MP_OBJ_TO_PTR(rhs);
switch (op) {
case MP_BINARY_OP_EQUAL:
return mp_obj_new_bool((left_hand_side->x == right_hand_side->x) \
&& (left_hand_side->y == right_hand_side->y) \
&& (left_hand_side->z == right_hand_side->z));
case MP_BINARY_OP_ADD:
return create_new_vector3D(left_hand_side->x + right_hand_side->x, \
left_hand_side->y + right_hand_side->y, \
left_hand_side->z + right_hand_side->z);
case MP_BINARY_OP_SUBTRACT:
return create_new_vector3D(left_hand_side->x - right_hand_side->x, \
left_hand_side->y - right_hand_side->y, \
left_hand_side->z - right_hand_side->z);
case MP_BINARY_OP_MULTIPLY: // inner product
return mp_obj_new_float(left_hand_side->x * right_hand_side->x +\
left_hand_side->y * right_hand_side->y +\
left_hand_side->z * right_hand_side->z);
default:
return MP_OBJ_NULL; // operator not supported
}
}
// STATIC const mp_rom_map_elem_t vector3D_locals_dict_table[] = {
// { MP_ROM_QSTR(MP_QSTR_normalize), MP_ROM_PTR(&vector3D_normalize_obj) },
// };
// STATIC MP_DEFINE_CONST_DICT(vector3D_locals_dict, vector3D_locals_dict_table);
const mp_obj_type_t vector_vector3D_type = {
{ &mp_type_type },
.name = MP_QSTR_vector3D,
.print = vector3D_print,
.make_new = vector3D_make_new,
.unary_op = vector3D_unary_op,
.binary_op = vector3D_binary_op,
.attr = vector3D_attr,
// .locals_dict = (mp_obj_dict_t*)&vector3D_locals_dict,
};
/*
* def reverse(vec):
return vector3D(-vec.x, -vec.y, -vec.z)
*/
STATIC mp_obj_t vector_reverse(mp_obj_t o_in) {
if (!mp_obj_is_type(o_in, &vector_vector3D_type)) {
mp_raise_TypeError(MP_ERROR_TEXT("argument is not a vector"));
}
vector_vector3D_obj_t *vector3D = MP_OBJ_TO_PTR(o_in);
return create_new_vector3D(-vector3D->x, -vector3D->y, -vector3D->z);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(vector_reverse_obj, vector_reverse);
STATIC const mp_rom_map_elem_t vector_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_vector) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_vector3D), (mp_obj_t)&vector_vector3D_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_reverse), (mp_obj_t)&vector_reverse_obj },
};
STATIC MP_DEFINE_CONST_DICT(vector_module_globals, vector_module_globals_table);
const mp_obj_module_t vector_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&vector_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_vector, vector_user_cmodule, MODULE_VECTOR_ENABLED);
以下是运行结果:
>>> from vector import vector3D
>>> a = vector3D(1, 2, 3)
>>> a
vector3D(1.0, 2.0, 3.0)
>>> vector3D
<class 'vector3D'>
>>> a.x
1.0
>>> a.y
2.0
>>> a.z
3.0
>>> a.normalize()
>>> a
vector3D(0.2672612, 0.5345225, 0.8017837)
>>> import vector
>>> vector.reverse(a)
vector3D(-0.2672612, -0.5345225, -0.8017837)
vector3D(4.0, 5.0, 6.0)
>>> a + b
vector3D(5.0, 7.0, 9.0)
>>> a - b
vector3D(-3.0, -3.0, -3.0)
>>> a * b
32.0
需要能够通过MicroPython直接调用ArduSensorPlatform的Host指针进行Read和Write操作。
现在的想法是封装一个结构体,成员是一个Host,在HostWrapper.h
中extern这个结构体变量并定义函数对结构体进行操作。然后在micropython中引入这个HostWrapper.h
。但问题是HostWrapper.h
肯定是要include Host.h
的,这个c++头文件要怎么解决?
可能可以用空指针搞。今天先摸了。
本文章使用limfx的vscode插件快速发布