rework fscripts

This commit is contained in:
lixianjing 2020-11-13 08:57:05 +08:00
parent 5c7d74eebf
commit e298f62872
11 changed files with 675 additions and 236 deletions

View File

@ -116,6 +116,7 @@ EXPORTS
value_double
value_set_str
value_dup_str
value_dup_str_with_len
value_set_wstr
value_str
value_str_ex
@ -1108,6 +1109,9 @@ EXPORTS
file_read
file_read_part
file_write
fscript_create
fscript_exec
fscript_destroy
fscript_eval
func_call_parser_init
func_call_parser_parse

View File

@ -101,6 +101,7 @@ EXPORTS
value_double
value_set_str
value_dup_str
value_dup_str_with_len
value_set_wstr
value_str
value_str_ex
@ -302,6 +303,9 @@ EXPORTS
file_read
file_read_part
file_write
fscript_create
fscript_exec
fscript_destroy
fscript_eval
func_call_parser_init
func_call_parser_parse

View File

@ -5,13 +5,14 @@
fscript 是一个极简的脚本引擎,借鉴了函数语言中一些思路,主要用于低端嵌入式系统,让用户轻松扩展现有系统,而不需要重新编译和下载固件。
* 特色:
* 小巧。核心代码 400 行,扩展函数 600 行。
* 小巧。核心代码 600 行,扩展函数 600 行。
* 灵活。支持多条语句、函数嵌套调用和变量定义。
* 强大。超过 50 个内置函数,支持用 C 语言扩展函数。
* 小内存。边解析边执行,释放不用的内存,最低开销小于 500 字节。
* 小内存。最低开销小于 500 字节。
* 限制:
* 不支持循环。
* 不支持函数定义。
## 2. 示例

View File

