From e298f628726223cf9c7f59b49ad52a4c513b4429 Mon Sep 17 00:00:00 2001 From: lixianjing Date: Fri, 13 Nov 2020 08:57:05 +0800 Subject: [PATCH] rework fscripts --- dllexports/awtk.def | 4 + dllexports/tkc.def | 4 + docs/fscript.md | 5 +- src/tkc/fscript.c | 604 ++++++++++++++++++++++++++--------------- src/tkc/fscript.h | 44 ++- src/tkc/types_def.h | 5 +- src/tkc/value.c | 12 +- src/tkc/value.h | 13 +- tests/fscript_run.cpp | 20 +- tools/idl_gen/idl.json | 100 ++++++- tools/idl_gen/tkc.json | 100 ++++++- 11 files changed, 675 insertions(+), 236 deletions(-) diff --git a/dllexports/awtk.def b/dllexports/awtk.def index 07d975d8c..8ed5bd496 100644 --- a/dllexports/awtk.def +++ b/dllexports/awtk.def @@ -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 diff --git a/dllexports/tkc.def b/dllexports/tkc.def index 048f77144..452d0db6e 100644 --- a/dllexports/tkc.def +++ b/dllexports/tkc.def @@ -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 diff --git a/docs/fscript.md b/docs/fscript.md index c1fdabcbe..288b9a51b 100644 --- a/docs/fscript.md +++ b/docs/fscript.md @@ -5,13 +5,14 @@ fscript 是一个极简的脚本引擎,借鉴了函数语言中一些思路,主要用于低端嵌入式系统,让用户轻松扩展现有系统,而不需要重新编译和下载固件。 * 特色: - * 小巧。核心代码 400 行,扩展函数 600 行。 + * 小巧。核心代码 600 行,扩展函数 600 行。 * 灵活。支持多条语句、函数嵌套调用和变量定义。 * 强大。超过 50 个内置函数,支持用 C 语言扩展函数。 - * 小内存。边解析边执行,释放不用的内存,最低开销小于 500 字节。 + * 小内存。最低开销小于 500 字节。 * 限制: * 不支持循环。 + * 不支持函数定义。 ## 2. 示例 diff --git a/src/tkc/fscript.c b/src/tkc/fscript.c index b81f751e3..12a666f2d 100644 --- a/src/tkc/fscript.c +++ b/src/tkc/fscript.c @@ -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; } diff --git a/src/tkc/fscript.h b/src/tkc/fscript.h index e203ec547..9fa97c9c2 100644 --- a/src/tkc/fscript.h +++ b/src/tkc/fscript.h @@ -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 diff --git a/src/tkc/types_def.h b/src/tkc/types_def.h index de04c9080..6ffb80a7c 100644 --- a/src/tkc/types_def.h +++ b/src/tkc/types_def.h @@ -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) \ diff --git a/src/tkc/value.c b/src/tkc/value.c index b2a222318..83d350828 100644 --- a/src/tkc/value.c +++ b/src/tkc/value.c @@ -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 { diff --git a/src/tkc/value.h b/src/tkc/value.h index 52dff719a..023957e93 100644 --- a/src/tkc/value.h +++ b/src/tkc/value.h @@ -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 * 设置类型为宽字符串的值。 diff --git a/tests/fscript_run.cpp b/tests/fscript_run.cpp index 17cde7ceb..238817530 100644 --- a/tests/fscript_run.cpp +++ b/tests/fscript_run.cpp @@ -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)); } diff --git a/tools/idl_gen/idl.json b/tools/idl_gen/idl.json index cd516dbf7..dbb179382 100644 --- a/tools/idl_gen/idl.json +++ b/tools/idl_gen/idl.json @@ -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 diff --git a/tools/idl_gen/tkc.json b/tools/idl_gen/tkc.json index 377701025..6043c2f4b 100644 --- a/tools/idl_gen/tkc.json +++ b/tools/idl_gen/tkc.json @@ -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