fscript support define function

This commit is contained in:
lixianjing 2021-08-26 07:40:37 +08:00
parent 7991663340
commit b0384390f3
9 changed files with 399 additions and 35 deletions

View File

@ -1,5 +1,7 @@
# 最新动态
2021/08/26
* fscript 支持函数定义。
2021/08/25
* 修改OpenGL的lcd旋转被裁剪的问题感谢智明提供补丁
* 修复linux-fb的lcd旋转异常的问题感谢智明提供补丁

View File

@ -1279,7 +1279,55 @@ fscript_register_func("foo", func_foo);
```
#### 5.1 定义私有函数
#### 5.4 定义脚本函数
* 函数定义
```
function foo1(v1, v2) {
return v1 + v2;
}
assert(foo1(100, 200) == 300)
```
* 使用var定义局部变量。
```
function foo2(v1, v2) {
var v3 = v1 + v2;
return v3
}
assert(foo2(100, 200) == 300)
function foo3(v1, v2) {
var v3 = v1 + v2;
if(v3 < 100) {
return true
} else {
return false
}
}
assert(foo3(10, 20))
assert(!foo3(100, 200))
```
> 函数内的临时变量,无论在哪里定义,一旦定义,在该函数内都可以使用。
```
function foo4 (v1, v2) {
var v3 = v1 + v2;
if(v3 < 100) {
var name = "awtk";
} else {
var name = "react-awtk";
}
return name;
}
assert(foo4(10, 20) == 'awtk')
assert(foo4(100, 200) == 'react-awtk')
```
### 6. 性能测量与优化

View File

@ -1,7 +1,7 @@
/**
* File: fscript.c
* Author: AWTK Develop Team
* Brief: a simple functional script language
* Brief: a simple script language
*
* Copyright (c) 2020 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
@ -18,10 +18,12 @@
#ifndef AWTK_LITE
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "tkc/darray.h"
#include "tkc/fscript.h"
#include "tkc/object_default.h"
struct _fscript_func_call_t {
void* ctx;
fscript_func_t func;
fscript_args_t args;
fscript_func_call_t* next;
@ -32,11 +34,29 @@ struct _fscript_func_call_t {
#define VALUE_TYPE_JSCRIPT_ID 128
#define VALUE_TYPE_JSCRIPT_FUNC VALUE_TYPE_JSCRIPT_ID + 1
static ret_t func_if(fscript_t* fscript, fscript_args_t* args, value_t* result);
static ret_t func_while(fscript_t* fscript, fscript_args_t* args, value_t* result);
static ret_t func_function_def(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
static ret_t func_if(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
static ret_t func_while(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
static ret_t func_noop(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
static ret_t func_return(fscript_t* fscript, fscript_args_t* args, value_t* result) {
if (args->size > 0) {
value_deep_copy(result, args->args);
}
fscript->returned = TRUE;
return RET_OK;
}
static ret_t func_set(fscript_t* fscript, fscript_args_t* args, value_t* result);
static ret_t func_unset(fscript_t* fscript, fscript_args_t* args, value_t* result);
static ret_t fscript_exec_func(fscript_t* fscript, fscript_func_call_t* iter, value_t* result);
static ret_t func_set_local(fscript_t* fscript, fscript_args_t* args, value_t* result);
ret_t fscript_set_error(fscript_t* fscript, ret_t code, const char* func, const char* message) {
fscript->error_code = code;
@ -57,7 +77,6 @@ static value_t* value_set_func(value_t* v, fscript_func_call_t* func) {
static fscript_func_call_t* value_func(const value_t* v) {
return_value_if_fail(v->type == VALUE_TYPE_JSCRIPT_FUNC, NULL);
return (fscript_func_call_t*)(v->value.ptr);
}
@ -132,9 +151,38 @@ static ret_t fscript_func_call_destroy(fscript_func_call_t* call) {
return RET_OK;
}
typedef struct _fscript_function_def_t {
darray_t params;
fscript_func_call_t* body;
} fscript_function_def_t;
static fscript_function_def_t* fscript_function_def_create(fscript_func_call_t* body) {
fscript_function_def_t* func = TKMEM_ZALLOC(fscript_function_def_t);
return_value_if_fail(func != NULL, NULL);
func->body = body;
darray_init(&(func->params), 3, default_destroy, NULL);
return func;
}
static ret_t fscript_function_def_destroy(fscript_function_def_t* func) {
return_value_if_fail(func != NULL, RET_BAD_PARAMS);
darray_deinit(&(func->params));
fscript_func_call_destroy(func->body);
memset(func, 0x00, sizeof(fscript_function_def_t));
return RET_OK;
}
static ret_t fscript_function_def_add_param(fscript_function_def_t* func, const char* name) {
return_value_if_fail(func != NULL, RET_BAD_PARAMS);
return darray_push(&(func->params), tk_strdup(name));
}
typedef enum _token_type_t {
TOKEN_ID = 1,
TOKEN_VAR,
TOKEN_FUNC,
TOKEN_RETURN,
TOKEN_FUNC_DEF,
TOKEN_STR,
TOKEN_NUMBER,
TOKEN_LPAREN,
@ -166,6 +214,7 @@ typedef struct _fscript_parser_t {
str_t temp;
uint16_t row;
uint16_t col;
object_t* funcs_def;
fscript_func_call_t* first;
fscript_parser_error_t* error;
} fscript_parser_t;
@ -206,6 +255,12 @@ static ret_t fscript_get_var(fscript_t* fscript, const char* name, value_t* valu
name += 1;
}
if (fscript->locals != NULL) {
if (object_get_prop(fscript->locals, name, value) == RET_OK) {
return RET_OK;
}
}
if (is_fast_var(name)) {
value_t* var = fscript_get_fast_var(fscript, name);
if (name[1] == '.') {
@ -240,9 +295,14 @@ static ret_t fscript_set_var(fscript_t* fscript, const char* name, const value_t
value_reset(var);
ret = value_deep_copy(var, value);
}
} else {
value_t v;
if (fscript->locals != NULL && object_get_prop(fscript->locals, name, &v) == RET_OK) {
ret = object_set_prop(fscript->locals, name, value);
} else {
ret = object_set_prop(fscript->obj, name, value);
}
}
return ret;
}
@ -256,7 +316,8 @@ static ret_t fscript_eval_arg(fscript_t* fscript, fscript_func_call_t* iter, uin
value_set_str(d, NULL);
if (s->type == VALUE_TYPE_JSCRIPT_ID) {
s->type = VALUE_TYPE_STRING;
if ((iter->func == func_set || iter->func == func_unset) && i == 0) {
if ((iter->func == func_set_local || iter->func == func_set || iter->func == func_unset) &&
i == 0) {
value_copy(d, s); /*func_set accept id/str as first param*/
} else {
const char* name = value_str(s);
@ -345,6 +406,8 @@ static ret_t fscript_exec_core_func(fscript_t* fscript, fscript_func_call_t* ite
return fscript_exec_if(fscript, iter, result);
} else if (iter->func == func_while) {
return fscript_exec_while(fscript, iter, result);
} else if (iter->func == func_function_def) {
return RET_OK;
}
return RET_NOT_FOUND;
@ -363,13 +426,14 @@ static ret_t fscript_exec_ext_func(fscript_t* fscript, fscript_func_call_t* iter
for (i = 0; i < iter->args.size; i++) {
ret = fscript_eval_arg(fscript, iter, i, args.args + i);
if (fscript->breaked || fscript->continued || fscript->returned) {
value_set_int(result, 0);
value_deep_copy(result, args.args + i);
func_args_deinit(&args);
return RET_OK;
}
}
value_set_int(result, 0);
fscript->curr = iter;
ret = iter->func(fscript, &args, result);
func_args_deinit(&args);
@ -406,11 +470,23 @@ ret_t fscript_exec(fscript_t* fscript, value_t* result) {
return RET_OK;
}
static ret_t on_free_func_def(void* ctx, const void* data) {
named_value_t* iter = (named_value_t*)(data);
fscript_function_def_destroy(value_pointer(&(iter->value)));
return RET_OK;
}
ret_t fscript_destroy(fscript_t* fscript) {
uint32_t i = 0;
return_value_if_fail(fscript != NULL, RET_FAIL);
str_reset(&(fscript->str));
OBJECT_UNREF(fscript->locals);
if (fscript->funcs_def != NULL) {
object_foreach_prop(fscript->funcs_def, on_free_func_def, NULL);
}
OBJECT_UNREF(fscript->funcs_def);
TKMEM_FREE(fscript->error_message);
fscript_func_call_destroy(fscript->first);
for (i = 0; i < ARRAY_SIZE(fscript->fast_vars); i++) {
@ -486,7 +562,7 @@ static ret_t fscript_parser_set_error(fscript_parser_t* parser, const char* str)
log_warn("token: \"%s\"\n", parser->token.token);
log_warn("at line(%u) col (%u): %s\n", parser->row, parser->col, str);
return RET_OK;
return RET_FAIL;
}
static ret_t fscript_parser_skip_seperators(fscript_parser_t* parser) {
@ -609,8 +685,8 @@ static ret_t fscript_parser_parse_id_or_number(fscript_parser_t* parser, token_t
do {
c = fscript_parser_get_char(parser);
if (tk_isxdigit(c) || tk_isdigit(c) || tk_isalpha(c) || c == '.' || c == '_' || c == '[' || c == ']' ||
c == '#') {
if (tk_isxdigit(c) || tk_isdigit(c) || tk_isalpha(c) || c == '.' || c == '_' || c == '[' ||
c == ']' || c == '#') {
str_append_char(str, c);
} else {
break;
@ -628,6 +704,16 @@ static ret_t fscript_parser_parse_id_or_number(fscript_parser_t* parser, token_t
fscript_parser_unget_char(parser, c);
}
if (t->type == TOKEN_ID) {
if (tk_str_eq(t->token, "function")) {
t->type = TOKEN_FUNC_DEF;
} else if (tk_str_eq(t->token, "var")) {
t->type = TOKEN_VAR;
} else if (tk_str_eq(t->token, "return")) {
t->type = TOKEN_RETURN;
}
}
return RET_OK;
}
@ -780,8 +866,7 @@ static ret_t fscript_parser_expect_token(fscript_parser_t* parser, token_type_t
const char* msg) {
token_t* t = fscript_parser_get_token(parser);
if (t == NULL || t->type != type) {
fscript_parser_set_error(parser, msg);
return RET_FAIL;
return fscript_parser_set_error(parser, msg);
}
return RET_OK;
@ -930,8 +1015,7 @@ static ret_t fexpr_parse_function(fscript_parser_t* parser, value_t* result) {
do {
t = fscript_parser_get_token(parser);
if (t == NULL) {
fscript_parser_set_error(parser, "expect \")\"");
return RET_FAIL;
return fscript_parser_set_error(parser, "expect \")\"");
}
if (t->type == TOKEN_RPAREN) {
@ -945,8 +1029,7 @@ static ret_t fexpr_parse_function(fscript_parser_t* parser, value_t* result) {
t = fscript_parser_get_token(parser);
if (t == NULL) {
fscript_parser_set_error(parser, "expect \")\"");
return RET_FAIL;
return fscript_parser_set_error(parser, "expect \")\"");
} else if (t->type == TOKEN_RPAREN) {
break;
}
@ -978,6 +1061,21 @@ static ret_t fexpr_parse_term(fscript_parser_t* parser, value_t* result) {
} else if (t->type == TOKEN_FUNC) {
fscript_parser_unget_token(parser);
ret = fexpr_parse_function(parser, result);
} else if (t->type == TOKEN_RETURN) {
fscript_func_call_t* acall = fscript_func_call_create(parser, "return", 6);
return_value_if_fail(acall != NULL, RET_OOM);
value_set_func(result, acall);
t = fscript_parser_get_token(parser);
if (t != NULL && t->type != TOKEN_SEMICOLON && t->type != TOKEN_RBRACKET) {
value_t v;
value_set_int(&v, 0);
fscript_parser_unget_token(parser);
if (fexpr_parse(parser, &v) == RET_OK) {
func_args_push(&(acall->args), &v);
}
} else {
fscript_parser_unget_token(parser);
}
} else if (t->type == TOKEN_LPAREN) {
ret = fexpr_parse(parser, result);
return_value_if_fail(ret == RET_OK, ret);
@ -1005,8 +1103,7 @@ static ret_t fexpr_parse_unary(fscript_parser_t* parser, value_t* result) {
bool_t valid = FALSE;
for (i = 0; i < t->size; i++) {
if (t->token[i] != c) {
fscript_parser_set_error(parser, "unexpected token");
return RET_FAIL;
return fscript_parser_set_error(parser, "unexpected token");
} else {
valid = !valid;
}
@ -1195,15 +1292,23 @@ static ret_t fexpr_parse_question(fscript_parser_t* parser, value_t* result) {
static ret_t fexpr_parse(fscript_parser_t* parser, value_t* result) {
value_t v;
token_t* t = NULL;
bool_t is_local = FALSE;
fscript_args_t* args = NULL;
fscript_func_call_t* acall = NULL;
value_set_int(result, 0);
t = fscript_parser_get_token_ex(parser, TRUE);
if (t != NULL && t->type == TOKEN_VAR) {
is_local = TRUE;
} else {
fscript_parser_unget_token(parser);
}
return_value_if_fail(fexpr_parse_question(parser, result) == RET_OK, RET_FAIL);
t = fscript_parser_get_token_ex(parser, TRUE);
if (t != NULL && tk_str_eq(t->token, "=")) {
acall = fscript_func_call_create(parser, t->token, t->size);
const char* name = is_local ? "set_local" : "=";
acall = fscript_func_call_create(parser, name, strlen(name));
return_value_if_fail(acall != NULL, RET_OOM);
args = &(acall->args);
func_args_push(args, result);
@ -1223,14 +1328,90 @@ fscript_t* fscript_create_impl(fscript_parser_t* parser) {
fscript->str = parser->temp;
fscript->obj = parser->obj;
fscript->first = parser->first;
fscript->funcs_def = parser->funcs_def;
parser->obj = NULL;
parser->first = NULL;
parser->temp.str = NULL;
parser->funcs_def = NULL;
return fscript;
}
static ret_t func_function(fscript_t* fscript, fscript_args_t* args, value_t* result) {
ret_t ret = RET_OK;
object_t* saved_locals = fscript->locals;
fscript_function_def_t* func_def = (fscript_function_def_t*)(fscript->curr->ctx);
fscript_func_call_t* func = func_def->body;
func->func = func_noop;
fscript->locals = NULL;
if (func_def->params.size > 0 && args->size > 0) {
uint32_t i = 0;
uint32_t n = tk_min(func_def->params.size, args->size);
fscript->locals = object_default_create();
for (i = 0; i < n; i++) {
const value_t* value = args->args + i;
const char* name = (const char*)(func_def->params.elms[i]);
object_set_prop(fscript->locals, name, value);
}
}
ret = fscript_exec_func(fscript, func, result);
OBJECT_UNREF(fscript->locals);
fscript->locals = saved_locals;
fscript->returned = FALSE;
return ret;
}
static ret_t fscript_parse_function_def(fscript_parser_t* parser, fscript_func_call_t* acall) {
char func_name[TK_NAME_LEN + 1];
fscript_function_def_t* func_def = NULL;
fscript_func_call_t* statements = NULL;
token_t* t = fscript_parser_get_token(parser);
if (t->type != TOKEN_FUNC) {
return fscript_parser_set_error(parser, "expect function name\n");
}
tk_strncpy(func_name, t->token, TK_NAME_LEN);
fscript_parser_expect_token(parser, TOKEN_LPAREN, "expect \"(\"");
if (parser->funcs_def == NULL) {
parser->funcs_def = object_default_create();
}
if (object_get_prop_pointer(parser->funcs_def, func_name) != NULL) {
return fscript_parser_set_error(parser, "duplicate function\n");
}
statements = fscript_func_call_create(parser, "func", 4);
return_value_if_fail(statements != NULL, RET_OOM);
func_def = fscript_function_def_create(statements);
return_value_if_fail(func_def != NULL, RET_OOM);
object_set_prop_pointer(parser->funcs_def, func_name, func_def);
while (TRUE) {
t = fscript_parser_get_token(parser);
if (t == NULL || t->type == TOKEN_EOF || t->type == TOKEN_RPAREN) {
fscript_parser_unget_token(parser);
break;
}
if (t->type == TOKEN_ID) {
fscript_function_def_add_param(func_def, t->token);
} else if (t->type == TOKEN_COMMA) {
} else {
fscript_parser_unget_token(parser);
break;
}
}
fscript_parser_expect_token(parser, TOKEN_RPAREN, "expect \")\"");
fscript_parser_expect_token(parser, TOKEN_LBRACKET, "expect \"{\"");
fscript_parse_statements(parser, statements);
fscript_parser_expect_token(parser, TOKEN_RBRACKET, "expect \"}\"");
return RET_OK;
}
static ret_t fscript_parse_statements(fscript_parser_t* parser, fscript_func_call_t* acall) {
value_t v;
ret_t ret = RET_OK;
@ -1251,14 +1432,13 @@ static ret_t fscript_parse_statements(fscript_parser_t* parser, fscript_func_cal
}
if (t->type == TOKEN_FUNC || t->type == TOKEN_ID || t->type == TOKEN_NUMBER ||
t->type == TOKEN_STR) {
t->type == TOKEN_STR || t->type == TOKEN_VAR || t->type == TOKEN_RETURN) {
fscript_parser_unget_token(parser);
} else if (t->type == TOKEN_RBRACKET) {
} else if (t->type == TOKEN_RBRACKET || t->type == TOKEN_FUNC_DEF) {
fscript_parser_unget_token(parser);
break;
} else if (t->type != TOKEN_COMMA && t->type != TOKEN_SEMICOLON) {
fscript_parser_set_error(parser, "unexpected token\n");
break;
return fscript_parser_set_error(parser, "unexpected token\n");
}
} else {
if (v.type == VALUE_TYPE_JSCRIPT_FUNC) {
@ -1274,6 +1454,23 @@ static ret_t fscript_parse_statements(fscript_parser_t* parser, fscript_func_cal
return RET_OK;
}
static ret_t fscript_parse_all(fscript_parser_t* parser, fscript_func_call_t* acall) {
ret_t ret = RET_OK;
while (ret == RET_OK) {
token_t* t = fscript_parser_get_token(parser);
if (t && t->type == TOKEN_FUNC_DEF) {
ret = fscript_parse_function_def(parser, acall);
} else if (t == NULL || t->type == TOKEN_EOF) {
break;
} else {
fscript_parser_unget_token(parser);
ret = fscript_parse_statements(parser, acall);
}
}
return ret;
}
fscript_parser_error_t* fscript_parser_error_init(fscript_parser_error_t* error) {
return_value_if_fail(error != NULL, NULL);
memset(error, 0x00, sizeof(fscript_parser_error_t));
@ -1313,7 +1510,7 @@ fscript_t* fscript_create(object_t* obj, const char* expr) {
fscript_parser_error_init(&error);
fscript_parser_init(&parser, obj, expr, &error);
parser.first = fscript_func_call_create(&parser, "expr", 4);
ret = fscript_parse_statements(&parser, parser.first);
ret = fscript_parse_all(&parser, parser.first);
if (ret == RET_OK) {
fscript = fscript_create_impl(&parser);
fscript_parser_deinit(&parser);
@ -1532,11 +1729,16 @@ static ret_t func_join(fscript_t* fscript, fscript_args_t* args, value_t* result
return RET_OK;
}
static ret_t func_if(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
static ret_t func_set_local(fscript_t* fscript, fscript_args_t* args, value_t* result) {
const char* name = NULL;
FSCRIPT_FUNC_CHECK(args->size == 2, RET_BAD_PARAMS);
name = value_str(args->args);
FSCRIPT_FUNC_CHECK(name != NULL, RET_BAD_PARAMS);
if (fscript->locals == NULL) {
fscript->locals = object_default_create();
}
value_set_bool(result, object_set_prop(fscript->locals, name, args->args + 1) == RET_OK);
static ret_t func_while(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
@ -1950,10 +2152,6 @@ static ret_t func_exec(fscript_t* fscript, fscript_args_t* args, value_t* result
return RET_OK;
}
static ret_t func_noop(fscript_t* fscript, fscript_args_t* args, value_t* result) {
return RET_OK;
}
static ret_t func_unset(fscript_t* fscript, fscript_args_t* args, value_t* result) {
value_t* var = NULL;
const char* name = NULL;
@ -1964,9 +2162,14 @@ static ret_t func_unset(fscript_t* fscript, fscript_args_t* args, value_t* resul
var = fscript_get_fast_var(fscript, name);
if (var != NULL) {
value_reset(var);
} else {
value_t v;
if (fscript->locals != NULL && object_get_prop(fscript->locals, name, &v) == RET_OK) {
object_remove_prop(fscript->locals, name);
} else {
object_remove_prop(fscript->obj, name);
}
}
value_set_bool(result, TRUE);
return RET_OK;
@ -1979,10 +2182,13 @@ typedef struct _func_entry_t {
} func_entry_t;
static const func_entry_t s_builtin_funcs[] = {
{"func", func_function_def, 4},
{"return", func_return, 1},
{"print", func_print, 4},
{"expr", func_expr, 4},
{"=", func_set, 2},
{"set", func_set, 2},
{"set_local", func_set_local, 2},
{"max", func_max, 2},
{"min", func_min, 2},
{"==", func_eq, 2},
@ -2089,6 +2295,14 @@ static fscript_func_call_t* fscript_func_call_create(fscript_parser_t* parser, c
func = (fscript_func_t)object_get_prop_pointer(parser->obj, full_func_name);
}
if (func == NULL) {
value_t v;
if (object_get_prop(parser->funcs_def, func_name, &v) == RET_OK) {
func = func_function;
call->ctx = value_pointer(&v);
}
}
if (func == NULL) {
func = func_noop;
log_warn("not found %s\n", func_name);

View File

@ -146,6 +146,11 @@ typedef struct _fscript_t {
bool_t continued;
bool_t returned;
uint8_t while_count;
/*函数局部变量和参数*/
object_t* locals;
/*脚本定义的函数*/
object_t* funcs_def;
} fscript_t;
typedef ret_t (*fscript_func_t)(fscript_t* fscript, fscript_args_t* args, value_t* v);

View File

@ -21,12 +21,16 @@ static ret_t run_fscript(const char* code, uint32_t times) {
uint64_t start = time_now_us();
object_t* obj = object_default_create();
tk_mem_dump();
log_debug("======================\n");
if (times > 1) {
/*stress test*/
uint32_t i = 0;
fscript_t* fscript = fscript_create(obj, code);
for (i = 0; i < times; i++) {
log_debug("%u times....\n", i);
tk_mem_dump();
fscript_exec(fscript, &v);
tk_mem_dump();
value_reset(&v);
}
fscript_destroy(fscript);

View File

@ -0,0 +1,34 @@
function add(v1, v2, v3){
return (v1+v2+v3);
}
assert(add(100, 200, 300) == 600)
function f(n) {
if(n == 0) {
return (0);
} else if(n == 1) {
return (1);
} else {
return (f(n-1) + f(n-2));
}
}
assert(f(0) == 0);
assert(f(1) == 1);
assert(f(2) == 1);
assert(f(3) == 2);
assert(f(4) == 3);
assert(f(5) == 5);
assert(f(6) == 8);
assert(f(7) == 13);
assert(f(8) == 21);
var cc = 100;
function bar(aa, bb) {
var cc = aa + bb;
return (cc)
}
assert(bar(100, 200) == 300);
assert(cc == 100);

View File

@ -0,0 +1,30 @@
function f1() {
var a = 10;
var b = 20;
var c = a + b;
return c;
}
assert(f1() == 30);
function f2() {
var a = 10;
var b = 20;
return a + b;
}
assert(f2() == 30);
function f3() {
var a = 10;
var b = 20;
return (a + b);
}
assert(f3() == 30);
function f4() {
var a = 10;
var b = 20;
return 30
}
assert(f4() == 30);

View File

@ -0,0 +1,13 @@
function foo4 (v1, v2) {
var v3 = v1 + v2;
if(v3 < 100) {
var name = "awtk";
} else {
var name = "react-awtk";
}
return name;
}
assert(foo4(10, 20) == 'awtk')
assert(foo4(100, 200) == 'react-awtk')

View File

@ -0,0 +1,14 @@
name = 'tom'
assert(name == 'tom')
var name='jim'
assert(name == 'jim')
unset(name);
assert(name == 'tom')
if(name == 'tom') {
var age = 100;
}
assert(age == 100)