@ -19,34 +19,73 @@
#include "tkc/time_now.h"
#include "tkc/fscript.h"
struct _fscript_func_call_t;
typedef struct _fscript_func_call_t fscript_func_call_t;
struct _fscript_func_call_t {
fscript_func_t func;
fscript_args_t args;
fscript_func_call_t* next;
};
#define VALUE_TYPE_JSCRIPT_ID 128
#define VALUE_TYPE_JSCRIPT_FUNC VALUE_TYPE_JSCRIPT_ID + 1
static ret_t func_if(object_t* obj, fscript_args_t* args, value_t* result);
static ret_t func_set(object_t* obj, fscript_args_t* args, value_t* result);
static ret_t func_args_push_str(fscript_args_t* args, const char* str, uint32_t size) {
char* new_str = NULL;
return_value_if_fail(str != NULL && args->size < FSCRIPT_MAX_ARGS, RET_BAD_PARAMS);
static value_t* value_set_func(value_t* v, fscript_func_call_t* func) {
value_set_pointer(v, func);
v->type = VALUE_TYPE_JSCRIPT_FUNC;
return v;
}
new_str = tk_strndup(str, size);
return_value_if_fail(new_str != NULL, RET_OOM);
static ret_t func_args_extend(fscript_args_t* args) {
if (args->size < args->capacity) {
return RET_OK;
}
value_set_str(args->args + args->size, new_str);
args->args[args->size].free_handle = TRUE;
args->size++;
if (args->capacity < FSCRIPT_MAX_ARGS) {
value_t* new_args = NULL;
uint16_t capacity = args->capacity + 2;
capacity = tk_min(capacity, FSCRIPT_MAX_ARGS);
new_args = TKMEM_REALLOC(args->args, capacity * sizeof(value_t));
return_value_if_fail(new_args != NULL, RET_OOM);
args->args = new_args;
args->capacity = capacity;
return RET_OK;
}
return RET_FAIL;
}
static ret_t func_args_push_str(fscript_args_t* args, const char* str, uint32_t size,
bool_t is_id) {
value_t* v = NULL;
return_value_if_fail(str != NULL && func_args_extend(args) == RET_OK, RET_BAD_PARAMS);
v = args->args + args->size++;
value_dup_str_with_len(v, str, size);
v->type = is_id ? VALUE_TYPE_JSCRIPT_ID : v->type;
return RET_OK;
}
static ret_t func_args_push(fscript_args_t* args, value_t* v) {
return_value_if_fail(v != NULL && args->size < FSCRIPT_MAX_ARGS, RET_BAD_PARAMS);
value_deep_copy(args->args + args->size, v);
args->size++;
return_value_if_fail(v != NULL && func_args_extend(args) == RET_OK, RET_BAD_PARAMS);
value_deep_copy(args->args + args->size++, v);
return RET_OK;
}
static ret_t func_args_init(fscript_args_t* args, str_t* str) {
static ret_t func_args_init(fscript_args_t* args, uint16_t init_args_capacity) {
memset(args, 0x00, sizeof(fscript_args_t));
args->str = str;
if (init_args_capacity > 0) {
args->args = TKMEM_ZALLOCN(value_t, init_args_capacity);
return_value_if_fail(args->args != NULL, RET_OOM);
args->capacity = init_args_capacity;
}
return RET_OK;
}
@ -54,9 +93,26 @@ static ret_t func_args_deinit(fscript_args_t* args) {
uint32_t i = 0;
for (i = 0; i < args->size; i++) {
value_t* v = args->args + i;
v->type = v->type == VALUE_TYPE_JSCRIPT_ID ? VALUE_TYPE_STRING : v->type;
value_reset(args->args + i);
}
args->size = 0;
TKMEM_FREE(args->args);
memset(args, 0x00, sizeof(fscript_args_t));
return RET_OK;
}
static ret_t fscript_func_call_destroy(fscript_func_call_t* call) {
fscript_func_call_t* iter = call;
fscript_func_call_t* next = NULL;
while (iter != NULL) {
next = iter->next;
func_args_deinit(&(call->args));
TKMEM_FREE(iter);
iter = next;
}
return RET_OK;
}
@ -78,7 +134,7 @@ typedef struct _token_t {
bool_t valid;
} token_t;
typedef struct _fscript_t {
typedef struct _fscript_parser_t {
const char* str;
const char* cursor;
object_t* obj;
@ -91,80 +147,170 @@ typedef struct _fscript_t {
char c;
str_t temp;
bool_t ignore;
fscript_func_call_t* first;
} fscript_parser_t;
typedef struct _fscript_t {
str_t str;
object_t* obj;
fscript_func_call_t* first;
} fscript_t;
static fscript_func_t fscript_lookup(fscript_t* fscript, const char* name, uint32_t size);
static fscript_func_call_t* fscript_func_call_create(fscript_parser_t* parser, const char* name,
uint32_t size);
static ret_t fscript_init(fscript_t* fscript, object_t* obj, const char* str) {
fscript_t* fscript_create_impl(fscript_parser_t* parser) {
fscript_t* fscript = TKMEM_ZALLOC(fscript_t);
return_value_if_fail(fscript != NULL, NULL);
fscript->str = parser->temp;
fscript->obj = OBJECT_REF(parser->obj);
fscript->first = parser->first;
parser->obj = NULL;
parser->first = NULL;
parser->temp.str = NULL;
return fscript;
}
static ret_t fscript_exec_func(fscript_t* fscript, fscript_func_call_t* iter, value_t* result) {
uint32_t i = 0;
ret_t ret = RET_OK;
fscript_args_t args;
func_args_init(&args, iter->args.size);
args.str = &(fscript->str);
args.size = iter->args.size;
return_value_if_fail((args.args != NULL || args.size == 0), RET_OOM);
for (i = 0; i < iter->args.size; i++) {
value_t* s = iter->args.args + i;
value_t* d = args.args + i;
int32_t save_type = s->type;
if (s->type == VALUE_TYPE_JSCRIPT_ID) {
s->type = VALUE_TYPE_STRING;
if (iter->func == func_set && i == 0) {
value_copy(d, s); /*func_set accept id/str as first param*/
} else if (object_get_prop(fscript->obj, value_str(s), d) != RET_OK) {
value_copy(d, s);
}
} else if (s->type == VALUE_TYPE_JSCRIPT_FUNC) {
s->type = VALUE_TYPE_POINTER;
fscript_func_call_t* func = (fscript_func_call_t*)value_pointer(s);
if (i > 0 && iter->func == func_if) {
if (value_bool(args.args) && i == 1) {
fscript_exec_func(fscript, func, d);
}
if (!value_bool(args.args) && i == 2) {
fscript_exec_func(fscript, func, d);
}
} else {
fscript_exec_func(fscript, func, d);
}
} else {
value_copy(d, s);
}
s->type = save_type;
}
ret = iter->func(fscript->obj, &args, result);
func_args_deinit(&args);
return ret;
}
ret_t fscript_exec(fscript_t* fscript, value_t* result) {
fscript_func_call_t* iter = NULL;
return_value_if_fail(fscript != NULL, RET_FAIL);
iter = fscript->first;
while (iter != NULL) {
return_value_if_fail(iter->func != NULL, RET_FAIL);
value_reset(result);
return_value_if_fail(fscript_exec_func(fscript, iter, result) == RET_OK, RET_FAIL);
iter = iter->next;
}
return RET_OK;
}
ret_t fscript_destroy(fscript_t* fscript) {
return_value_if_fail(fscript != NULL, RET_FAIL);
OBJECT_UNREF(fscript->obj);
str_reset(&(fscript->str));
fscript_func_call_destroy(fscript->first);
memset(fscript, 0x00, sizeof(fscript_t));
fscript->obj = obj;
fscript->str = str;
fscript->cursor = str;
str_init(&(fscript->temp), 64);
return RET_OK;
}
static ret_t fscript_parser_init(fscript_parser_t* parser, object_t* obj, const char* str) {
memset(parser, 0x00, sizeof(fscript_parser_t));
parser->obj = obj;
parser->str = str;
parser->cursor = str;
str_init(&(parser->temp), 64);
return RET_OK;
}
static ret_t fscript_deinit(fscript_t* fscript) {
str_reset(&(fscript->temp));
TKMEM_FREE(fscript->error);
static ret_t fscript_parser_deinit(fscript_parser_t* parser) {
str_reset(&(parser->temp));
TKMEM_FREE(parser->error);
fscript_func_call_destroy(parser->first);
return RET_OK;
}
static char fscript_get_char(fscript_t* fscript) {
static char fscript_parser_get_char(fscript_parser_t* parser) {
char c = '\0';
return_value_if_fail(fscript != NULL, c);
if (fscript->c) {
c = fscript->c;
fscript->c = '\0';
return_value_if_fail(parser != NULL, c);
if (parser->c) {
c = parser->c;
parser->c = '\0';
return c;
}
c = fscript->cursor[0];
c = parser->cursor[0];
if (c) {
if (c == '\n') {
fscript->row++;
fscript->col = 0;
parser->row++;
parser->col = 0;
} else {
fscript->col++;
parser->col++;
}
fscript->cursor++;
parser->cursor++;
}
return c;
}
static ret_t fscript_unget_char(fscript_t* fscript, char c) {
return_value_if_fail(fscript->cursor > fscript->str, RET_BAD_PARAMS);
static ret_t fscript_parser_unget_char(fscript_parser_t* parser, char c) {
return_value_if_fail(parser->cursor > parser->str, RET_BAD_PARAMS);
fscript->c = c;
parser->c = c;
return RET_OK;
}
typedef struct _func_entry_t {
const char* name;
fscript_func_t func;
} func_entry_t;
static ret_t fscript_parser_set_error(fscript_parser_t* parser, const char* str) {
return_value_if_fail(parser != NULL && str != NULL, RET_BAD_PARAMS);
static ret_t fscript_set_error(fscript_t* fscript, const char* str) {
return_value_if_fail(fscript != NULL && str != NULL, RET_BAD_PARAMS);
fscript->error = tk_str_copy(fscript->error, str);
log_warn("at line(%u) col (%u): %s\n", fscript->row, fscript->col, str);
parser->error = tk_str_copy(parser->error, str);
log_warn("at line(%u) col (%u): %s\n", parser->row, parser->col, str);
return RET_OK;
}
static ret_t fscript_skip_seperators(fscript_t* fscript) {
static ret_t fscript_parser_skip_seperators(fscript_parser_t* parser) {
char c = '\0';
do {
c = fscript_get_char(fscript);
c = fscript_parser_get_char(parser);
} while (isspace(c) || c == ';');
fscript_unget_char(fscript, c);
fscript_parser_unget_char(parser, c);
return RET_OK;
}
@ -174,15 +320,15 @@ static ret_t fscript_skip_seperators(fscript_t* fscript) {
t->token = str->str; \
t->size = str->size;
static ret_t fscript_parse_str(fscript_t* fscript) {
static ret_t fscript_parser_parse_str(fscript_parser_t* parser) {
char c = '\0';
bool_t escape = FALSE;
str_t* str = &(fscript->temp);
token_t* t = &(fscript->token);
str_t* str = &(parser->temp);
token_t* t = &(parser->token);
str_set(str, "");
do {
c = fscript_get_char(fscript);
c = fscript_parser_get_char(parser);
if ((!escape && c == '\"') || c == '\0') {
break;
}
@ -198,20 +344,20 @@ static ret_t fscript_parse_str(fscript_t* fscript) {
TOKEN_INIT(t, TOKEN_STR, str);
if (c != '\0' && c != '\"') {
fscript_unget_char(fscript, c);
fscript_parser_unget_char(parser, c);
}
return RET_OK;
}
static ret_t fscript_parse_id_or_number(fscript_t* fscript, token_type_t def_type) {
static ret_t fscript_parser_parse_id_or_number(fscript_parser_t* parser, token_type_t def_type) {
char c = '\0';
str_t* str = &(fscript->temp);
token_t* t = &(fscript->token);
str_t* str = &(parser->temp);
token_t* t = &(parser->token);
str_set(str, "");
do {
c = fscript_get_char(fscript);
c = fscript_parser_get_char(parser);
if (c != '(' && c != ')' && c != ',' && c != '\"' && c != '\0') {
str_append_char(str, c);
} else {
@ -222,26 +368,26 @@ static ret_t fscript_parse_id_or_number(fscript_t* fscript, token_type_t def_typ
str_trim(str, " \t\r\n");
TOKEN_INIT(t, (c == '(' ? TOKEN_FUNC : def_type), str);
if (c != '\0') {
fscript_unget_char(fscript, c);
fscript_parser_unget_char(parser, c);
}
return RET_OK;
}
static token_t* fscript_get_token(fscript_t* fscript) {
static token_t* fscript_parser_get_token(fscript_parser_t* parser) {
char c = 0;
token_t* t = &(fscript->token);
token_t* t = &(parser->token);
if (t->valid) {
t->valid = FALSE;
return t;
}
fscript_skip_seperators(fscript);
c = fscript_get_char(fscript);
fscript_parser_skip_seperators(parser);
c = fscript_parser_get_char(parser);
t->size = 1;
t->token = fscript->cursor - 1;
t->token = parser->cursor - 1;
switch (c) {
case '\0': {
return NULL;
@ -269,103 +415,81 @@ static token_t* fscript_get_token(fscript_t* fscript) {
case '^':
case '~':
case '&': {
fscript_skip_seperators(fscript);
c = fscript_get_char(fscript);
fscript_parser_skip_seperators(parser);
c = fscript_parser_get_char(parser);
if (c == '(') {
fscript_unget_char(fscript, c);
t->size = fscript->cursor - t->token - 1;
fscript_parser_unget_char(parser, c);
t->size = parser->cursor - t->token - 1;
} else {
t->size = fscript->cursor - t->token;
t->size = parser->cursor - t->token;
}
t->type = TOKEN_FUNC;
return t;
}
case '\"': {
fscript_parse_str(fscript);
fscript_parser_parse_str(parser);
return t;
}
default: {
fscript_unget_char(fscript, c);
fscript_parser_unget_char(parser, c);
if (c == '+' || c == '-' || isdigit(c)) {
fscript_parse_id_or_number(fscript, TOKEN_NUMBER);
fscript_parser_parse_id_or_number(parser, TOKEN_NUMBER);
} else {
fscript_parse_id_or_number(fscript, TOKEN_ID);
fscript_parser_parse_id_or_number(parser, TOKEN_ID);
}
return t;
}
}
}
static ret_t fscript_expect_token(fscript_t* fscript, token_type_t type, const char* msg) {
token_t* t = fscript_get_token(fscript);
static ret_t fscript_parser_expect_token(fscript_parser_t* parser, token_type_t type,
const char* msg) {
token_t* t = fscript_parser_get_token(parser);
if (t == NULL || t->type != type) {
fscript_set_error(fscript, msg);
fscript_parser_set_error(parser, msg);
}
return RET_OK;
}
static ret_t fscript_unget_token(fscript_t* fscript) {
fscript->token.valid = TRUE;
static ret_t fscript_parser_unget_token(fscript_parser_t* parser) {
parser->token.valid = TRUE;
return RET_OK;
}
static ret_t fscript_exec_func(fscript_t* fscript, value_t* result) {
static ret_t fscript_parse_func(fscript_parser_t* parser, fscript_func_call_t* call) {
value_t v;
bool_t done = FALSE;
fscript_args_t args;
str_t* str = &(fscript->temp);
token_t* t = fscript_get_token(fscript);
fscript_func_t func = fscript_lookup(fscript, t->token, t->size);
bool_t is_if = t && t->size == 2 && strncmp(t->token, "if", 2) == 0;
return_value_if_fail(func != NULL, RET_NOT_FOUND);
fscript_args_t* args = &(call->args);
token_t* t = fscript_parser_get_token(parser);
value_set_int(result, 0);
func_args_init(&args, &(fscript->temp));
fscript_expect_token(fscript, TOKEN_LPAREN, "expect \"(\"");
while (fscript->error == NULL && t != NULL && t->type != TOKEN_RPAREN) {
t = fscript_get_token(fscript);
fscript_parser_expect_token(parser, TOKEN_LPAREN, "expect \"(\"");
while (parser->error == NULL && t != NULL && t->type != TOKEN_RPAREN) {
t = fscript_parser_get_token(parser);
if (t == NULL) {
fscript_expect_token(fscript, TOKEN_LPAREN, "expect \")\"");
fscript_parser_expect_token(parser, TOKEN_LPAREN, "expect \")\"");
return RET_FAIL;
}
switch (t->type) {
case TOKEN_ID: {
if (t->token[0] == 't' && strncmp(t->token, "true", 4) == 0) {
func_args_push(&args, value_set_bool(&v, TRUE));
func_args_push(args, value_set_bool(&v, TRUE));
} else if (t->token[0] == 'f' && strncmp(t->token, "false", 5) == 0) {
func_args_push(&args, value_set_bool(&v, FALSE));
func_args_push(args, value_set_bool(&v, FALSE));
} else {
if(args.size > 0 || (func != func_set && args.size == 0)) {
if(object_get_prop(fscript->obj, str->str, &v) == RET_OK) {
func_args_push(&args, &v);
} else {
func_args_push_str(&args, t->token, t->size);
}
} else {
func_args_push_str(&args, t->token, t->size);
}
func_args_push_str(args, t->token, t->size, TRUE);
}
break;
}
case TOKEN_FUNC: {
bool_t ignore = fscript->ignore;
fscript_unget_token(fscript);
if (is_if && args.size > 0) {
if (value_bool(args.args)) {
fscript->ignore = args.size != 1;
} else {
fscript->ignore = args.size == 1;
}
}
fscript_exec_func(fscript, &v);
fscript->ignore = ignore;
func_args_push(&args, &v);
value_reset(&v);
fscript_parser_unget_token(parser);
fscript_func_call_t* acall = fscript_func_call_create(parser, t->token, t->size);
return_value_if_fail(acall != NULL, RET_BAD_PARAMS);
fscript_parse_func(parser, acall);
value_set_func(&v, acall);
func_args_push(args, &v);
break;
}
case TOKEN_NUMBER: {
@ -376,11 +500,11 @@ static ret_t fscript_exec_func(fscript_t* fscript, value_t* result) {
} else {
value_set_int(&v, tk_atoi(number));
}
func_args_push(&args, &v);
func_args_push(args, &v);
break;
}
case TOKEN_STR: {
func_args_push_str(&args, t->token, t->size);
func_args_push_str(args, t->token, t->size, FALSE);
break;
}
case TOKEN_RPAREN: {
@ -388,7 +512,7 @@ static ret_t fscript_exec_func(fscript_t* fscript, value_t* result) {
break;
}
default: {
fscript_set_error(fscript, "unexpected token:");
fscript_parser_set_error(parser, "unexpected token:");
break;
}
}
@ -397,33 +521,41 @@ static ret_t fscript_exec_func(fscript_t* fscript, value_t* result) {
break;
}
t = fscript_get_token(fscript);
t = fscript_parser_get_token(parser);
if (t == NULL) {
fscript_expect_token(fscript, TOKEN_LPAREN, "expect \")\"");
fscript_parser_expect_token(parser, TOKEN_LPAREN, "expect \")\"");
break;
}
}
if (!fscript->ignore) {
func(fscript->obj, &args, result);
}
func_args_deinit(&args);
return RET_OK;
}
static ret_t fscript_exec(fscript_t* fscript, value_t* result) {
static ret_t fscript_parse(fscript_parser_t* parser) {
char c = '\0';
ret_t ret = RET_OK;
while (fscript->cursor[0]) {
ret = fscript_exec_func(fscript, result);
fscript_func_call_t* acall = NULL;
fscript_func_call_t* last = NULL;
fscript_skip_seperators(fscript);
c = fscript_get_char(fscript);
while (parser->cursor[0]) {
token_t* t = fscript_parser_get_token(parser);
if (t != NULL && t->type == TOKEN_FUNC) {
acall = fscript_func_call_create(parser, t->token, t->size);
return_value_if_fail(acall != NULL, RET_BAD_PARAMS);
fscript_parser_unget_token(parser);
fscript_parse_func(parser, acall);
if (last == NULL) {
parser->first = acall;
} else {
last->next = acall;
}
last = acall;
}
fscript_parser_skip_seperators(parser);
c = fscript_parser_get_char(parser);
if (c) {
value_reset(result);
fscript_unget_char(fscript, c);
fscript_parser_unget_char(parser, c);
} else {
break;
}
@ -432,22 +564,37 @@ static ret_t fscript_exec(fscript_t* fscript, value_t* result) {
return ret;
}
ret_t fscript_eval(object_t* obj, const char* script, value_t* result) {
value_t v;
fscript_t fscript;
fscript_t* fscript_create(object_t* obj, const char* script) {
ret_t ret = RET_OK;
return_value_if_fail(obj != NULL && script != NULL, RET_BAD_PARAMS);
fscript_t* fscript = NULL;
fscript_parser_t parser;
return_value_if_fail(obj != NULL && script != NULL, NULL);
value_set_int(&v, 0);
fscript_init(&fscript, obj, script);
ret = fscript_exec(&fscript, &v);
if (result != NULL) {
value_deep_copy(result, &v);
fscript_parser_init(&parser, obj, script);
ret = fscript_parse(&parser);
if (ret == RET_OK) {
fscript = fscript_create_impl(&parser);
fscript_parser_deinit(&parser);
} else {
log_warn("parser error:%s\n", script);
fscript_parser_deinit(&parser);
}
return fscript;
}
ret_t fscript_eval(object_t* obj, const char* script, value_t* result) {
value_t v;
ret_t ret = RET_OK;
fscript_t* fscript = fscript_create(obj, script);
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
value_set_int(&v, 0);
if (fscript_exec(fscript, &v) == RET_OK && result != NULL) {
value_deep_copy(result, &v);
}
value_reset(&v);
fscript_deinit(&fscript);
fscript_destroy(fscript);
return ret;
}
@ -958,85 +1105,110 @@ static ret_t func_unset(object_t* obj, fscript_args_t* args, value_t* result) {
return RET_OK;
}
typedef struct _func_entry_t {
const char* name;
fscript_func_t func;
uint32_t max_args_nr;
} func_entry_t;
static const func_entry_t s_builtin_funcs[] = {
{"set", func_set},
{"max", func_max},
{"min", func_min},
{"==", func_eq},
{">=", func_ge},
{">", func_great},
{"<=", func_le},
{"<", func_less},
{"!", func_not},
{"||", func_or},
{"exec", func_exec},
{"join", func_join},
{"if", func_if},
{"int", func_int},
{"float", func_float},
{"iformat", func_iformat},
{"print", func_print},
{"fformat", func_fformat},
{"time_now", func_time_now},
{"unset", func_unset},
{"str", func_str},
{"sub", func_sub},
{"substr", func_substr},
{"sum", func_sum},
{"tolower", func_tolower},
{"toupper", func_toupper},
{"trim", func_trim},
{"&&", func_and},
{"abs", func_abs},
{"acos", func_acos},
{"and", func_and},
{"asin", func_asin},
{"atan", func_atan},
{"clamp", func_clamp},
{"contains", func_contains},
{"cos", func_cos},
{"div", func_div},
{"eq", func_eq},
{"ge", func_ge},
{"great", func_great},
{"le", func_le},
{"len", func_len},
{"less", func_less},
{"mul", func_mul},
{"noop", func_noop},
{"not", func_not},
{"or", func_or},
{"pow", func_pow},
{"random", func_random},
{"replace", func_replace},
{"sin", func_sin},
{"sqrt", func_sqrt},
{"tan", func_tan},
{"time_now_ms", func_time_now_ms},
{"time_now_us", func_time_now_us},
{"&", func_bit_and},
{"^", func_bit_nor},
{"~", func_bit_not},
{"|", func_bit_or},
{"/", func_div},
{"%", func_mod},
{"*", func_mul},
{"-", func_sub},
{"+", func_sum},
{"print", func_print, 4},
{"set", func_set, 2},
{"max", func_max, 2},
{"min", func_min, 2},
{"==", func_eq, 2},
{">=", func_ge, 2},
{">", func_great, 2},
{"<=", func_le, 2},
{"<", func_less, 2},
{"!", func_not, 1},
{"||", func_or, 2},
{"exec", func_exec, 2},
{"join", func_join, 8},
{"if", func_if, 3},
{"int", func_int, 1},
{"float", func_float, 1},
{"iformat", func_iformat, 2},
{"fformat", func_fformat, 2},
{"time_now", func_time_now, 0},
{"unset", func_unset, 1},
{"str", func_str, 1},
{"sub", func_sub, 2},
{"substr", func_substr, 3},
{"sum", func_sum, 8},
{"tolower", func_tolower, 1},
{"toupper", func_toupper, 1},
{"trim", func_trim, 1},
{"&&", func_and, 2},
{"abs", func_abs, 1},
{"acos", func_acos, 1},
{"and", func_and, 2},
{"asin", func_asin, 1},
{"atan", func_atan, 1},
{"clamp", func_clamp, 3},
{"contains", func_contains, 2},
{"cos", func_cos, 1},
{"div", func_div, 2},
{"eq", func_eq, 2},
{"ge", func_ge, 2},
{"great", func_great, 2},
{"le", func_le, 2},
{"len", func_len, 1},
{"less", func_less, 2},
{"mul", func_mul, 2},
{"noop", func_noop, 0},
{"not", func_not, 1},
{"or", func_or, 2},
{"pow", func_pow, 2},
{"random", func_random, 2},
{"replace", func_replace, 3},
{"sin", func_sin, 1},
{"sqrt", func_sqrt, 1},
{"tan", func_tan, 1},
{"time_now_ms", func_time_now_ms, 0},
{"time_now_us", func_time_now_us, 0},
{"&", func_bit_and, 2},
{"^", func_bit_nor, 1},
{"~", func_bit_not, 1},
{"|", func_bit_or, 2},
{"/", func_div, 2},
{"%", func_mod, 2},
{"*", func_mul, 2},
{"-", func_sub, 2},
{"+", func_sum, 8},
};
static fscript_func_t fscript_lookup(fscript_t* fscript, const char* name, uint32_t size) {
static fscript_func_call_t* fscript_func_call_create(fscript_parser_t* parser, const char* name,
uint32_t size) {
uint32_t i = 0;
fscript_func_t func = NULL;
char func_name[TK_NAME_LEN + 1];
char full_func_name[2 * TK_NAME_LEN + 1];
fscript_func_call_t* call = TKMEM_ZALLOC(fscript_func_call_t);
return_value_if_fail(call != NULL, NULL);
tk_strncpy(func_name, name, tk_min(size, TK_NAME_LEN));
for (i = 0; i < ARRAY_SIZE(s_builtin_funcs); i++) {
if (tk_str_eq(s_builtin_funcs[i].name, func_name)) {
return s_builtin_funcs[i].func;
const func_entry_t* iter = s_builtin_funcs + i;
if (tk_str_eq(iter->name, func_name)) {
call->func = iter->func;
func_args_init(&(call->args), iter->max_args_nr);
return call;
}
}
tk_snprintf(full_func_name, sizeof(full_func_name)-1, "%s%s", STR_FSCRIPT_FUNCTION_PREFIX, func_name);
return (fscript_func_t)object_get_prop_pointer(fscript->obj, full_func_name);
tk_snprintf(full_func_name, sizeof(full_func_name) - 1, "%s%s", STR_FSCRIPT_FUNCTION_PREFIX,
func_name);
func = (fscript_func_t)object_get_prop_pointer(parser->obj, full_func_name);
if (func != NULL) {
call->func = func;
func_args_init(&(call->args), 2);
return call;
} else {
TKMEM_FREE(call);
log_warn("not found func:%s\n", func_name);
}
return NULL;
}

View File

@ -32,18 +32,24 @@ BEGIN_C_DECLS
*/
typedef struct _fscript_args_t {
/**
* @property {uint32_t} size
* @property {uint16_t} size
* @annotation ["readable"]
*
*/
uint32_t size;
uint16_t size;
/**
* @property {uint16_t} capacity
* @annotation ["readable"]
*
*/
uint16_t capacity;
/**
* @property {value_t*} args
* @annotation ["readable"]
*
*/
value_t args[FSCRIPT_MAX_ARGS];
value_t* args;
/**
* @property {str_t*} str
@ -59,10 +65,40 @@ typedef ret_t (*fscript_func_t)(object_t* obj, fscript_args_t* args, value_t* v)
* @class fscript_t
* @annotation ["fake"]
*
*
*
* https://github.com/zlgopen/awtk/blob/master/docs/fscript.md
*
*/
struct _fscript_t;
typedef struct _fscript_t fscript_t;
/**
* @method fscript_create
*
* @param {object_t*} obj
* @param {const char*} script
*
* @return {fscript_t*} fscript对象
*/
fscript_t* fscript_create(object_t* obj, const char* script);
/**
* @method fscript_exec
* @param {fscript_t*} fscript
* @param {value_t*} result (value_reset函数清除result)
*
* @return {ret_t} RET_OK表示成功
*/
ret_t fscript_exec(fscript_t* fscript, value_t* result);
/**
* @method fscript_destroy
*
* @param {fscript_t*} fscript
*
* @return {ret_t} RET_OK表示成功
*/
ret_t fscript_destroy(fscript_t* fscript);
/**
* @method fscript_eval

View File

@ -334,10 +334,13 @@ enum { TK_NAME_LEN = 31 };
#ifdef WITH_CPPCHECK
#define tk_str_eq strcmp
#define tk_str_eq strcasecmp
#define tk_str_ieq strcasecmp
#define tk_str_eq_with_len strncmp
#else
#define tk_str_eq(s1, s2) \
(((s1) != NULL) && ((s2) != NULL) && *(s1) == *(s2) && strcmp((s1), (s2)) == 0)
#define tk_str_eq_with_len(s1, s2, len) \
(((s1) != NULL) && ((s2) != NULL) && *(s1) == *(s2) && strncmp((s1), (s2), len) == 0)
#define tk_str_ieq(s1, s2) (((s1) != NULL) && ((s2) != NULL) && strcasecmp((s1), (s2)) == 0)
#define tk_wstr_eq(s1, s2) \

View File

@ -384,6 +384,16 @@ value_t* value_dup_str(value_t* v, const char* value) {
return v;
}
value_t* value_dup_str_with_len(value_t* v, const char* value, uint32_t len) {
return_value_if_fail(v != NULL, NULL);
value_init(v, VALUE_TYPE_STRING);
v->value.str = tk_strndup(value, len);
v->free_handle = TRUE;
return v;
}
value_t* value_set_wstr(value_t* v, const wchar_t* value) {
return_value_if_fail(v != NULL, NULL);
@ -747,7 +757,7 @@ const char* value_str_ex(const value_t* v, char* buff, uint32_t size) {
v->type == VALUE_TYPE_FLOAT) {
tk_snprintf(buff, size, "%lf", value_double(v));
} else if (v->type == VALUE_TYPE_WSTRING) {
tk_utf8_from_utf16(value_wstr, buff, size);
tk_utf8_from_utf16(value_wstr(v), buff, size);
} else if (v->type == VALUE_TYPE_BOOL) {
tk_snprintf(buff, size, "%s", value_bool(v) ? "true" : "false");
} else {

View File

@ -476,7 +476,6 @@ value_t* value_set_str(value_t* v, const char* value);
* @method value_dup_str
* ()
*
* > 使
* @alias value_set_str
* @annotation ["scriptable"]
* @param {value_t*} v value对象
@ -486,6 +485,18 @@ value_t* value_set_str(value_t* v, const char* value);
*/
value_t* value_dup_str(value_t* v, const char* value);
/**
* @method value_dup_str_with_len
* ()
*
* @param {value_t*} v value对象
* @param {const char*} value
* @param {uint32_t} len
*
* @return {value_t*} value对象本身
*/
value_t* value_dup_str_with_len(value_t* v, const char* value, uint32_t len);
/**
* @method value_set_wstr
*

View File

@ -1,3 +1,4 @@
#include "tkc/utils.h"
#include "tkc/platform.h"
#include "tkc/time_now.h"
#include "tkc/fscript.h"
@ -6,15 +7,28 @@
int main(int argc, char* argv[]) {
platform_prepare();
if (argc != 2) {
if (argc < 2) {
printf("Usage: %s script\n", argv[0]);
return 0;
} else {
value_t v;
uint64_t start = time_now_us();
const char* code = argv[1];
object_t* obj = object_default_create();
fscript_eval(obj, argv[1], &v);
value_reset(&v);
if(argc == 3) {
/*stress test*/
uint32_t i = 0;
uint32_t times = tk_atoi(argv[2]);
fscript_t* fscript = fscript_create(obj, code);
for(i = 0; i < times; i++) {
fscript_exec(fscript, &v);
value_reset(&v);
}
fscript_destroy(fscript);
} else {
fscript_eval(obj, code, &v);
value_reset(&v);
}
OBJECT_UNREF(obj);
log_debug("cost: %d us\n", (int)(time_now_us() - start));
}

View File

@ -2984,7 +2984,7 @@
"annotation": {
"scriptable": true
},
"desc": "设置类型为字符串的值(并拷贝字符串)。\n\n> 供脚本语言使用。",
"desc": "设置类型为字符串的值(并拷贝字符串)。",
"name": "value_dup_str",
"alias": "value_set_str",
"return": {
@ -2992,6 +2992,32 @@
"desc": "value对象本身。"
}
},
{
"params": [
{
"type": "value_t*",
"name": "v",
"desc": "value对象。"
},
{
"type": "const char*",
"name": "value",
"desc": "待设置的值。"
},
{
"type": "uint32_t",
"name": "len",
"desc": "长度。"
}
],
"annotation": {},
"desc": "设置类型为字符串的值(并拷贝字符串)。",
"name": "value_dup_str_with_len",
"return": {
"type": "value_t*",
"desc": "value对象本身。"
}
},
{
"params": [
{
@ -31374,7 +31400,15 @@
{
"name": "size",
"desc": "参数个数。",
"type": "uint32_t",
"type": "uint16_t",
"annotation": {
"readable": true
}
},
{
"name": "capacity",
"desc": "目前最大容量。",
"type": "uint16_t",
"annotation": {
"readable": true
}
@ -31415,11 +31449,69 @@
"type": "const char*",
"name": "script",
"desc": "脚本代码。"
}
],
"annotation": {},
"desc": "创建引擎对象。",
"name": "fscript_create",
"return": {
"type": "fscript_t*",
"desc": "返回fscript对象。"
}
},
{
"params": [
{
"type": "fscript_t*",
"name": "fscript",
"desc": "脚本引擎对象。"
},
{
"type": "value_t*",
"name": "result",
"desc": "执行结果。"
"desc": "执行结果(调用者需要用value_reset函数清除result)。"
}
],
"annotation": {},
"desc": "",
"name": "fscript_exec",
"return": {
"type": "ret_t",
"desc": "返回RET_OK表示成功否则表示失败。"
}
},
{
"params": [
{
"type": "fscript_t*",
"name": "fscript",
"desc": "脚本引擎对象。"
}
],
"annotation": {},
"desc": "销毁引擎对象。",
"name": "fscript_destroy",
"return": {
"type": "ret_t",
"desc": "返回RET_OK表示成功否则表示失败。"
}
},
{
"params": [
{
"type": "object_t*",
"name": "obj",
"desc": "脚本执行上下文。"
},
{
"type": "const char*",
"name": "script",
"desc": "脚本代码。"
},
{
"type": "value_t*",
"name": "result",
"desc": "执行结果(调用者需要用value_reset函数清除result)。"
}
],
"annotation": {},
@ -31434,7 +31526,7 @@
"events": [],
"properties": [],
"header": "tkc/fscript.h",
"desc": "一个简易的函数式脚本。",
"desc": "一个简易的函数式脚本引擎\n用法请参考https://github.com/zlgopen/awtk/blob/master/docs/fscript.md",
"name": "fscript_t",
"annotation": {
"fake": true

View File

@ -2558,7 +2558,7 @@
"annotation": {
"scriptable": true
},
"desc": "设置类型为字符串的值(并拷贝字符串)。\n\n> 供脚本语言使用。",
"desc": "设置类型为字符串的值(并拷贝字符串)。",
"name": "value_dup_str",
"alias": "value_set_str",
"return": {
@ -2566,6 +2566,32 @@
"desc": "value对象本身。"
}
},
{
"params": [
{
"type": "value_t*",
"name": "v",
"desc": "value对象。"
},
{
"type": "const char*",
"name": "value",
"desc": "待设置的值。"
},
{
"type": "uint32_t",
"name": "len",
"desc": "长度。"
}
],
"annotation": {},
"desc": "设置类型为字符串的值(并拷贝字符串)。",
"name": "value_dup_str_with_len",
"return": {
"type": "value_t*",
"desc": "value对象本身。"
}
},
{
"params": [
{
@ -7727,7 +7753,15 @@
{
"name": "size",
"desc": "参数个数。",
"type": "uint32_t",
"type": "uint16_t",
"annotation": {
"readable": true
}
},
{
"name": "capacity",
"desc": "目前最大容量。",
"type": "uint16_t",
"annotation": {
"readable": true
}
@ -7768,11 +7802,69 @@
"type": "const char*",
"name": "script",
"desc": "脚本代码。"
}
],
"annotation": {},
"desc": "创建引擎对象。",
"name": "fscript_create",
"return": {
"type": "fscript_t*",
"desc": "返回fscript对象。"
}
},
{
"params": [
{
"type": "fscript_t*",
"name": "fscript",
"desc": "脚本引擎对象。"
},
{
"type": "value_t*",
"name": "result",
"desc": "执行结果。"
"desc": "执行结果(调用者需要用value_reset函数清除result)。"
}
],
"annotation": {},
"desc": "",
"name": "fscript_exec",
"return": {
"type": "ret_t",
"desc": "返回RET_OK表示成功否则表示失败。"
}
},
{
"params": [
{
"type": "fscript_t*",
"name": "fscript",
"desc": "脚本引擎对象。"
}
],
"annotation": {},
"desc": "销毁引擎对象。",
"name": "fscript_destroy",
"return": {
"type": "ret_t",
"desc": "返回RET_OK表示成功否则表示失败。"
}
},
{
"params": [
{
"type": "object_t*",
"name": "obj",
"desc": "脚本执行上下文。"
},
{
"type": "const char*",
"name": "script",
"desc": "脚本代码。"
},
{
"type": "value_t*",
"name": "result",
"desc": "执行结果(调用者需要用value_reset函数清除result)。"
}
],
"annotation": {},
@ -7787,7 +7879,7 @@
"events": [],
"properties": [],
"header": "tkc/fscript.h",
"desc": "一个简易的函数式脚本。",
"desc": "一个简易的函数式脚本引擎\n用法请参考https://github.com/zlgopen/awtk/blob/master/docs/fscript.md",
"name": "fscript_t",
"annotation": {
"fake": true