my_basic 一款轻量级basic解析器

my_basic 一款轻量级basic解析器

Louis 544 2020-02-15

我的本职工作是为机器人开发应用程序,每个项目可能使用到的业务场景不一样,导致没做一次不同的项目都需要在业务层的代码中耗费很多时间。何不把业务层的代码逻辑与程序底层的逻辑分离开,让用户使用一种统一的语言对机器人行为进行编程。程序员就可以从业务层代码中解脱出来,参与更加底层的驱动开发。

Basic语言是一种简单的入门级语言,是一种非常方便客户掌握的语言,项目中交付一些示例代码,就可以让客户自行进行二次开发,定义机器人行为。

本次用到的basic语言解析器来自:

https://github.com/paladin-t/my_basic

MY-BASIC是用标准C语言编写的轻量级BASIC解释器,包含在两个文件中。它的目标是可嵌入,可扩展和可移植。它是一种动态类型化的编程语言,保留了结构化的语法,支持一种基于原型的编程(OOP)的样式,还通过lambda抽象实现了一种功能范例。内核写入C源文件和关联的头文件中。既可以将其用作独立的解释器,也可以将其嵌入C,C ++,Java,Objective-C,Swift,C#等现有项目中,并且可以通过添加自己的脚本接口进行完全自定义。

这里有一个使用my_basic进行二次开发的例子:

https://github.com/jacmoe/nasl/tree/fd1ea456d8d13c5e4d9cc5b5ae1eb6be792e0049

里面有基本的函数绑定,my_basic解析器的初始化和使用等案例。

1. 从basic中传入值

/* Register an API function to a MY-BASIC environment */
int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f) {
	if(!s || !n || !f) return 0;

	return _register_func(s, (char*)n, f, false);
}

使用方式:

//进行绑定
void static basic_arm_script_init() {
/**
* 用于绑定basic函数和用户定义函数
* 1. 参数 s ,全局上下文参数
* 2. 参数 n ,basic中的函数名
* 3. 参数 f ,用户定义的函数的入口地址
*/
    mb_register_func(basic_script_get_interpreter(), "startJob", _start_job);
}

//对应用户定义的函数
static int _start_job(struct mb_interpreter_t *s, void **l) {
    char *job_name_c = nullptr;
    int result = MB_FUNC_OK;
    //确定送入的指针非空
    mb_assert(s && l);
    //开左边括号
    mb_check(mb_attempt_open_bracket(s, l));
    //尝试取字符串值
    mb_check(mb_pop_string(s, l, &job_name_c));
    //关括号 完成解析
    mb_check(mb_attempt_close_bracket(s, l));
    //do something
    return result;
}

在basic文件中调用

startJob ("job name");

2.给basic返回值

/* Register an API function to a MY-BASIC environment */
int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f) {
	if(!s || !n || !f) return 0;

	return _register_func(s, (char*)n, f, false);
}

使用方式:

//进行绑定,参数见1 
void static basic_arm_script_init() {
    mb_register_func(basic_script_get_interpreter(), "getVar", _getVarFormArm);
}

//对应用户定义的函数
static int _getVarFormArm (struct mb_interpreter_t *s, void **l){
    int result = MB_FUNC_OK;
    int var_index;
    mb_assert(s && l);
    mb_check(mb_attempt_open_bracket(s, l));
    mb_check(mb_pop_int(s, l, &var_index));
    mb_check(mb_attempt_close_bracket(s, l));
//给basic放入返回值,这里直接+1
    mb_check(mb_push_int(s,l,var_index+1));
    return result;
}

在basic文件中调用,就可以把数值打出来。

print getVar (25);

3. 使用collection双向传值

使用collection进行传值,目前完成了list传值的内容,dict还未完成

basic代码中,初始化一个list数据格式,送入getMutiVar函数中,并从中返回一个list

index_array = list(1, 2, 3, 4,5,6,7,8,9)
l = getMutiVar (index_array)

在c++代码中:

static int _getMutiVar(struct mb_interpreter_t *s, void **l) {
    int result = MB_FUNC_OK;
    mb_value_t val;
    mb_make_nil(val);
    mb_assert(s && l);
    mb_check(mb_attempt_open_bracket(s, l));
//  从传入参数中拿到list的元数据,下面通过build_vector_from_val转换为vector
    mb_check(mb_pop_value(s, l, &val));
    mb_check(mb_attempt_close_bracket(s, l));
    std::vector<int> list;
//通过build_vector_from_val转换为vector
    BasicUtil::build_vector_from_val<int>(s, l, val, list);
    //do something

//下面是把vector转换为元数据返回给basic
//  新建一个coll表
    mb_value_t coll;
    coll->type = MB_DT_LIST;
    mb_init_coll(s, l, coll);
//通过build_val_from_vector转换为coll
    BasicUtil::build_val_from_vector(s, l, feedback, &coll);
//将数据传入my_basic 即可在上层得到返回
    mb_push_value(s, l, coll);
    return result;
}
/**
* 用于把获取的元数据转换成vector的形式
* 1. 参数 s ,全局上下文参数
* 2. 参数 l ,也是上下文相关的,不知道是干嘛的
* 3. 参数 val ,list元数据,在mb_pop_value()中获取
* 4. 参数 vtr , 用于接收结果的vector
*/
static void
build_vector_from_val(struct mb_interpreter_t *s, void **l, mb_value_t val, std::vector<T> &vtr) {
    mb_value_t idx;
//新建迭代器并初始化
    mb_value_t element = mb_value_t();
    int size = 0;
    mb_count_coll(s, l, val, &size);
    for (int i = 0; i < size; i++) {
//推进迭代器,并且通过mb_get_coll获得元数据中具体的数值
        mb_make_int(idx, i);
        mb_get_coll(s, l, val, idx, &element);
        vtr.push_back(element.value.integer);
    }
}

static void
build_val_from_vector(struct mb_interpreter_t *s, void **l, const std::vector<T> &vtr, mb_value_t *val) {
    int feedback_index = 0;
    for (auto item  : vtr) {
//推进迭代器,并且通过mb_set_coll获得元数据中具体的数值
        mb_value_t mb_feedback_index;
        mb_value_t feedback_value;
        mb_make_int(mb_feedback_index, feedback_index++);
        mb_make_int(feedback_value, item);
        mb_set_coll(s, l, *val, mb_feedback_index, feedback_value);
    }
}

4. 使用usertype保存用户自定义的上下文参数

该功能用于在basic中保存复杂的数据类型,该数据在源码中是以对象指针的方式保存的,如果处理不慎,可能
引起数据被提前释放或者是内存泄漏等问题。建议专门写一些api进行内存管理

另外,由于是对象指针,usertype在basic中无法直接进行修改、查看等操作,需要提供专门对操作函数。

在basic中:

blitbuf = buffer_create()

buffer_clear(blitbuf, ORANGE)

该程序段返回一个blitbuf的用户类型,用户无法直接在basic中读取到blitbuf内的内容和数据结构,于是作者提供了多个
api帮助basic用户对该结构进行操作,例如:

BUFFER_CREATE 
MAIN_BUFFER
SET_MAINBUFFER
BUFFER_DESTROY
BUFFER_BLIT
BUFFER_CLEAR
SUB_BUFFER
BUFFER_SET_PIXEL
BUFFER_GET_PIXEL

在c++代码中,实现一个_BUFFER_CREATE

static int _buffer_create(struct mb_interpreter_t *s, void **l) {
    int result = MB_FUNC_OK;
    auto *buffer = new Buffer;
    mb_assert(s && l);
    mb_check(mb_attempt_open_bracket(s, l));
    mb_check(mb_attempt_close_bracket(s, l));
    buffer->width = 100;
    buffer->height = 60;
//do something
//强制转换为空指针,该函数不会进行拷贝,不要调用delete删除指针内容
    mb_check(mb_push_usertype(s, l, (void *) buffer));
//除非在析构函数中,否则不能调用delete 这样会销毁在basic中存放对basic
//    delete buffer;
    return result;
}

在c++代码中,实现一个_BUFFER_CLEAR

static int _buffer_clear(struct mb_interpreter_t *s, void **l) {
    int result = MB_FUNC_OK;
//Buffer是一个定义的结构体,内容自定即可
    auto *buffer = new Buffer;
//用于接收数据的空指针
    void *up = 0;
    mb_assert(s && l);
    mb_check(mb_attempt_open_bracket(s, l));
//取出元数据
    mb_check(mb_pop_usertype(s, l, &up));
    mb_check(mb_attempt_close_bracket(s, l));
    if (!up)
        return MB_FUNC_ERR;
//强制类型转换
    buffer = (Buffer *) up;
    std::cout << buffer->width << std::endl;
    std::cout << buffer->height << std::endl;
// do something
//即可在对buffer数据进行操作
//除非在析构函数中,否则不能调用delete 这样会销毁在basic中存放basic中存放的buffer
//    delete buffer;
    return result;
}