本帖最后由 vsfopen 于 2018-6-18 23:42 编辑
1. 简介
vsfvm是VSF中的脚本系统,包括编译器和虚拟机。vsfvm的开发主要针对小资源的应用,虚拟机最小占用3-5K flash、百来字节 ram;编译器最小占用13-15K flash、3-4K ram。当然,实际资源占用还是看用了那些扩展,以及代码的规模。
vsfvm虚拟机核心是基于堆栈的实现,使用尽量精简的语法以减少资源占用。vsfvm虚拟机支持各种扩展,并且扩展可以实现面向对象,后面会用GPIO扩展来举例说明。
2. bringup
struct
{
struct vsfvm_t vm;
struct vsfvm_script_t script;
bool polling;
struct vsfvmc_t vmc;
struct vsfvmc_lexer_list_t dart;
bool compiling;
uint32_t token[4 * 1024];
uint32_t token_num;
uint8_t source[4 * 1024];
} vsfvm;
上面只是示例代码,大部分在实际应用中并不需要,比如source[4 * 1024],是用于存放上位机发过来的源代码;token[4 * 1024]和token_num是用于存放编译结果。这里只是为了测试方便,所以都放在ram里,实际应用环境中,一般都不需要。
编译器部分代码:app->vsfvm.compiling = true;
vsfdbg_prints("start compiling ..." VSFCFG_DEBUG_LINEEND);
vsfvmc_init(vmc, &usrapp, NULL);
vsfvmc_register_ext(vmc, &vsfvm_ext_std);
vsfvmc_ext_register_vsf(vmc);
vsfvmc_register_lexer(vmc, &app->vsfvm.dart);
err = vsfvmc_script(vmc, "main.dart");
if (err < 0) goto err_return;
err = vsfvmc_input(vmc, src);
if (err < 0)
{
err_return:
err = -err;
vsfdbg_printf("command line compile error: %s" VSFCFG_DEBUG_LINEEND,
(err >= VSFVMC_ERRCODE_END) ? "unknwon error" :
vsfvmc_errcode_str[err]);
compile_end:
vsfvmc_fini(vmc);
app->vsfvm.compiling = false;
return VSFERR_NONE;
}
err = vsfvmc_input(vmc, "\xFF");
if (!err)
{
if (vmc->bytecode.sp >= dimof(app->vsfvm.token))
{
vsfdbg_prints("not enough space for token buffer" VSFCFG_DEBUG_LINEEND);
goto compile_end;
}
vsfdbg_printf("compiled OK, token number : %d" VSFCFG_DEBUG_LINEEND,
vmc->bytecode.sp);
}
这里,首先调用vsfvmc_init初始化编译器;然后调用vsfvmc_register_ext注册需要支持的扩展(这里的顺序必须和虚拟机注册扩展的顺序完全一致,后面会有说明),并且调用vsfvmc_register_lexer注册词法分析器;然后调用vsfvmc_script生成脚本编译环境;之后调用vsfvmc_input输入脚本,这里可以多次调用vsfvmc_input(每次调用的时候,输入的代码不可能分隔语法,比如第一次输入"ma",第二次输入"in();",这里把main分隔开了),也可以整个代码一次输入;所有代码输入完后,最后调用一次vsfvmc_input(vmc, "\xFF");表示代码输入完毕,可以从vmc->bytecode里读取编译结果。
虚拟机部分的代码:
app->vsfvm.polling = true;
memset(vm, 0, sizeof(*vm));
memset(script, 0, sizeof(*script));
vm->thread_pool.pool_size = 16;
script->token = app->vsfvm.token;
script->token_num = app->vsfvm.token_num;
vsfvm_init(vm);
vsfvm_register_ext(vm, &vsfvm_ext_std);
vsfvm_ext_register_vsf(vm);
vsfvm_script_init(vm, script);
虚拟机里,只需要初始化script结构,指向对应的编译结果,然后调用vsfvm_init初始化虚拟机;调用vsfvm_register_ext注册扩展(顺序必须和编译器里注册扩展的顺序完全一致);然后调用vsfvm_script_init初始化脚本就可以运行了。
另外,在VSF的nrt_poll(非实时)里,还需要调用vsfvm_poll来轮询虚拟机。当然,这里虽然说是轮询,实际上vsfsm_nrt_poll也是事件驱动的,有事件的时候(MCU被唤醒)才会运行,运行之后,如果没有事件,MCU继续休眠,直到下一次被中断唤醒:
void usrapp_nrt_poll(struct usrapp_t *app)
{
if (app->vsfvm.polling)
vsfvm_poll(&app->vsfvm.vm);
}
注意,上面代码里有vsfvm_ext_register_vsf和vsfvmc_ext_register_vsf,是用来注册VSF的基本扩展,比如GPIO等等。
|